From f29704386210f11bf5d1f2d16e6970536d5c24ef Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sat, 12 May 2018 11:15:04 -0700 Subject: [PATCH] Fixes #190: `insteadof` should be able to accept a name list Do this in a way that makes it less likely that applications already using targetName will throw an exception or error. This should be revisited in a future backwards incompatible release. Add remainingTargetNames as a new property. NOTE: traits16.php is invalid php 7 code, so traits16.php.diag should be non-empty. However, tolerant-php-parser emits a slightly different error message than `php -l` would. --- src/Node/TraitSelectOrAliasClause.php | 18 ++- src/Parser.php | 17 ++- tests/cases/parser/traits10.php.tree | 3 +- tests/cases/parser/traits12.php.tree | 6 +- tests/cases/parser/traits13.php.tree | 3 +- tests/cases/parser/traits14.php.tree | 3 +- tests/cases/parser/traits15.php.tree | 3 +- tests/cases/parser/traits16.php.diag | 33 ++++- tests/cases/parser/traits16.php.tree | 76 ++++++---- tests/cases/parser/traits17.php.tree | 3 +- tests/cases/parser/traits19.php.tree | 3 +- tests/cases/parser/traits20.php.tree | 3 +- tests/cases/parser/traits21.php.tree | 6 +- tests/cases/parser/traits22.php.tree | 6 +- tests/cases/parser/traits24.php | 8 ++ tests/cases/parser/traits24.php.diag | 1 + tests/cases/parser/traits24.php.tree | 191 ++++++++++++++++++++++++++ tests/cases/parser/traits9.php.tree | 3 +- 18 files changed, 341 insertions(+), 45 deletions(-) create mode 100644 tests/cases/parser/traits24.php create mode 100644 tests/cases/parser/traits24.php.diag create mode 100644 tests/cases/parser/traits24.php.tree diff --git a/src/Node/TraitSelectOrAliasClause.php b/src/Node/TraitSelectOrAliasClause.php index 711d52c..0144716 100644 --- a/src/Node/TraitSelectOrAliasClause.php +++ b/src/Node/TraitSelectOrAliasClause.php @@ -22,10 +22,26 @@ class TraitSelectOrAliasClause extends Node { /** @var QualifiedName|Node\Expression\ScopedPropertyAccessExpression */ public $targetName; + /** + * @var Token[]|QualifiedName[]|null + * + * This is set if $asOrInsteadOfKeyword is an insteadof keyword. + * (E.g. for parsing `use T1, T2, T3{T1::foo insteadof T2, T3}` + * + * NOTE: This was added as a separate property to minimize + * backwards compatibility breaks in applications using this file. + * + * TODO: Use a more consistent design such as either of the following: + * 1. Combine targetName and remainingTargetNames into a DelimitedList + * 2. Use two distinct properties for the targets of `as` and `insteadof` + */ + public $remainingTargetNames; + const CHILD_NAMES = [ 'name', 'asOrInsteadOfKeyword', 'modifiers', - 'targetName' + 'targetName', + 'remainingTargetNames', ]; } diff --git a/src/Parser.php b/src/Parser.php index 2bd41f7..f6112e5 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2665,6 +2665,10 @@ class Parser { return $propertyDeclaration; } + /** + * @param Node $parentNode + * @return DelimitedList\QualifiedNameList + */ private function parseQualifiedNameList($parentNode) { return $this->parseDelimitedList( DelimitedList\QualifiedNameList::class, @@ -2943,8 +2947,17 @@ class Parser { $traitSelectAndAliasClause->asOrInsteadOfKeyword = $this->eat(TokenKind::AsKeyword, TokenKind::InsteadOfKeyword); $traitSelectAndAliasClause->modifiers = $this->parseModifiers(); // TODO accept all modifiers, verify later - $traitSelectAndAliasClause->targetName = - $this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause); + if ($traitSelectAndAliasClause->asOrInsteadOfKeyword->kind === TokenKind::InsteadOfKeyword) { + // https://github.com/Microsoft/tolerant-php-parser/issues/190 + // TODO: In the next backwards incompatible release, convert targetName to a list? + $interfaceNameList = $this->parseQualifiedNameList($traitSelectAndAliasClause)->children; + $traitSelectAndAliasClause->targetName = $interfaceNameList[0] ?? null; + $traitSelectAndAliasClause->remainingTargetNames = array_slice($interfaceNameList, 1); + } else { + $traitSelectAndAliasClause->targetName = + $this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause); + } + // TODO errors for insteadof/as return $traitSelectAndAliasClause; diff --git a/tests/cases/parser/traits10.php.tree b/tests/cases/parser/traits10.php.tree index ce6c435..9259946 100644 --- a/tests/cases/parser/traits10.php.tree +++ b/tests/cases/parser/traits10.php.tree @@ -89,7 +89,8 @@ } ] } - } + }, + "remainingTargetNames": null } } ] diff --git a/tests/cases/parser/traits12.php.tree b/tests/cases/parser/traits12.php.tree index 8eaa224..aee008d 100644 --- a/tests/cases/parser/traits12.php.tree +++ b/tests/cases/parser/traits12.php.tree @@ -89,7 +89,8 @@ } ] } - } + }, + "remainingTargetNames": [] } }, { @@ -126,7 +127,8 @@ } ] } - } + }, + "remainingTargetNames": null } }, { diff --git a/tests/cases/parser/traits13.php.tree b/tests/cases/parser/traits13.php.tree index 16404d5..b0a450b 100644 --- a/tests/cases/parser/traits13.php.tree +++ b/tests/cases/parser/traits13.php.tree @@ -97,7 +97,8 @@ } ] } - } + }, + "remainingTargetNames": [] } }, { diff --git a/tests/cases/parser/traits14.php.tree b/tests/cases/parser/traits14.php.tree index e99519e..77204b8 100644 --- a/tests/cases/parser/traits14.php.tree +++ b/tests/cases/parser/traits14.php.tree @@ -97,7 +97,8 @@ } ] } - } + }, + "remainingTargetNames": [] } }, { diff --git a/tests/cases/parser/traits15.php.tree b/tests/cases/parser/traits15.php.tree index 7633e5e..64d1f22 100644 --- a/tests/cases/parser/traits15.php.tree +++ b/tests/cases/parser/traits15.php.tree @@ -101,7 +101,8 @@ } ] } - } + }, + "remainingTargetNames": [] } }, { diff --git a/tests/cases/parser/traits16.php.diag b/tests/cases/parser/traits16.php.diag index 0637a08..50ca458 100644 --- a/tests/cases/parser/traits16.php.diag +++ b/tests/cases/parser/traits16.php.diag @@ -1 +1,32 @@ -[] \ No newline at end of file +[ + { + "kind": 0, + "message": "'}' expected.", + "start": 41, + "length": 0 + }, + { + "kind": 0, + "message": "Unexpected '::'", + "start": 41, + "length": 2 + }, + { + "kind": 0, + "message": "'}' expected.", + "start": 43, + "length": 0 + }, + { + "kind": 0, + "message": "Unexpected '}'", + "start": 46, + "length": 1 + }, + { + "kind": 0, + "message": "Unexpected '}'", + "start": 48, + "length": 1 + } +] \ No newline at end of file diff --git a/tests/cases/parser/traits16.php.tree b/tests/cases/parser/traits16.php.tree index ecdd569..87b08fd 100644 --- a/tests/cases/parser/traits16.php.tree +++ b/tests/cases/parser/traits16.php.tree @@ -79,52 +79,74 @@ }, "modifiers": [], "targetName": { - "ScopedPropertyAccessExpression": { - "scopeResolutionQualifier": { - "QualifiedName": { - "globalSpecifier": null, - "relativeSpecifier": null, - "nameParts": [ - { - "kind": "Name", - "textLength": 1 - } - ] + "QualifiedName": { + "globalSpecifier": null, + "relativeSpecifier": null, + "nameParts": [ + { + "kind": "Name", + "textLength": 1 } - }, - "doubleColon": { - "kind": "ColonColonToken", - "textLength": 2 - }, - "memberName": { - "kind": "Name", - "textLength": 1 - } + ] } - } + }, + "remainingTargetNames": [] } - }, - { - "kind": "SemicolonToken", - "textLength": 1 } ] } }, "closeBrace": { + "error": "MissingToken", "kind": "CloseBraceToken", - "textLength": 1 + "textLength": 0 } } + }, + { + "error": "SkippedToken", + "kind": "ColonColonToken", + "textLength": 2 } ], "closeBrace": { + "error": "MissingToken", "kind": "CloseBraceToken", - "textLength": 1 + "textLength": 0 } } } } + }, + { + "ExpressionStatement": { + "expression": { + "QualifiedName": { + "globalSpecifier": null, + "relativeSpecifier": null, + "nameParts": [ + { + "kind": "Name", + "textLength": 1 + } + ] + } + }, + "semicolon": { + "kind": "SemicolonToken", + "textLength": 1 + } + } + }, + { + "error": "SkippedToken", + "kind": "CloseBraceToken", + "textLength": 1 + }, + { + "error": "SkippedToken", + "kind": "CloseBraceToken", + "textLength": 1 } ], "endOfFileToken": { diff --git a/tests/cases/parser/traits17.php.tree b/tests/cases/parser/traits17.php.tree index c5674cc..c8d9861 100644 --- a/tests/cases/parser/traits17.php.tree +++ b/tests/cases/parser/traits17.php.tree @@ -109,7 +109,8 @@ } ] } - } + }, + "remainingTargetNames": [] } }, { diff --git a/tests/cases/parser/traits19.php.tree b/tests/cases/parser/traits19.php.tree index 8a8f622..7723c43 100644 --- a/tests/cases/parser/traits19.php.tree +++ b/tests/cases/parser/traits19.php.tree @@ -94,7 +94,8 @@ } ] } - } + }, + "remainingTargetNames": null } }, { diff --git a/tests/cases/parser/traits20.php.tree b/tests/cases/parser/traits20.php.tree index 3588210..8c4a36a 100644 --- a/tests/cases/parser/traits20.php.tree +++ b/tests/cases/parser/traits20.php.tree @@ -98,7 +98,8 @@ } ] } - } + }, + "remainingTargetNames": null } }, { diff --git a/tests/cases/parser/traits21.php.tree b/tests/cases/parser/traits21.php.tree index d0ff0be..cb126b2 100644 --- a/tests/cases/parser/traits21.php.tree +++ b/tests/cases/parser/traits21.php.tree @@ -95,7 +95,8 @@ } ] } - } + }, + "remainingTargetNames": null } }, { @@ -155,7 +156,8 @@ } ] } - } + }, + "remainingTargetNames": null } }, { diff --git a/tests/cases/parser/traits22.php.tree b/tests/cases/parser/traits22.php.tree index 4c90785..d41cd6b 100644 --- a/tests/cases/parser/traits22.php.tree +++ b/tests/cases/parser/traits22.php.tree @@ -110,7 +110,8 @@ } ] } - } + }, + "remainingTargetNames": [] } }, { @@ -147,7 +148,8 @@ } ] } - } + }, + "remainingTargetNames": null } }, { diff --git a/tests/cases/parser/traits24.php b/tests/cases/parser/traits24.php new file mode 100644 index 0000000..fc69cf1 --- /dev/null +++ b/tests/cases/parser/traits24.php @@ -0,0 +1,8 @@ +