Skip to content

Commit 5d5770a

Browse files
authored
Merge pull request microsoft#100 from mousetraps/api
microsoft#98 trait select clauses do not properly detect start of fully qualified name elements
2 parents 299f508 + 474a255 commit 5d5770a

File tree

10 files changed

+481
-26
lines changed

10 files changed

+481
-26
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ cache:
2020
- validation/frameworks
2121

2222
before_script:
23+
- set -e
2324
- phpenv config-rm xdebug.ini
2425
- if find . -name "*.php" -path "./src/*" -path "./experiments/*" -path "./tools/*" -path "./syntax-visualizer/server/src/*" -exec php -l {} 2>&1 \; | grep "syntax error, unexpected"; then exit 1; fi
2526
- if find . -name "*.php" -path "./tests/*" -path "./validation/*" -maxdepth 0 --exec php -l {} 2>&1 \; | grep "syntax error, unexpected"; then exit 1; fi

src/Node/TraitSelectOrAliasClause.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
use Microsoft\PhpParser\Token;
1111

1212
class TraitSelectOrAliasClause extends Node {
13-
14-
/** @var Token */
13+
/** @var QualifiedName | Node\Expression\ScopedPropertyAccessExpression */
1514
public $name;
1615

1716
/** @var Token */
@@ -20,6 +19,6 @@ class TraitSelectOrAliasClause extends Node {
2019
/** @var Token[] */
2120
public $modifiers;
2221

23-
/** @var Token */
22+
/** @var QualifiedName | Node\Expression\ScopedPropertyAccessExpression */
2423
public $targetName;
2524
}

src/Parser.php

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2682,35 +2682,41 @@ private function parseTraitUseClause($parentNode) {
26822682

26832683
$traitUseClause->semicolonOrOpenBrace = $this->eat(TokenKind::OpenBraceToken, TokenKind::SemicolonToken);
26842684
if ($traitUseClause->semicolonOrOpenBrace->kind === TokenKind::OpenBraceToken) {
2685-
$traitUseClause->traitSelectAndAliasClauses = $this->parseDelimitedList(
2686-
DelimitedList\TraitSelectOrAliasClauseList::class,
2687-
TokenKind::SemicolonToken,
2688-
function ($token) {
2689-
return $token->kind === TokenKind::Name;
2690-
},
2691-
function ($parentNode) {
2692-
$traitSelectAndAliasClause = new TraitSelectOrAliasClause();
2693-
$traitSelectAndAliasClause->parent = $parentNode;
2694-
$traitSelectAndAliasClause->name = // TODO update spec
2695-
$this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause);
2696-
2697-
$traitSelectAndAliasClause->asOrInsteadOfKeyword = $this->eat(TokenKind::AsKeyword, TokenKind::InsteadOfKeyword);
2698-
$traitSelectAndAliasClause->modifiers = $this->parseModifiers(); // TODO accept all modifiers, verify later
2699-
2700-
$traitSelectAndAliasClause->targetName =
2701-
$this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause);
2702-
2703-
// TODO errors for insteadof/as
2704-
return $traitSelectAndAliasClause;
2705-
},
2706-
$traitUseClause
2707-
);
2685+
$traitUseClause->traitSelectAndAliasClauses = $this->parseTraitSelectAndAliasClauseList($traitUseClause);
27082686
$traitUseClause->closeBrace = $this->eat(TokenKind::CloseBraceToken);
27092687
}
27102688

27112689
return $traitUseClause;
27122690
}
27132691

