Skip to content
This repository was archived by the owner on Dec 1, 2024. It is now read-only.

Commit ef96527

Browse files
committed
Fix generic type inference for EditableList to handle Missing items
1 parent d0a7d34 commit ef96527

10 files changed

+75
-49
lines changed

codegen/syntax/AnonymousFunctionUseClause.php

+9-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is generated. Do not modify it manually!
44
*
5-
* @generated SignedSource<<b9233d4c77be7faf60229e0434172361>>
5+
* @generated SignedSource<<fa070672841864ae51658fb9a864e620>>
66
*/
77
namespace Facebook\HHAST;
88
use namespace Facebook\TypeAssert;
@@ -187,18 +187,20 @@ public function hasVariables(): bool {
187187
}
188188

189189
/**
190-
* @returns EditableList<EditableNode> | EditableList<PrefixUnaryExpression>
191-
* | EditableList<VariableToken>
190+
* @returns EditableList<?VariableToken> |
191+
* EditableList<PrefixUnaryExpression> | EditableList<EditableNode> |
192+
* EditableList<VariableToken>
192193
*/
193-
public function getVariables(): EditableList<EditableNode> {
194+
public function getVariables(): EditableList<?EditableNode> {
194195
return TypeAssert\instance_of(EditableList::class, $this->_variables);
195196
}
196197

197198
/**
198-
* @returns EditableList<EditableNode> | EditableList<PrefixUnaryExpression>
199-
* | EditableList<VariableToken>
199+
* @returns EditableList<?VariableToken> |
200+
* EditableList<PrefixUnaryExpression> | EditableList<EditableNode> |
201+
* EditableList<VariableToken>
200202
*/
201-
public function getVariablesx(): EditableList<EditableNode> {
203+
public function getVariablesx(): EditableList<?EditableNode> {
202204
return $this->getVariables();
203205
}
204206

codegen/syntax/ClassishDeclaration.php

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is generated. Do not modify it manually!
44
*
5-
* @generated SignedSource<<b56445b6d716354ff9df3e100b08a423>>
5+
* @generated SignedSource<<1d0a68ac9b486e5ea4466b5d650a04e2>>
66
*/
77
namespace Facebook\HHAST;
88
use namespace Facebook\TypeAssert;
@@ -483,9 +483,9 @@ public function hasExtendsList(): bool {
483483

484484
/**
485485
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
486-
* EditableList<Missing> | EditableList<SimpleTypeSpecifier> | Missing
486+
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier> | Missing
487487
*/
488-
public function getExtendsList(): ?EditableList<EditableNode> {
488+
public function getExtendsList(): ?EditableList<?EditableNode> {
489489
if ($this->_extends_list->isMissing()) {
490490
return null;
491491
}
@@ -494,9 +494,9 @@ public function getExtendsList(): ?EditableList<EditableNode> {
494494

495495
/**
496496
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
497-
* EditableList<Missing> | EditableList<SimpleTypeSpecifier>
497+
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier>
498498
*/
499-
public function getExtendsListx(): EditableList<EditableNode> {
499+
public function getExtendsListx(): EditableList<?EditableNode> {
500500
return TypeAssert\instance_of(EditableList::class, $this->_extends_list);
501501
}
502502

@@ -577,9 +577,9 @@ public function hasImplementsList(): bool {
577577

578578
/**
579579
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
580-
* EditableList<Missing> | EditableList<SimpleTypeSpecifier> | Missing
580+
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier> | Missing
581581
*/
582-
public function getImplementsList(): ?EditableList<EditableNode> {
582+
public function getImplementsList(): ?EditableList<?EditableNode> {
583583
if ($this->_implements_list->isMissing()) {
584584
return null;
585585
}
@@ -588,9 +588,9 @@ public function getImplementsList(): ?EditableList<EditableNode> {
588588

589589
/**
590590
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
591-
* EditableList<Missing> | EditableList<SimpleTypeSpecifier>
591+
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier>
592592
*/
593-
public function getImplementsListx(): EditableList<EditableNode> {
593+
public function getImplementsListx(): EditableList<?EditableNode> {
594594
return TypeAssert\instance_of(EditableList::class, $this->_implements_list);
595595
}
596596

codegen/syntax/EchoStatement.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is generated. Do not modify it manually!
44
*
5-
* @generated SignedSource<<f0c227b31ce4e716904836ffebba8a1f>>
5+
* @generated SignedSource<<5db90c37c54b28f972cc134d02229d5f>>
66
*/
77
namespace Facebook\HHAST;
88
use namespace Facebook\TypeAssert;
@@ -134,7 +134,7 @@ public function hasExpressions(): bool {
134134
* EditableList<CastExpression> | EditableList<ConditionalExpression> |
135135
* EditableList<EmptyExpression> | EditableList<FunctionCallExpression> |
136136
* EditableList<IssetExpression> | EditableList<LiteralExpression> |
137-
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
137+
* EditableList<MemberSelectionExpression> | EditableList<?EditableNode> |
138138
* EditableList<ObjectCreationExpression> |
139139
* EditableList<ParenthesizedExpression> |
140140
* EditableList<PipeVariableExpression> |
@@ -143,7 +143,7 @@ public function hasExpressions(): bool {
143143
* EditableList<SubscriptExpression> | EditableList<NameToken> |
144144
* EditableList<VariableExpression> | EditableList<XHPExpression>
145145
*/
146-
public function getExpressions(): EditableList<EditableNode> {
146+
public function getExpressions(): EditableList<?EditableNode> {
147147
return TypeAssert\instance_of(EditableList::class, $this->_expressions);
148148
}
149149

@@ -152,7 +152,7 @@ public function getExpressions(): EditableList<EditableNode> {
152152
* EditableList<CastExpression> | EditableList<ConditionalExpression> |
153153
* EditableList<EmptyExpression> | EditableList<FunctionCallExpression> |
154154
* EditableList<IssetExpression> | EditableList<LiteralExpression> |
155-
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
155+
* EditableList<MemberSelectionExpression> | EditableList<?EditableNode> |
156156
* EditableList<ObjectCreationExpression> |
157157
* EditableList<ParenthesizedExpression> |
158158
* EditableList<PipeVariableExpression> |
@@ -161,7 +161,7 @@ public function getExpressions(): EditableList<EditableNode> {
161161
* EditableList<SubscriptExpression> | EditableList<NameToken> |
162162
* EditableList<VariableExpression> | EditableList<XHPExpression>
163163
*/
164-
public function getExpressionsx(): EditableList<EditableNode> {
164+
public function getExpressionsx(): EditableList<?EditableNode> {
165165
return $this->getExpressions();
166166
}
167167

codegen/syntax/FunctionCallExpression.php

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is generated. Do not modify it manually!
44
*
5-
* @generated SignedSource<<fb1408ce2622bff1391e43c9af8f9f98>>
5+
* @generated SignedSource<<8940685b43624ab69083ede9317e478f>>
66
*/
77
namespace Facebook\HHAST;
88
use namespace Facebook\TypeAssert;
@@ -209,8 +209,8 @@ public function hasArgumentList(): bool {
209209
* EditableList<InstanceofExpression> | EditableList<IsExpression> |
210210
* EditableList<IssetExpression> | EditableList<KeysetIntrinsicExpression> |
211211
* EditableList<LambdaExpression> | EditableList<LiteralExpression> |
212-
* EditableList<MemberSelectionExpression> |
213-
* EditableList<ObjectCreationExpression> |
212+
* EditableList<?LiteralExpression> | EditableList<MemberSelectionExpression>
213+
* | EditableList<ObjectCreationExpression> |
214214
* EditableList<ParenthesizedExpression> |
215215
* EditableList<PipeVariableExpression> |
216216
* EditableList<PostfixUnaryExpression> | EditableList<PrefixUnaryExpression>
@@ -223,7 +223,7 @@ public function hasArgumentList(): bool {
223223
* EditableList<VectorIntrinsicExpression> | EditableList<XHPExpression> |
224224
* Missing
225225
*/
226-
public function getArgumentList(): ?EditableList<EditableNode> {
226+
public function getArgumentList(): ?EditableList<?EditableNode> {
227227
if ($this->_argument_list->isMissing()) {
228228
return null;
229229
}
@@ -245,8 +245,8 @@ public function getArgumentList(): ?EditableList<EditableNode> {
245245
* EditableList<InstanceofExpression> | EditableList<IsExpression> |
246246
* EditableList<IssetExpression> | EditableList<KeysetIntrinsicExpression> |
247247
* EditableList<LambdaExpression> | EditableList<LiteralExpression> |
248-
* EditableList<MemberSelectionExpression> |
249-
* EditableList<ObjectCreationExpression> |
248+
* EditableList<?LiteralExpression> | EditableList<MemberSelectionExpression>
249+
* | EditableList<ObjectCreationExpression> |
250250
* EditableList<ParenthesizedExpression> |
251251
* EditableList<PipeVariableExpression> |
252252
* EditableList<PostfixUnaryExpression> | EditableList<PrefixUnaryExpression>
@@ -258,7 +258,7 @@ public function getArgumentList(): ?EditableList<EditableNode> {
258258
* EditableList<VarrayIntrinsicExpression> |
259259
* EditableList<VectorIntrinsicExpression> | EditableList<XHPExpression>
260260
*/
261-
public function getArgumentListx(): EditableList<EditableNode> {
261+
public function getArgumentListx(): EditableList<?EditableNode> {
262262
return TypeAssert\instance_of(EditableList::class, $this->_argument_list);
263263
}
264264

codegen/syntax/FunctionDeclarationHeader.php

+7-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is generated. Do not modify it manually!
44
*
5-
* @generated SignedSource<<442097dbd83f770e5926c0ba07b101a9>>
5+
* @generated SignedSource<<2547bcd75a637fc92376ac2c86223997>>
66
*/
77
namespace Facebook\HHAST;
88
use namespace Facebook\TypeAssert;
@@ -509,21 +509,23 @@ public function hasParameterList(): bool {
509509
}
510510

511511
/**
512-
* @returns EditableList<EditableNode> | EditableList<ParameterDeclaration> |
512+
* @returns EditableList<?ParameterDeclaration> |
513+
* EditableList<ParameterDeclaration> | EditableList<EditableNode> |
513514
* EditableList<VariadicParameter> | Missing
514515
*/
515-
public function getParameterList(): ?EditableList<EditableNode> {
516+
public function getParameterList(): ?EditableList<?EditableNode> {
516517
if ($this->_parameter_list->isMissing()) {
517518
return null;
518519
}
519520
return TypeAssert\instance_of(EditableList::class, $this->_parameter_list);
520521
}
521522

522523
/**
523-
* @returns EditableList<EditableNode> | EditableList<ParameterDeclaration> |
524+
* @returns EditableList<?ParameterDeclaration> |
525+
* EditableList<ParameterDeclaration> | EditableList<EditableNode> |
524526
* EditableList<VariadicParameter>
525527
*/
526-
public function getParameterListx(): EditableList<EditableNode> {
528+
public function getParameterListx(): EditableList<?EditableNode> {
527529
return TypeAssert\instance_of(EditableList::class, $this->_parameter_list);
528530
}
529531

codegen/syntax/ListExpression.php

+9-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is generated. Do not modify it manually!
44
*
5-
* @generated SignedSource<<68855fa3314c289fb7c3477915d061a6>>
5+
* @generated SignedSource<<6041c108a5012b665480b290cd66d2eb>>
66
*/
77
namespace Facebook\HHAST;
88
use namespace Facebook\TypeAssert;
@@ -184,11 +184,11 @@ public function hasMembers(): bool {
184184

185185
/**
186186
* @returns EditableList<ListExpression> | EditableList<EditableNode> |
187-
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
188-
* EditableList<SubscriptExpression> | EditableList<VariableExpression> |
189-
* Missing
187+
* EditableList<?EditableNode> | EditableList<MemberSelectionExpression> |
188+
* EditableList<?VariableExpression> | EditableList<SubscriptExpression> |
189+
* EditableList<VariableExpression> | Missing
190190
*/
191-
public function getMembers(): ?EditableList<EditableNode> {
191+
public function getMembers(): ?EditableList<?EditableNode> {
192192
if ($this->_members->isMissing()) {
193193
return null;
194194
}
@@ -197,10 +197,11 @@ public function getMembers(): ?EditableList<EditableNode> {
197197

198198
/**
199199
* @returns EditableList<ListExpression> | EditableList<EditableNode> |
200-
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
201-
* EditableList<SubscriptExpression> | EditableList<VariableExpression>
200+
* EditableList<?EditableNode> | EditableList<MemberSelectionExpression> |
201+
* EditableList<?VariableExpression> | EditableList<SubscriptExpression> |
202+
* EditableList<VariableExpression>
202203
*/
203-
public function getMembersx(): EditableList<EditableNode> {
204+
public function getMembersx(): EditableList<?EditableNode> {
204205
return TypeAssert\instance_of(EditableList::class, $this->_members);
205206
}
206207

codegen/syntax/QualifiedName.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is generated. Do not modify it manually!
44
*
5-
* @generated SignedSource<<ce4efac348f23a1cbd79664111408e6d>>
5+
* @generated SignedSource<<9bd5049fbd3cc5de04cf5151949bde9c>>
66
*/
77
namespace Facebook\HHAST;
88
use namespace Facebook\TypeAssert;
@@ -71,16 +71,16 @@ public function hasParts(): bool {
7171
}
7272

7373
/**
74-
* @returns EditableList<EditableNode> | EditableList<NameToken>
74+
* @returns EditableList<?NameToken> | EditableList<NameToken>
7575
*/
76-
public function getParts(): EditableList<EditableNode> {
76+
public function getParts(): EditableList<?NameToken> {
7777
return TypeAssert\instance_of(EditableList::class, $this->_parts);
7878
}
7979

8080
/**
81-
* @returns EditableList<EditableNode> | EditableList<NameToken>
81+
* @returns EditableList<?NameToken> | EditableList<NameToken>
8282
*/
83-
public function getPartsx(): EditableList<EditableNode> {
83+
public function getPartsx(): EditableList<?NameToken> {
8484
return $this->getParts();
8585
}
8686
}

src/__Private/codegen/CodegenSyntax.php

+6
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ private function getUnifiedSyntaxClass(keyset<string> $types): string {
9393
if (C\is_empty($types)) {
9494
return 'EditableNode';
9595
}
96+
97+
if (C\contains_key($types, 'missing')) {
98+
unset($types['missing']);
99+
return '?'.$this->getUnifiedSyntaxClass($types);
100+
}
101+
96102
if (C\count($types) === 1) {
97103
$type = C\onlyx($types);
98104
if ($type === 'list<>') {

src/nodes/EditableList.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
use namespace HH\Lib\{C, Dict, Vec};
1414

15-
final class EditableList<Titem as EditableNode> extends EditableNode {
15+
final class EditableList<Titem as ?EditableNode> extends EditableNode {
1616
private vec<EditableNode> $_children;
1717
<<__Override>>
1818
public function __construct(vec<EditableNode> $children) {
@@ -40,12 +40,17 @@ public function getChildren(): dict<string, EditableNode> {
4040
}
4141

4242
final public function getItems(): vec<Titem> {
43+
// The `filter_nulls()` is needed for for expressions like
44+
// `list($a,,$c) = $foo` and types like `\Foo\Bar`, now that the first
45+
// is parsed as name token items with backslash separators - i.e. the first
46+
// item is empty.
47+
4348
/* HH_FIXME[4110] we have to trust the typechecker here; in future, use
4449
* reified generics */
4550
return Vec\map(
4651
$this->_children,
4752
$child ==> $child instanceof ListItem ? $child->getItem() : $child,
48-
);
53+
);// |> Vec\filter_nulls($$);
4954
}
5055

5156
final public function getItemsOfType<T as EditableNode>(

tests/PHPTest.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace Facebook\HHAST;
1313

1414
use function Facebook\FBExpect\expect;
15-
use namespace HH\Lib\C;
15+
use namespace HH\Lib\{C, Vec};
1616

1717
final class PHPTest extends TestCase {
1818
public function testPHPOnlyFeature(): void {
@@ -46,4 +46,14 @@ public function testHackOnlyFeature(): void {
4646
);
4747
expect($lambda)->toNotBeNull();
4848
}
49+
50+
public function testEmptyListItems(): void {
51+
$ast = from_code('<?php list($a,,$c) = [1,2,3]');
52+
$assignment =
53+
C\find($ast->traverse(), $x ==> $x is ListExpression) as ListExpression;
54+
$members = $assignment->getMembersx();
55+
expect($members->getCode())->toBeSame('$a,,$c');
56+
$item_code = Vec\map($members->getItems(), $item ==> $item?->getCode());
57+
expect($item_code)->toBeSame(vec['$a', null, '$c']);
58+
}
4959
}

0 commit comments

Comments
 (0)