diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index 085282c0..bf5b18a3 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -127,6 +127,14 @@ private function enrichWithAttributes(TokenIterator $tokens, Ast\Node $tag, int } if ($this->useIndexAttributes) { + $tokensArray = $tokens->getTokens(); + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_CLOSE_PHPDOC) { + $endIndex--; + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + $endIndex--; + } + } + $tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex); $tag->setAttribute(Ast\Attribute::END_INDEX, $endIndex); } diff --git a/src/Parser/TokenIterator.php b/src/Parser/TokenIterator.php index f01200ac..bb197e6d 100644 --- a/src/Parser/TokenIterator.php +++ b/src/Parser/TokenIterator.php @@ -37,6 +37,15 @@ public function __construct(array $tokens, int $index = 0) } + /** + * @return list + */ + public function getTokens(): array + { + return $this->tokens; + } + + public function currentTokenValue(): string { return $this->tokens[$this->index][Lexer::VALUE_OFFSET]; diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index 5d005a63..3016406c 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -72,6 +72,14 @@ private function enrichWithAttributes(TokenIterator $tokens, Ast\Type\TypeNode $ } if ($this->useIndexAttributes) { + $tokensArray = $tokens->getTokens(); + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_CLOSE_PHPDOC) { + $endIndex--; + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + $endIndex--; + } + } + $type->setAttribute(Ast\Attribute::START_INDEX, $startIndex); $type->setAttribute(Ast\Attribute::END_INDEX, $endIndex); } diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 7f51e9b8..7798828d 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -5530,7 +5530,7 @@ public function dataLinesAndIndexes(): iterable yield [ '/** @param Foo $a */', [ - [1, 1, 1, 7], + [1, 1, 1, 5], ], ]; @@ -5573,7 +5573,7 @@ public function dataLinesAndIndexes(): iterable yield [ '/** @param Foo( */', [ - [1, 1, 1, 6], + [1, 1, 1, 4], ], ]; @@ -5587,7 +5587,28 @@ public function dataLinesAndIndexes(): iterable yield [ '/** @param Foo::** $a */', [ - [1, 1, 1, 10], + [1, 1, 1, 8], + ], + ]; + + yield [ + '/** @param Foo::** $a*/', + [ + [1, 1, 1, 8], + ], + ]; + + yield [ + '/** @return Foo */', + [ + [1, 1, 1, 3], + ], + ]; + + yield [ + '/** @return Foo*/', + [ + [1, 1, 1, 3], ], ]; } @@ -5617,4 +5638,47 @@ public function testLinesAndIndexes(string $phpDoc, array $childrenLines): void } } + /** + * @return array + */ + public function dataTypeLinesAndIndexes(): iterable + { + yield [ + '/** @return Foo */', + [1, 1, 3, 3], + ]; + + yield [ + '/** @return Foo*/', + [1, 1, 3, 3], + ]; + } + + /** + * @dataProvider dataTypeLinesAndIndexes + * @param array{int, int, int, int} $lines + */ + public function testTypeLinesAndIndexes(string $phpDoc, array $lines): void + { + $tokens = new TokenIterator($this->lexer->tokenize($phpDoc)); + $constExprParser = new ConstExprParser(true, true); + $usedAttributes = [ + 'lines' => true, + 'indexes' => true, + ]; + $typeParser = new TypeParser($constExprParser, true, $usedAttributes); + $phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes); + $phpDocNode = $phpDocParser->parse($tokens); + $this->assertInstanceOf(PhpDocTagNode::class, $phpDocNode->children[0]); + $this->assertInstanceOf(ReturnTagValueNode::class, $phpDocNode->children[0]->value); + + $type = $phpDocNode->children[0]->value->type; + $this->assertInstanceOf(IdentifierTypeNode::class, $type); + + $this->assertSame($lines[0], $type->getAttribute(Attribute::START_LINE)); + $this->assertSame($lines[1], $type->getAttribute(Attribute::END_LINE)); + $this->assertSame($lines[2], $type->getAttribute(Attribute::START_INDEX)); + $this->assertSame($lines[3], $type->getAttribute(Attribute::END_INDEX)); + } + }