Skip to content

Commit 9b30d6f

Browse files
shmaxondrejmirtes
authored andcommittedFeb 19, 2025
TypeParser - support comments at EOL with //
1 parent 12c4e9f commit 9b30d6f

13 files changed

+1110
-81
lines changed
 

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ use PHPStan\PhpDocParser\Printer\Printer;
8383

8484
// basic setup with enabled required lexer attributes
8585

86-
$config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true]);
86+
$config = new ParserConfig(usedAttributes: ['lines' => true, 'indexes' => true, 'comments' => true]);
8787
$lexer = new Lexer($config);
8888
$constExprParser = new ConstExprParser($config);
8989
$typeParser = new TypeParser($config, $constExprParser);

‎src/Ast/Attribute.php

+2
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ final class Attribute
1313

1414
public const ORIGINAL_NODE = 'originalNode';
1515

16+
public const COMMENTS = 'comments';
17+
1618
}

‎src/Ast/Comment.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast;
4+
5+
use function trim;
6+
7+
class Comment
8+
{
9+
10+
public string $text;
11+
12+
public int $startLine;
13+
14+
public int $startIndex;
15+
16+
public function __construct(string $text, int $startLine = -1, int $startIndex = -1)
17+
{
18+
$this->text = $text;
19+
$this->startLine = $startLine;
20+
$this->startIndex = $startIndex;
21+
}
22+
23+
public function getReformattedText(): string
24+
{
25+
return trim($this->text);
26+
}
27+
28+
}

‎src/Ast/NodeAttributes.php

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ trait NodeAttributes
1515
*/
1616
public function setAttribute(string $key, $value): void
1717
{
18+
if ($value === null) {
19+
unset($this->attributes[$key]);
20+
return;
21+
}
1822
$this->attributes[$key] = $value;
1923
}
2024

‎src/Lexer/Lexer.php

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class Lexer
5151
public const TOKEN_NEGATED = 35;
5252
public const TOKEN_ARROW = 36;
5353

54+
public const TOKEN_COMMENT = 37;
55+
5456
public const TOKEN_LABELS = [
5557
self::TOKEN_REFERENCE => '\'&\'',
5658
self::TOKEN_UNION => '\'|\'',
@@ -66,6 +68,7 @@ class Lexer
6668
self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'',
6769
self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'',
6870
self::TOKEN_COMMA => '\',\'',
71+
self::TOKEN_COMMENT => '\'//\'',
6972
self::TOKEN_COLON => '\':\'',
7073
self::TOKEN_VARIADIC => '\'...\'',
7174
self::TOKEN_DOUBLE_COLON => '\'::\'',
@@ -160,6 +163,7 @@ private function generateRegexp(): string
160163
self::TOKEN_CLOSE_CURLY_BRACKET => '\\}',
161164

162165
self::TOKEN_COMMA => ',',
166+
self::TOKEN_COMMENT => '\/\/[^\\r\\n]*(?=\n|\r|\*/)',
163167
self::TOKEN_VARIADIC => '\\.\\.\\.',
164168
self::TOKEN_DOUBLE_COLON => '::',
165169
self::TOKEN_DOUBLE_ARROW => '=>',

‎src/Parser/PhpDocParser.php

+10
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,19 @@ public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
116116

117117
$tokens->forwardToTheEnd();
118118

119+
$comments = $tokens->flushComments();
120+
if ($comments !== []) {
121+
throw new LogicException('Comments should already be flushed');
122+
}
123+
119124
return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0);
120125
}
121126

127+
$comments = $tokens->flushComments();
128+
if ($comments !== []) {
129+
throw new LogicException('Comments should already be flushed');
130+
}
131+
122132
return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode($children), 1, 0);
123133
}
124134

‎src/Parser/TokenIterator.php

+48-14
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\PhpDocParser\Parser;
44

