1010
1111class DiagnosticsProvider {
1212
13- private static $ tokenKindToText ;
13+ /**
14+ * @param int $kind (must be a valid token kind)
15+ * @return string
16+ */
17+ public static function getTextForTokenKind ($ kind ) {
18+ static $ tokenKindToText ;
19+ if (!isset ($ tokenKindToText )) {
20+ $ tokenKindToText = \array_flip (\array_merge (
21+ TokenStringMaps::OPERATORS_AND_PUNCTUATORS ,
22+ TokenStringMaps::KEYWORDS ,
23+ TokenStringMaps::RESERVED_WORDS
24+ ));
25+ }
26+ return $ tokenKindToText [$ kind ];
27+ }
1428
1529 /**
1630 * Returns the diagnostic for $node, or null.
17- * @param \Microsoft\PhpParser\Node $node
31+ * @param \Microsoft\PhpParser\Node|\Microsoft\PhpParser\Token $node
1832 * @return Diagnostic|null
1933 */
2034 public static function checkDiagnostics ($ node ) {
21- if (!isset (self ::$ tokenKindToText )) {
22- self ::$ tokenKindToText = \array_flip (\array_merge (
35+ if ($ node instanceof Token) {
36+ if (\get_class ($ node ) === Token::class) {
37+ return null ;
38+ }
39+ return self ::checkDiagnosticForUnexpectedToken ($ node );
40+ }
41+
42+ if ($ node instanceof Node) {
43+ return $ node ->getDiagnosticForNode ();
44+ }
45+ return null ;
46+ }
47+
48+ /**
49+ * @param Token $token
50+ * @return Diagnostic|null
51+ */
52+ private static function checkDiagnosticForUnexpectedToken ($ token ) {
53+ static $ tokenKindToText ;
54+ if (!isset ($ tokenKindToText )) {
55+ $ tokenKindToText = \array_flip (\array_merge (
2356 TokenStringMaps::OPERATORS_AND_PUNCTUATORS ,
2457 TokenStringMaps::KEYWORDS ,
2558 TokenStringMaps::RESERVED_WORDS
2659 ));
2760 }
28-
2961 if ($ node instanceof SkippedToken) {
3062 // TODO - consider also attaching parse context information to skipped tokens
3163 // this would allow us to provide more helpful error messages that inform users what to do
3264 // about the problem rather than simply pointing out the mistake.
3365 return new Diagnostic (
3466 DiagnosticKind::Error,
3567 "Unexpected ' " .
36- (isset (self :: $ tokenKindToText [$ node ->kind ])
37- ? self :: $ tokenKindToText [$ node ->kind ]
68+ (isset ($ tokenKindToText [$ node ->kind ])
69+ ? $ tokenKindToText [$ node ->kind ]
3870 : Token::getTokenKindNameFromValue ($ node ->kind )) .
3971 "' " ,
4072 $ node ->start ,
@@ -44,93 +76,14 @@ public static function checkDiagnostics($node) {
4476 return new Diagnostic (
4577 DiagnosticKind::Error,
4678 "' " .
47- (isset (self :: $ tokenKindToText [$ node ->kind ])
48- ? self :: $ tokenKindToText [$ node ->kind ]
79+ (isset ($ tokenKindToText [$ node ->kind ])
80+ ? $ tokenKindToText [$ node ->kind ]
4981 : Token::getTokenKindNameFromValue ($ node ->kind )) .
5082 "' expected. " ,
5183 $ node ->start ,
5284 $ node ->getEndPosition () - $ node ->start
5385 );
5486 }
55-
56- if ($ node === null || $ node instanceof Token) {
57- return null ;
58- }
59-
60- if ($ node instanceof Node) {
61- if ($ node instanceof Node \MethodDeclaration) {
62- foreach ($ node ->modifiers as $ modifier ) {
63- if ($ modifier ->kind === TokenKind::VarKeyword) {
64- return new Diagnostic (
65- DiagnosticKind::Error,
66- "Unexpected modifier ' " . self ::$ tokenKindToText [$ modifier ->kind ] . "' " ,
67- $ modifier ->start ,
68- $ modifier ->length
69- );
70- }
71- }
72- }
73- elseif ($ node instanceof Node \Statement \NamespaceUseDeclaration) {
74- if (
75- $ node ->useClauses != null
76- && \count ($ node ->useClauses ->children ) > 1
77- ) {
78- foreach ($ node ->useClauses ->children as $ useClause ) {
79- if ($ useClause instanceof Node \NamespaceUseClause && !is_null ($ useClause ->openBrace )) {
80- return new Diagnostic (
81- DiagnosticKind::Error,
82- "; expected. " ,
83- $ useClause ->getEndPosition (),
84- 1
85- );
86- }
87- }
88- }
89- }
90- else if ($ node instanceof Node \Statement \BreakOrContinueStatement) {
91- if ($ node ->breakoutLevel === null ) {
92- return null ;
93- }
94-
95- $ breakoutLevel = $ node ->breakoutLevel ;
96- while ($ breakoutLevel instanceof Node \Expression \ParenthesizedExpression) {
97- $ breakoutLevel = $ breakoutLevel ->expression ;
98- }
99-
100- if (
101- $ breakoutLevel instanceof Node \NumericLiteral
102- && $ breakoutLevel ->children ->kind === TokenKind::IntegerLiteralToken
103- ) {
104- $ literalString = $ breakoutLevel ->getText ();
105- $ firstTwoChars = \substr ($ literalString , 0 , 2 );
106-
107- if ($ firstTwoChars === '0b ' || $ firstTwoChars === '0B ' ) {
108- if (\bindec (\substr ($ literalString , 2 )) > 0 ) {
109- return null ;
110- }
111- }
112- else if (\intval ($ literalString , 0 ) > 0 ) {
113- return null ;
114- }
115- }
116-
117- if ($ breakoutLevel instanceof Token) {
118- $ start = $ breakoutLevel ->getStartPosition ();
119- }
120- else {
121- $ start = $ breakoutLevel ->getStart ();
122- }
123- $ end = $ breakoutLevel ->getEndPosition ();
124-
125- return new Diagnostic (
126- DiagnosticKind::Error,
127- "Positive integer literal expected. " ,
128- $ start ,
129- $ end - $ start
130- );
131- }
132- }
133- return null ;
13487 }
13588
13689 /**
@@ -141,11 +94,15 @@ public static function checkDiagnostics($node) {
14194 public static function getDiagnostics (Node $ n ) : array {
14295 $ diagnostics = [];
14396
144- foreach ($ n ->getDescendantNodesAndTokens () as $ node ) {
97+ /**
98+ * @param \Microsoft\PhpParser\Node|\Microsoft\PhpParser\Token $node
99+ */
100+ $ n ->walkDescendantNodesAndTokens (function ($ node ) use (&$ diagnostics ) {
101+ // echo "Processing " . get_class($node) . "\n";
145102 if (($ diagnostic = self ::checkDiagnostics ($ node )) !== null ) {
146103 $ diagnostics [] = $ diagnostic ;
147104 }
148- }
105+ });
149106
150107 return $ diagnostics ;
151108 }
0 commit comments