Skip to content

Commit ca7b670

Browse files
committed
Improved annotations parsing
1 parent 3a9b0bd commit ca7b670

File tree

3 files changed

+44
-6
lines changed

3 files changed

+44
-6
lines changed

SlevomatCodingStandard/Helpers/AnnotationHelper.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,27 @@ public static function getAnnotations(\PHP_CodeSniffer\Files\File $codeSnifferFi
3737
}
3838

3939
$tokens = $codeSnifferFile->getTokens();
40-
for ($i = $docCommentOpenToken + 1; $i < $tokens[$docCommentOpenToken]['comment_closer']; $i++) {
40+
$i = $docCommentOpenToken + 1;
41+
while ($i < $tokens[$docCommentOpenToken]['comment_closer']) {
42+
$annotationPointer = $i;
43+
4144
if ($tokens[$i]['code'] !== T_DOC_COMMENT_TAG) {
45+
$i++;
4246
continue;
4347
}
4448

4549
// Fix for wrong PHPCS parsing
50+
$parenthesesLevel = substr_count($tokens[$i]['content'], '(') - substr_count($tokens[$i]['content'], ')');
4651
$annotationCode = $tokens[$i]['content'];
4752

48-
for ($j = $i + 1; $j < $tokens[$docCommentOpenToken]['comment_closer']; $j++) {
49-
if (!in_array($tokens[$j]['code'], [T_DOC_COMMENT_WHITESPACE, T_DOC_COMMENT_STRING, T_DOC_COMMENT_STAR], true)) {
53+
for ($j = $i + 1; $j <= $tokens[$docCommentOpenToken]['comment_closer']; $j++) {
54+
if ($tokens[$j]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
55+
$i = $j;
56+
break;
57+
}
58+
59+
if ($tokens[$j]['code'] === T_DOC_COMMENT_TAG && $parenthesesLevel === 0) {
60+
$i = $j;
5061
break;
5162
}
5263

@@ -63,13 +74,14 @@ public static function getAnnotations(\PHP_CodeSniffer\Files\File $codeSnifferFi
6374
}
6475
}
6576

77+
$parenthesesLevel += substr_count($tokens[$j]['content'], '(') - substr_count($tokens[$j]['content'], ')');
6678
$annotationCode .= $tokens[$j]['content'];
6779
}
6880

69-
$annotationName = $tokens[$i]['content'];
81+
$annotationName = $tokens[$annotationPointer]['content'];
7082
$annotationParameters = null;
7183
$annotationContent = null;
72-
if (preg_match('~^(@[a-zA-Z\\\\]+)(?:\((.*?)\))?(?:\\s+(.+))?($)~s', trim($annotationCode), $matches)) {
84+
if (preg_match('~^(@[a-zA-Z\\\\]+)(?:\((.*)\))?(?:\\s+(.+))?($)~s', trim($annotationCode), $matches)) {
7385
$annotationName = $matches[1];
7486
$annotationParameters = trim($matches[2]);
7587
if ($annotationParameters === '') {
@@ -81,7 +93,7 @@ public static function getAnnotations(\PHP_CodeSniffer\Files\File $codeSnifferFi
8193
}
8294
}
8395

84-
$annotations[$annotationName][] = new Annotation($annotationName, $i, $annotationParameters, $annotationContent);
96+
$annotations[$annotationName][] = new Annotation($annotationName, $annotationPointer, $annotationParameters, $annotationContent);
8597
}
8698

8799
return $annotations;

tests/Helpers/AnnotationHelperTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,20 @@ public function testFunctionWithoutAnnotation(): void
107107
self::assertCount(0, AnnotationHelper::getAnnotationsByName($this->getTestedCodeSnifferFile(), $this->findFunctionPointerByName($this->getTestedCodeSnifferFile(), 'withoutAnnotation'), '@param'));
108108
}
109109

110+
public function testMultilineIndentedAnnotation(): void
111+
{
112+
$annotations = AnnotationHelper::getAnnotations($this->getTestedCodeSnifferFile(), $this->findPropertyPointerByName($this->getTestedCodeSnifferFile(), 'multilineIndentedAnnotation'));
113+
114+
self::assertCount(1, $annotations);
115+
self::assertArrayHasKey('@X', $annotations);
116+
117+
$xAnnotations = $annotations['@X'];
118+
119+
self::assertCount(1, $xAnnotations);
120+
self::assertSame('@X', $xAnnotations[0]->getName());
121+
self::assertSame('Content', $xAnnotations[0]->getContent());
122+
}
123+
110124
private function getTestedCodeSnifferFile(): \PHP_CodeSniffer\Files\File
111125
{
112126
if ($this->testedCodeSnifferFile === null) {

tests/Helpers/data/annotation.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,16 @@ abstract public function withMultilineParametrizedAnnotation();
6060
/** @ORM\OneToMany(targetEntity=Bar::class, mappedBy="boo") */
6161
private $inlineDocComment;
6262

63+
/**
64+
* @X(
65+
* a=Y::SOME,
66+
* b={
67+
* @Z(
68+
* code=123
69+
* )
70+
* }
71+
* ) Content
72+
*/
73+
private $multilineIndentedAnnotation;
74+
6375
}

0 commit comments

Comments
 (0)