Skip to content

Commit 5b710b8

Browse files
committed
PEAR/FunctionComment: prevent false positives with attributes
PHP 8.0+ attributes can be placed between a docblock and the function declaration it applies to. The `PEAR.Commenting.FunctionComment` sniff - and by extension the `Squiz.Commenting.FunctionComment` sniff - did not yet take this into account. This would result in a false positive `Missing doc comment for function ... ` error. Resolving that error though, would result in a new `There must be no blank lines after the function comment` error, so the blank line check also needed to be fixed. With the current fix, blank lines between the docblock and the function declaration are still not allowed, but non-blank lines between the two (i.e. lines containing attributes) will be ignored. Only one "no blank lines between" error will be thrown per function declaration.
1 parent d5c1cf7 commit 5b710b8

File tree

4 files changed

+128
-6
lines changed

4 files changed

+128
-6
lines changed

src/Standards/PEAR/Sniffs/Commenting/FunctionCommentSniff.php

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,25 @@ public function process(File $phpcsFile, $stackPtr)
6868
return;
6969
}
7070

71-
$tokens = $phpcsFile->getTokens();
72-
$ignore = Tokens::$methodPrefixes;
73-
$ignore[] = T_WHITESPACE;
71+
$tokens = $phpcsFile->getTokens();
72+
$ignore = Tokens::$methodPrefixes;
73+
$ignore[T_WHITESPACE] = T_WHITESPACE;
74+
75+
for ($commentEnd = ($stackPtr - 1); $commentEnd >= 0; $commentEnd--) {
76+
if (isset($ignore[$tokens[$commentEnd]['code']]) === true) {
77+
continue;
78+
}
79+
80+
if ($tokens[$commentEnd]['code'] === T_ATTRIBUTE_END
81+
&& isset($tokens[$commentEnd]['attribute_opener']) === true
82+
) {
83+
$commentEnd = $tokens[$commentEnd]['attribute_opener'];
84+
continue;
85+
}
86+
87+
break;
88+
}
7489

75-
$commentEnd = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
7690
if ($tokens[$commentEnd]['code'] === T_COMMENT) {
7791
// Inline comments might just be closing comments for
7892
// control structures or functions instead of function comments
@@ -106,8 +120,19 @@ public function process(File $phpcsFile, $stackPtr)
106120
}
107121

108122
if ($tokens[$commentEnd]['line'] !== ($tokens[$stackPtr]['line'] - 1)) {
109-
$error = 'There must be no blank lines after the function comment';
110-
$phpcsFile->addError($error, $commentEnd, 'SpacingAfter');
123+
for ($i = ($commentEnd + 1); $i < $stackPtr; $i++) {
124+
if ($tokens[$i]['column'] !== 1) {
125+
continue;
126+
}
127+
128+
if ($tokens[$i]['code'] === T_WHITESPACE
129+
&& $tokens[$i]['line'] !== $tokens[($i + 1)]['line']
130+
) {
131+
$error = 'There must be no blank lines after the function comment';
132+
$phpcsFile->addError($error, $commentEnd, 'SpacingAfter');
133+
break;
134+
}
135+
}
111136
}
112137

113138
$commentStart = $tokens[$commentEnd]['comment_opener'];

src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,3 +429,50 @@ public function ignored() {
429429
}
430430

431431
// phpcs:set PEAR.Commenting.FunctionComment specialMethods[] __construct,__destruct
432+
433+
class Something implements JsonSerializable {
434+
/**
435+
* Single attribute.
436+
*
437+
* @return mixed
438+
*/
439+
#[ReturnTypeWillChange]
440+
public function jsonSerialize() {}
441+
442+
/**
443+
* Multiple attributes.
444+
*
445+
* @return Something
446+
*/
447+
#[AttributeA]
448+
#[AttributeB]
449+
public function methodName() {}
450+
451+
/**
452+
* Blank line between docblock and attribute.
453+
*
454+
* @return mixed
455+
*/
456+
457+
#[ReturnTypeWillChange]
458+
public function blankLineDetectionA() {}
459+
460+
/**
461+
* Blank line between attribute and function declaration.
462+
*
463+
* @return mixed
464+
*/
465+
#[ReturnTypeWillChange]
466+
467+
public function blankLineDetectionB() {}
468+
469+
/**
470+
* Blank line between both docblock and attribute and attribute and function declaration.
471+
*
472+
* @return mixed
473+
*/
474+
475+
#[ReturnTypeWillChange]
476+
477+
public function blankLineDetectionC() {}
478+
}

src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc.fixed

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,3 +429,50 @@ public function ignored() {
429429
}
430430

431431
// phpcs:set PEAR.Commenting.FunctionComment specialMethods[] __construct,__destruct
432+
433+
class Something implements JsonSerializable {
434+
/**
435+
* Single attribute.
436+
*
437+
* @return mixed
438+
*/
439+
#[ReturnTypeWillChange]
440+
public function jsonSerialize() {}
441+
442+
/**
443+
* Multiple attributes.
444+
*
445+
* @return Something
446+
*/
447+
#[AttributeA]
448+
#[AttributeB]
449+
public function methodName() {}
450+
451+
/**
452+
* Blank line between docblock and attribute.
453+
*
454+
* @return mixed
455+
*/
456+
457+
#[ReturnTypeWillChange]
458+
public function blankLineDetectionA() {}
459+
460+
/**
461+
* Blank line between attribute and function declaration.
462+
*
463+
* @return mixed
464+
*/
465+
#[ReturnTypeWillChange]
466+
467+
public function blankLineDetectionB() {}
468+
469+
/**
470+
* Blank line between both docblock and attribute and attribute and function declaration.
471+
*
472+
* @return mixed
473+
*/
474+
475+
#[ReturnTypeWillChange]
476+
477+
public function blankLineDetectionC() {}
478+
}

src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ public function getErrorList()
7070
364 => 1,
7171
406 => 1,
7272
417 => 1,
73+
455 => 1,
74+
464 => 1,
75+
473 => 1,
7376
];
7477

7578
}//end getErrorList()

0 commit comments

Comments
 (0)