1010
1111class DiagnosticsProvider {
1212
13+ /**
14+ * @var string[] maps the token kind to the corresponding name
15+ */
1316 private static $ tokenKindToText ;
1417
18+ /**
19+ * @param int $kind (must be a valid token kind)
20+ * @return string
21+ */
22+ public static function getTextForTokenKind ($ kind ) {
23+ return self ::$ tokenKindToText [$ kind ];
24+ }
25+
26+ /**
27+ * This is called when this class is loaded, at the bottom of this file.
28+ * @return void
29+ */
30+ public static function initTokenKindToText () {
31+ self ::$ tokenKindToText = \array_flip (\array_merge (
32+ TokenStringMaps::OPERATORS_AND_PUNCTUATORS ,
33+ TokenStringMaps::KEYWORDS ,
34+ TokenStringMaps::RESERVED_WORDS
35+ ));
36+ }
37+
1538 /**
1639 * Returns the diagnostic for $node, or null.
17- * @param \Microsoft\PhpParser\Node $node
40+ * @param \Microsoft\PhpParser\Node|\Microsoft\PhpParser\Token $node
1841 * @return Diagnostic|null
1942 */
2043 public static function checkDiagnostics ($ node ) {
21- if (!isset (self ::$ tokenKindToText )) {
22- self ::$ tokenKindToText = \array_flip (\array_merge (
23- TokenStringMaps::OPERATORS_AND_PUNCTUATORS ,
24- TokenStringMaps::KEYWORDS ,
25- TokenStringMaps::RESERVED_WORDS
26- ));
44+ if ($ node instanceof Token) {
45+ if (\get_class ($ node ) === Token::class) {
46+ return null ;
47+ }
48+ return self ::checkDiagnosticForUnexpectedToken ($ node );
2749 }
2850
29- if ($ node instanceof SkippedToken) {
51+ if ($ node instanceof Node) {
52+ return $ node ->getDiagnosticForNode ();
53+ }
54+ return null ;
55+ }
56+
57+ /**
58+ * @param Token $token
59+ * @return Diagnostic|null
60+ */
61+ private static function checkDiagnosticForUnexpectedToken ($ token ) {
62+ if ($ token instanceof SkippedToken) {
3063 // TODO - consider also attaching parse context information to skipped tokens
3164 // this would allow us to provide more helpful error messages that inform users what to do
3265 // about the problem rather than simply pointing out the mistake.
3366 return new Diagnostic (
3467 DiagnosticKind::Error,
3568 "Unexpected ' " .
36- (self ::$ tokenKindToText [$ node ->kind ]
37- ?? Token::getTokenKindNameFromValue ($ node ->kind )) .
69+ (self ::$ tokenKindToText [$ token ->kind ]
70+ ?? Token::getTokenKindNameFromValue ($ token ->kind )) .
3871 "' " ,
39- $ node ->start ,
40- $ node ->getEndPosition () - $ node ->start
72+ $ token ->start ,
73+ $ token ->getEndPosition () - $ token ->start
4174 );
42- } elseif ($ node instanceof MissingToken) {
75+ } elseif ($ token instanceof MissingToken) {
4376 return new Diagnostic (
4477 DiagnosticKind::Error,
4578 "' " .
46- (self ::$ tokenKindToText [$ node ->kind ]
47- ?? Token::getTokenKindNameFromValue ($ node ->kind )) .
79+ (self ::$ tokenKindToText [$ token ->kind ]
80+ ?? Token::getTokenKindNameFromValue ($ token ->kind )) .
4881 "' expected. " ,
49- $ node ->start ,
50- $ node ->getEndPosition () - $ node ->start
82+ $ token ->start ,
83+ $ token ->getEndPosition () - $ token ->start
5184 );
5285 }
53-
54- if ($ node === null || $ node instanceof Token) {
55- return null ;
56- }
57-
58- if ($ node instanceof Node) {
59- if ($ node instanceof Node \MethodDeclaration) {
60- foreach ($ node ->modifiers as $ modifier ) {
61- if ($ modifier ->kind === TokenKind::VarKeyword) {
62- return new Diagnostic (
63- DiagnosticKind::Error,
64- "Unexpected modifier ' " . self ::$ tokenKindToText [$ modifier ->kind ] . "' " ,
65- $ modifier ->start ,
66- $ modifier ->length
67- );
68- }
69- }
70- }
71- elseif ($ node instanceof Node \Statement \NamespaceUseDeclaration) {
72- if (
73- $ node ->useClauses != null
74- && \count ($ node ->useClauses ->children ) > 1
75- ) {
76- foreach ($ node ->useClauses ->children as $ useClause ) {
77- if ($ useClause instanceof Node \NamespaceUseClause && !is_null ($ useClause ->openBrace )) {
78- return new Diagnostic (
79- DiagnosticKind::Error,
80- "; expected. " ,
81- $ useClause ->getEndPosition (),
82- 1
83- );
84- }
85- }
86- }
87- }
88- else if ($ node instanceof Node \Statement \BreakOrContinueStatement) {
89- if ($ node ->breakoutLevel === null ) {
90- return null ;
91- }
92-
93- $ breakoutLevel = $ node ->breakoutLevel ;
94- while ($ breakoutLevel instanceof Node \Expression \ParenthesizedExpression) {
95- $ breakoutLevel = $ breakoutLevel ->expression ;
96- }
97-
98- if (
99- $ breakoutLevel instanceof Node \NumericLiteral
100- && $ breakoutLevel ->children ->kind === TokenKind::IntegerLiteralToken
101- ) {
102- $ literalString = $ breakoutLevel ->getText ();
103- $ firstTwoChars = \substr ($ literalString , 0 , 2 );
104-
105- if ($ firstTwoChars === '0b ' || $ firstTwoChars === '0B ' ) {
106- if (\bindec (\substr ($ literalString , 2 )) > 0 ) {
107- return null ;
108- }
109- }
110- else if (\intval ($ literalString , 0 ) > 0 ) {
111- return null ;
112- }
113- }
114-
115- if ($ breakoutLevel instanceof Token) {
116- $ start = $ breakoutLevel ->getStartPosition ();
117- }
118- else {
119- $ start = $ breakoutLevel ->getStart ();
120- }
121- $ end = $ breakoutLevel ->getEndPosition ();
122-
123- return new Diagnostic (
124- DiagnosticKind::Error,
125- "Positive integer literal expected. " ,
126- $ start ,
127- $ end - $ start
128- );
129- }
130- }
131- return null ;
13286 }
13387
13488 /**
@@ -139,12 +93,17 @@ public static function checkDiagnostics($node) {
13993 public static function getDiagnostics (Node $ n ) : array {
14094 $ diagnostics = [];
14195
142- foreach ($ n ->getDescendantNodesAndTokens () as $ node ) {
96+ /**
97+ * @param \Microsoft\PhpParser\Node|\Microsoft\PhpParser\Token $node
98+ */
99+ $ n ->walkDescendantNodesAndTokens (function ($ node ) use (&$ diagnostics ) {
143100 if (($ diagnostic = self ::checkDiagnostics ($ node )) !== null ) {
144101 $ diagnostics [] = $ diagnostic ;
145102 }
146- }
103+ });
147104
148105 return $ diagnostics ;
149106 }
150107}
108+
109+ DiagnosticsProvider::initTokenKindToText ();
0 commit comments