2692+
private function parseTraitSelectAndAliasClauseList($parentNode) {
2693+
return $this->parseDelimitedList(
2694+
DelimitedList\TraitSelectOrAliasClauseList::class,
2695+
TokenKind::SemicolonToken,
2696+
$this->isQualifiedNameStartFn(),
2697+
$this->parseTraitSelectOrAliasClauseFn(),
2698+
$parentNode
2699+
);
2700+
}
2701+
2702+
private function parseTraitSelectOrAliasClauseFn() {
2703+
return function ($parentNode) {
2704+
$traitSelectAndAliasClause = new TraitSelectOrAliasClause();
2705+
$traitSelectAndAliasClause->parent = $parentNode;
2706+
$traitSelectAndAliasClause->name = // TODO update spec
2707+
$this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause);
2708+
2709+
$traitSelectAndAliasClause->asOrInsteadOfKeyword = $this->eat(TokenKind::AsKeyword, TokenKind::InsteadOfKeyword);
2710+
$traitSelectAndAliasClause->modifiers = $this->parseModifiers(); // TODO accept all modifiers, verify later
2711+
2712+
$traitSelectAndAliasClause->targetName =
2713+
$this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause);
2714+
2715+
// TODO errors for insteadof/as
2716+
return $traitSelectAndAliasClause;
2717+
};
2718+
}
2719+
27142720
private function parseQualifiedNameOrScopedPropertyAccessExpression($parentNode) {
27152721
$qualifiedNameOrScopedProperty = $this->parseQualifiedName($parentNode);
27162722
if ($this->getCurrentToken()->kind === TokenKind::ColonColonToken) {

tests/cases/parser/traits21.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
// https://github.com/Microsoft/tolerant-php-parser/issues/98
3+
4+
class A {
5+
use \A {
6+
a as b;
7+
\c\d::a as d;
8+
}
9+
}

tests/cases/parser/traits21.php.tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
{
2+
"SourceFileNode": {
3+
"statementList": [
4+
{
5+
"InlineHtml": {
6+
"scriptSectionEndTag": null,
7+
"text": null,
8+
"scriptSectionStartTag": {
9+
"kind": "ScriptSectionStartTag",
10+
"textLength": 6
11+
}
12+
}
13+
},
14+
{
15+
"ClassDeclaration": {
16+
"abstractOrFinalModifier": null,
17+
"classKeyword": {
18+
"kind": "ClassKeyword",
19+
"textLength": 5
20+
},
21+
"name": {
22+
"kind": "Name",
23+
"textLength": 1
24+
},
25+
"classBaseClause": null,
26+
"classInterfaceClause": null,
27+
"classMembers": {
28+
"ClassMembersNode": {
29+
"openBrace": {
30+
"kind": "OpenBraceToken",
31+
"textLength": 1
32+
},
33+
"classMemberDeclarations": [
34+
{
35+
"TraitUseClause": {
36+
"useKeyword": {
37+
"kind": "UseKeyword",
38+
"textLength": 3
39+
},
40+
"traitNameList": {
41+
"QualifiedNameList": {
42+
"children": [
43+
{
44+
"QualifiedName": {
45+
"globalSpecifier": {
46+
"kind": "BackslashToken",
47+
"textLength": 1
48+
},
49+
"relativeSpecifier": null,
50+
"nameParts": {
51+
"QualifiedNameParts": {
52+
"children": [
53+
{
54+
"kind": "Name",
55+
"textLength": 1
56+
}
57+
]
58+
}
59+
}
60+
}
61+
}
62+
]
63+
}
64+
},
65+
"semicolonOrOpenBrace": {
66+
"kind": "OpenBraceToken",
67+
"textLength": 1
68+
},
69+
"traitSelectAndAliasClauses": {
70+
"TraitSelectOrAliasClauseList": {
71+
"children": [
72+
{
73+
"TraitSelectOrAliasClause": {
74+
"name": {
75+
"QualifiedName": {
76+
"globalSpecifier": null,
77+
"relativeSpecifier": null,
78+
"nameParts": {
79+
"QualifiedNameParts": {
80+
"children": [
81+
{
82+
"kind": "Name",
83+
"textLength": 1
84+
}
85+
]
86+
}
87+
}
88+
}
89+
},
90+
"asOrInsteadOfKeyword": {
91+
"kind": "AsKeyword",
92+
"textLength": 2
93+
},
94+
"modifiers": [],
95+
"targetName": {
96+
"QualifiedName": {
97+
"globalSpecifier": null,
98+
"relativeSpecifier": null,
99+
"nameParts": {
100+
"QualifiedNameParts": {
101+
"children": [
102+
{
103+
"kind": "Name",
104+
"textLength": 1
105+
}
106+
]
107+
}
108+
}
109+
}
110+
}
111+
}
112+
},
113+
{
114+
"kind": "SemicolonToken",
115+
"textLength": 1
116+
},
117+
{
118+
"TraitSelectOrAliasClause": {
119+
"name": {
120+
"ScopedPropertyAccessExpression": {
121+
"scopeResolutionQualifier": {
122+
"QualifiedName": {
123+
"globalSpecifier": {
124+
"kind": "BackslashToken",
125+
"textLength": 1
126+
},
127+
"relativeSpecifier": null,
128+
"nameParts": {
129+
"QualifiedNameParts": {
130+
"children": [
131+
{
132+
"kind": "Name",
133+
"textLength": 1
134+
},
135+
{
136+
"kind": "BackslashToken",
137+
"textLength": 1
138+
},
139+
{
140+
"kind": "Name",
141+
"textLength": 1
142+
}
143+
]
144+
}
145+
}
146+
}
147+
},
148+
"doubleColon": {
149+
"kind": "ColonColonToken",
150+
"textLength": 2
151+
},
152+
"memberName": {
153+
"kind": "Name",
154+
"textLength": 1
155+
}
156+
}
157+
},
158+
"asOrInsteadOfKeyword": {
159+
"kind": "AsKeyword",
160+
"textLength": 2
161+
},
162+
"modifiers": [],
163+
"targetName": {
164+
"QualifiedName": {
165+
"globalSpecifier": null,
166+
"relativeSpecifier": null,
167+
"nameParts": {
168+
"QualifiedNameParts": {
169+
"children": [
170+
{
171+
"kind": "Name",
172+
"textLength": 1
173+
}
174+
]
175+
}
176+
}
177+
}
178+
}
179+
}
180+
},
181+
{
182+
"kind": "SemicolonToken",
183+
"textLength": 1
184+
}
185+
]
186+
}
187+
},
188+
"closeBrace": {
189+
"kind": "CloseBraceToken",
190+
"textLength": 1
191+
}
192+
}
193+
}
194+
],
195+
"closeBrace": {
196+
"kind": "CloseBraceToken",
197+
"textLength": 1
198+
}
199+
}
200+
}
201+
}
202+
}
203+
],
204+
"endOfFileToken": {
205+
"kind": "EndOfFileToken",
206+
"textLength": 0
207+
}
208+
}
209+
}

tests/cases/parser/traits22.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
// https://github.com/Microsoft/tolerant-php-parser/issues/98
3+
4+
class A {
5+
use \A {
6+
\a::b insteadof b;
7+
a as d;
8+
}
9+
}

0 commit comments

Comments
 (0)