55
use LogicException;
6+
use PHPStan\PhpDocParser\Ast\Comment;
67
use PHPStan\PhpDocParser\Lexer\Lexer;
78
use function array_pop;
89
use function assert;
@@ -19,7 +20,10 @@ class TokenIterator
1920

2021
private int $index;
2122

22-
/** @var int[] */
23+
/** @var list<Comment> */
24+
private array $comments = [];
25+
26+
/** @var list<array{int, list<Comment>}> */
2327
private array $savePoints = [];
2428

2529
/** @var list<int> */
@@ -152,8 +156,7 @@ public function consumeTokenType(int $tokenType): void
152156
}
153157
}
154158

155-
$this->index++;
156-
$this->skipIrrelevantTokens();
159+
$this->next();
157160
}
158161

159162

@@ -166,8 +169,7 @@ public function consumeTokenValue(int $tokenType, string $tokenValue): void
166169
$this->throwError($tokenType, $tokenValue);
167170
}
168171

169-
$this->index++;
170-
$this->skipIrrelevantTokens();
172+
$this->next();
171173
}
172174

173175

@@ -178,12 +180,20 @@ public function tryConsumeTokenValue(string $tokenValue): bool
178180
return false;
179181
}
180182

181-
$this->index++;
182-
$this->skipIrrelevantTokens();
183+
$this->next();
183184

184185
return true;
185186
}
186187

188+
/**
189+
* @return list<Comment>
190+
*/
191+
public function flushComments(): array
192+
{
193+
$res = $this->comments;
194+
$this->comments = [];
195+
return $res;
196+
}
187197

188198
/** @phpstan-impure */
189199
public function tryConsumeTokenType(int $tokenType): bool
@@ -198,14 +208,15 @@ public function tryConsumeTokenType(int $tokenType): bool
198208
}
199209
}
200210

201-
$this->index++;
202-
$this->skipIrrelevantTokens();
211+
$this->next();
203212

204213
return true;
205214
}
206215

207216

208-
/** @phpstan-impure */
217+
/**
218+
* @deprecated Use skipNewLineTokensAndConsumeComments instead (when parsing a type)
219+
*/
209220
public function skipNewLineTokens(): void
210221
{
211222
if (!$this->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
@@ -218,6 +229,29 @@ public function skipNewLineTokens(): void
218229
}
219230

220231

232+
public function skipNewLineTokensAndConsumeComments(): void
233+
{
234+
if ($this->currentTokenType() === Lexer::TOKEN_COMMENT) {
235+
$this->comments[] = new Comment($this->currentTokenValue(), $this->currentTokenLine(), $this->currentTokenIndex());
236+
$this->next();
237+
}
238+
239+
if (!$this->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
240+
return;
241+
}
242+
243+
do {
244+
$foundNewLine = $this->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
245+
if ($this->currentTokenType() !== Lexer::TOKEN_COMMENT) {
246+
continue;
247+
}
248+
249+
$this->comments[] = new Comment($this->currentTokenValue(), $this->currentTokenLine(), $this->currentTokenIndex());
250+
$this->next();
251+
} while ($foundNewLine === true);
252+
}
253+
254+
221255
private function detectNewline(): void
222256
{
223257
$value = $this->currentTokenValue();
@@ -293,7 +327,7 @@ public function forwardToTheEnd(): void
293327

294328
public function pushSavePoint(): void
295329
{
296-
$this->savePoints[] = $this->index;
330+
$this->savePoints[] = [$this->index, $this->comments];
297331
}
298332

299333

@@ -305,9 +339,9 @@ public function dropSavePoint(): void
305339

306340
public function rollback(): void
307341
{
308-
$index = array_pop($this->savePoints);
309-
assert($index !== null);
310-
$this->index = $index;
342+
$savepoint = array_pop($this->savePoints);
343+
assert($savepoint !== null);
344+
[$this->index, $this->comments] = $savepoint;
311345
}
312346

313347

0 commit comments

Comments
 (0)
Failed to load comments.