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

Commit

Permalink
Fix generic type inference for EditableList to handle Missing items
Browse files Browse the repository at this point in the history
  • Loading branch information
fredemmott committed Aug 29, 2018
1 parent d0a7d34 commit ef96527
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 49 deletions.
16 changes: 9 additions & 7 deletions codegen/syntax/AnonymousFunctionUseClause.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is generated. Do not modify it manually!
*
* @generated SignedSource<<b9233d4c77be7faf60229e0434172361>>
* @generated SignedSource<<fa070672841864ae51658fb9a864e620>>
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
Expand Down Expand Up @@ -187,18 +187,20 @@ public function hasVariables(): bool {
}

/**
* @returns EditableList<EditableNode> | EditableList<PrefixUnaryExpression>
* | EditableList<VariableToken>
* @returns EditableList<?VariableToken> |
* EditableList<PrefixUnaryExpression> | EditableList<EditableNode> |
* EditableList<VariableToken>
*/
public function getVariables(): EditableList<EditableNode> {
public function getVariables(): EditableList<?EditableNode> {
return TypeAssert\instance_of(EditableList::class, $this->_variables);
}

/**
* @returns EditableList<EditableNode> | EditableList<PrefixUnaryExpression>
* | EditableList<VariableToken>
* @returns EditableList<?VariableToken> |
* EditableList<PrefixUnaryExpression> | EditableList<EditableNode> |
* EditableList<VariableToken>
*/
public function getVariablesx(): EditableList<EditableNode> {
public function getVariablesx(): EditableList<?EditableNode> {
return $this->getVariables();
}

Expand Down
18 changes: 9 additions & 9 deletions codegen/syntax/ClassishDeclaration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is generated. Do not modify it manually!
*
* @generated SignedSource<<b56445b6d716354ff9df3e100b08a423>>
* @generated SignedSource<<1d0a68ac9b486e5ea4466b5d650a04e2>>
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
Expand Down Expand Up @@ -483,9 +483,9 @@ public function hasExtendsList(): bool {

/**
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
* EditableList<Missing> | EditableList<SimpleTypeSpecifier> | Missing
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier> | Missing
*/
public function getExtendsList(): ?EditableList<EditableNode> {
public function getExtendsList(): ?EditableList<?EditableNode> {
if ($this->_extends_list->isMissing()) {
return null;
}
Expand All @@ -494,9 +494,9 @@ public function getExtendsList(): ?EditableList<EditableNode> {

/**
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
* EditableList<Missing> | EditableList<SimpleTypeSpecifier>
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier>
*/
public function getExtendsListx(): EditableList<EditableNode> {
public function getExtendsListx(): EditableList<?EditableNode> {
return TypeAssert\instance_of(EditableList::class, $this->_extends_list);
}

Expand Down Expand Up @@ -577,9 +577,9 @@ public function hasImplementsList(): bool {

/**
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
* EditableList<Missing> | EditableList<SimpleTypeSpecifier> | Missing
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier> | Missing
*/
public function getImplementsList(): ?EditableList<EditableNode> {
public function getImplementsList(): ?EditableList<?EditableNode> {
if ($this->_implements_list->isMissing()) {
return null;
}
Expand All @@ -588,9 +588,9 @@ public function getImplementsList(): ?EditableList<EditableNode> {

/**
* @returns EditableList<GenericTypeSpecifier> | EditableList<EditableNode> |
* EditableList<Missing> | EditableList<SimpleTypeSpecifier>
* EditableList<?EditableNode> | EditableList<SimpleTypeSpecifier>
*/
public function getImplementsListx(): EditableList<EditableNode> {
public function getImplementsListx(): EditableList<?EditableNode> {
return TypeAssert\instance_of(EditableList::class, $this->_implements_list);
}

Expand Down
10 changes: 5 additions & 5 deletions codegen/syntax/EchoStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is generated. Do not modify it manually!
*
* @generated SignedSource<<f0c227b31ce4e716904836ffebba8a1f>>
* @generated SignedSource<<5db90c37c54b28f972cc134d02229d5f>>
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
Expand Down Expand Up @@ -134,7 +134,7 @@ public function hasExpressions(): bool {
* EditableList<CastExpression> | EditableList<ConditionalExpression> |
* EditableList<EmptyExpression> | EditableList<FunctionCallExpression> |
* EditableList<IssetExpression> | EditableList<LiteralExpression> |
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
* EditableList<MemberSelectionExpression> | EditableList<?EditableNode> |
* EditableList<ObjectCreationExpression> |
* EditableList<ParenthesizedExpression> |
* EditableList<PipeVariableExpression> |
Expand All @@ -143,7 +143,7 @@ public function hasExpressions(): bool {
* EditableList<SubscriptExpression> | EditableList<NameToken> |
* EditableList<VariableExpression> | EditableList<XHPExpression>
*/
public function getExpressions(): EditableList<EditableNode> {
public function getExpressions(): EditableList<?EditableNode> {
return TypeAssert\instance_of(EditableList::class, $this->_expressions);
}

Expand All @@ -152,7 +152,7 @@ public function getExpressions(): EditableList<EditableNode> {
* EditableList<CastExpression> | EditableList<ConditionalExpression> |
* EditableList<EmptyExpression> | EditableList<FunctionCallExpression> |
* EditableList<IssetExpression> | EditableList<LiteralExpression> |
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
* EditableList<MemberSelectionExpression> | EditableList<?EditableNode> |
* EditableList<ObjectCreationExpression> |
* EditableList<ParenthesizedExpression> |
* EditableList<PipeVariableExpression> |
Expand All @@ -161,7 +161,7 @@ public function getExpressions(): EditableList<EditableNode> {
* EditableList<SubscriptExpression> | EditableList<NameToken> |
* EditableList<VariableExpression> | EditableList<XHPExpression>
*/
public function getExpressionsx(): EditableList<EditableNode> {
public function getExpressionsx(): EditableList<?EditableNode> {
return $this->getExpressions();
}

Expand Down
14 changes: 7 additions & 7 deletions codegen/syntax/FunctionCallExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is generated. Do not modify it manually!
*
* @generated SignedSource<<fb1408ce2622bff1391e43c9af8f9f98>>
* @generated SignedSource<<8940685b43624ab69083ede9317e478f>>
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
Expand Down Expand Up @@ -209,8 +209,8 @@ public function hasArgumentList(): bool {
* EditableList<InstanceofExpression> | EditableList<IsExpression> |
* EditableList<IssetExpression> | EditableList<KeysetIntrinsicExpression> |
* EditableList<LambdaExpression> | EditableList<LiteralExpression> |
* EditableList<MemberSelectionExpression> |
* EditableList<ObjectCreationExpression> |
* EditableList<?LiteralExpression> | EditableList<MemberSelectionExpression>
* | EditableList<ObjectCreationExpression> |
* EditableList<ParenthesizedExpression> |
* EditableList<PipeVariableExpression> |
* EditableList<PostfixUnaryExpression> | EditableList<PrefixUnaryExpression>
Expand All @@ -223,7 +223,7 @@ public function hasArgumentList(): bool {
* EditableList<VectorIntrinsicExpression> | EditableList<XHPExpression> |
* Missing
*/
public function getArgumentList(): ?EditableList<EditableNode> {
public function getArgumentList(): ?EditableList<?EditableNode> {
if ($this->_argument_list->isMissing()) {
return null;
}
Expand All @@ -245,8 +245,8 @@ public function getArgumentList(): ?EditableList<EditableNode> {
* EditableList<InstanceofExpression> | EditableList<IsExpression> |
* EditableList<IssetExpression> | EditableList<KeysetIntrinsicExpression> |
* EditableList<LambdaExpression> | EditableList<LiteralExpression> |
* EditableList<MemberSelectionExpression> |
* EditableList<ObjectCreationExpression> |
* EditableList<?LiteralExpression> | EditableList<MemberSelectionExpression>
* | EditableList<ObjectCreationExpression> |
* EditableList<ParenthesizedExpression> |
* EditableList<PipeVariableExpression> |
* EditableList<PostfixUnaryExpression> | EditableList<PrefixUnaryExpression>
Expand All @@ -258,7 +258,7 @@ public function getArgumentList(): ?EditableList<EditableNode> {
* EditableList<VarrayIntrinsicExpression> |
* EditableList<VectorIntrinsicExpression> | EditableList<XHPExpression>
*/
public function getArgumentListx(): EditableList<EditableNode> {
public function getArgumentListx(): EditableList<?EditableNode> {
return TypeAssert\instance_of(EditableList::class, $this->_argument_list);
}

Expand Down
12 changes: 7 additions & 5 deletions codegen/syntax/FunctionDeclarationHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is generated. Do not modify it manually!
*
* @generated SignedSource<<442097dbd83f770e5926c0ba07b101a9>>
* @generated SignedSource<<2547bcd75a637fc92376ac2c86223997>>
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
Expand Down Expand Up @@ -509,21 +509,23 @@ public function hasParameterList(): bool {
}

/**
* @returns EditableList<EditableNode> | EditableList<ParameterDeclaration> |
* @returns EditableList<?ParameterDeclaration> |
* EditableList<ParameterDeclaration> | EditableList<EditableNode> |
* EditableList<VariadicParameter> | Missing
*/
public function getParameterList(): ?EditableList<EditableNode> {
public function getParameterList(): ?EditableList<?EditableNode> {
if ($this->_parameter_list->isMissing()) {
return null;
}
return TypeAssert\instance_of(EditableList::class, $this->_parameter_list);
}

/**
* @returns EditableList<EditableNode> | EditableList<ParameterDeclaration> |
* @returns EditableList<?ParameterDeclaration> |
* EditableList<ParameterDeclaration> | EditableList<EditableNode> |
* EditableList<VariadicParameter>
*/
public function getParameterListx(): EditableList<EditableNode> {
public function getParameterListx(): EditableList<?EditableNode> {
return TypeAssert\instance_of(EditableList::class, $this->_parameter_list);
}

Expand Down
17 changes: 9 additions & 8 deletions codegen/syntax/ListExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is generated. Do not modify it manually!
*
* @generated SignedSource<<68855fa3314c289fb7c3477915d061a6>>
* @generated SignedSource<<6041c108a5012b665480b290cd66d2eb>>
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
Expand Down Expand Up @@ -184,11 +184,11 @@ public function hasMembers(): bool {

/**
* @returns EditableList<ListExpression> | EditableList<EditableNode> |
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
* EditableList<SubscriptExpression> | EditableList<VariableExpression> |
* Missing
* EditableList<?EditableNode> | EditableList<MemberSelectionExpression> |
* EditableList<?VariableExpression> | EditableList<SubscriptExpression> |
* EditableList<VariableExpression> | Missing
*/
public function getMembers(): ?EditableList<EditableNode> {
public function getMembers(): ?EditableList<?EditableNode> {
if ($this->_members->isMissing()) {
return null;
}
Expand All @@ -197,10 +197,11 @@ public function getMembers(): ?EditableList<EditableNode> {

/**
* @returns EditableList<ListExpression> | EditableList<EditableNode> |
* EditableList<MemberSelectionExpression> | EditableList<Missing> |
* EditableList<SubscriptExpression> | EditableList<VariableExpression>
* EditableList<?EditableNode> | EditableList<MemberSelectionExpression> |
* EditableList<?VariableExpression> | EditableList<SubscriptExpression> |
* EditableList<VariableExpression>
*/
public function getMembersx(): EditableList<EditableNode> {
public function getMembersx(): EditableList<?EditableNode> {
return TypeAssert\instance_of(EditableList::class, $this->_members);
}

Expand Down
10 changes: 5 additions & 5 deletions codegen/syntax/QualifiedName.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is generated. Do not modify it manually!
*
* @generated SignedSource<<ce4efac348f23a1cbd79664111408e6d>>
* @generated SignedSource<<9bd5049fbd3cc5de04cf5151949bde9c>>
*/
namespace Facebook\HHAST;
use namespace Facebook\TypeAssert;
Expand Down Expand Up @@ -71,16 +71,16 @@ public function hasParts(): bool {
}

/**
* @returns EditableList<EditableNode> | EditableList<NameToken>
* @returns EditableList<?NameToken> | EditableList<NameToken>
*/
public function getParts(): EditableList<EditableNode> {
public function getParts(): EditableList<?NameToken> {
return TypeAssert\instance_of(EditableList::class, $this->_parts);
}

/**
* @returns EditableList<EditableNode> | EditableList<NameToken>
* @returns EditableList<?NameToken> | EditableList<NameToken>
*/
public function getPartsx(): EditableList<EditableNode> {
public function getPartsx(): EditableList<?NameToken> {
return $this->getParts();
}
}
6 changes: 6 additions & 0 deletions src/__Private/codegen/CodegenSyntax.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ private function getUnifiedSyntaxClass(keyset<string> $types): string {
if (C\is_empty($types)) {
return 'EditableNode';
}

if (C\contains_key($types, 'missing')) {
unset($types['missing']);
return '?'.$this->getUnifiedSyntaxClass($types);
}

if (C\count($types) === 1) {
$type = C\onlyx($types);
if ($type === 'list<>') {
Expand Down
9 changes: 7 additions & 2 deletions src/nodes/EditableList.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

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

final class EditableList<Titem as EditableNode> extends EditableNode {
final class EditableList<Titem as ?EditableNode> extends EditableNode {
private vec<EditableNode> $_children;
<<__Override>>
public function __construct(vec<EditableNode> $children) {
Expand Down Expand Up @@ -40,12 +40,17 @@ public function getChildren(): dict<string, EditableNode> {
}

final public function getItems(): vec<Titem> {
// The `filter_nulls()` is needed for for expressions like
// `list($a,,$c) = $foo` and types like `\Foo\Bar`, now that the first
// is parsed as name token items with backslash separators - i.e. the first
// item is empty.

/* HH_FIXME[4110] we have to trust the typechecker here; in future, use
* reified generics */
return Vec\map(
$this->_children,
$child ==> $child instanceof ListItem ? $child->getItem() : $child,
);
);// |> Vec\filter_nulls($$);
}

final public function getItemsOfType<T as EditableNode>(
Expand Down
12 changes: 11 additions & 1 deletion tests/PHPTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Facebook\HHAST;

use function Facebook\FBExpect\expect;
use namespace HH\Lib\C;
use namespace HH\Lib\{C, Vec};

final class PHPTest extends TestCase {
public function testPHPOnlyFeature(): void {
Expand Down Expand Up @@ -46,4 +46,14 @@ public function testHackOnlyFeature(): void {
);
expect($lambda)->toNotBeNull();
}

public function testEmptyListItems(): void {
$ast = from_code('<?php list($a,,$c) = [1,2,3]');
$assignment =
C\find($ast->traverse(), $x ==> $x is ListExpression) as ListExpression;
$members = $assignment->getMembersx();
expect($members->getCode())->toBeSame('$a,,$c');
$item_code = Vec\map($members->getItems(), $item ==> $item?->getCode());
expect($item_code)->toBeSame(vec['$a', null, '$c']);
}
}

0 comments on commit ef96527

Please sign in to comment.