diff --git a/src/Annotator/AbstractAnnotator.php b/src/Annotator/AbstractAnnotator.php index 8e50efcc..774eb26b 100644 --- a/src/Annotator/AbstractAnnotator.php +++ b/src/Annotator/AbstractAnnotator.php @@ -480,7 +480,29 @@ protected function parseExistingAnnotations(File $file, int $closeTagIndex, arra /** @var \PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode $valueNode */ $valueNode = static::getValueNode($tokens[$i]['content'], $content); if ($valueNode instanceof InvalidTagValueNode) { - continue; + $multilineFixed = false; + for ($p = $i + 3; $p < $closeTagIndex; $p++) { + if ($tokens[$p]['type'] === 'T_DOC_COMMENT_TAG') { + break; + } + + if ($tokens[$p]['type'] !== 'T_DOC_COMMENT_STRING') { + continue; + } + + $content .= $tokens[$p]['content']; + /** @var \PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode|\PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode $valueNode */ + $valueNode = static::getValueNode($tokens[$i]['content'], $content); + if (!($valueNode instanceof InvalidTagValueNode)) { + $multilineFixed = true; + + break; + } + } + + if (!$multilineFixed || $valueNode instanceof InvalidTagValueNode) { + continue; + } } $returnTypes = $this->valueNodeParts($valueNode); diff --git a/tests/TestCase/Annotator/TemplateAnnotatorTest.php b/tests/TestCase/Annotator/TemplateAnnotatorTest.php index f7e9272c..9aa56106 100644 --- a/tests/TestCase/Annotator/TemplateAnnotatorTest.php +++ b/tests/TestCase/Annotator/TemplateAnnotatorTest.php @@ -438,6 +438,33 @@ public function testAnnotateWithShapedArray() { $this->assertTextContains(' -> 1 annotation added.', $output); } + /** + * Tests that a multiline array is parsed completly. + * + * @return void + */ + public function testAnnotateWithMultilineArray() { + $annotator = $this->_getAnnotatorMock([]); + + $expectedContent = str_replace("\r\n", "\n", file_get_contents(TEST_FILES . 'templates/multiline.php')); + $callback = function($value) use ($expectedContent) { + $value = str_replace(["\r\n", "\r"], "\n", $value); + if ($value !== $expectedContent) { + $this->_displayDiff($expectedContent, $value); + } + + return $value === $expectedContent; + }; + $annotator->expects($this->once())->method('storeFile')->with($this->anything(), $this->callback($callback)); + + $path = TEST_ROOT . 'templates/Foos/multiline.php'; + $annotator->annotate($path); + + $output = $this->out->output(); + + $this->assertTextContains(' -> 1 annotation added.', $output); + } + /** * @param array $params * @return \IdeHelper\Annotator\TemplateAnnotator|\PHPUnit\Framework\MockObject\MockObject diff --git a/tests/test_app/templates/Foos/multiline.php b/tests/test_app/templates/Foos/multiline.php new file mode 100644 index 00000000..ad5bf042 --- /dev/null +++ b/tests/test_app/templates/Foos/multiline.php @@ -0,0 +1,34 @@ + $ints + * @var array{ + * a: int, + * b: string|null + * }|null $shaped + * @var array{ + * c: array{ + * d: int|string, + * e: string|null + * } + * } $nested + */ + foreach ($x as $y) { + echo $y; + } + foreach ($foo as $int) { + echo $int; + } +?> +