Skip to content

Commit 0cec515

Browse files
committed
SlevomatCodingStandard.TypeHints.ParameterTypeHintSniff: "MissingTraversableTypeHintSpecification" is not reported when promoted property has @var annotation
1 parent 1cd31e0 commit 0cec515

File tree

7 files changed

+105
-11
lines changed

7 files changed

+105
-11
lines changed

SlevomatCodingStandard/Helpers/FunctionHelper.php

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHP_CodeSniffer\Util\Tokens;
88
use SlevomatCodingStandard\Helpers\Annotation\ParameterAnnotation;
99
use SlevomatCodingStandard\Helpers\Annotation\ReturnAnnotation;
10+
use SlevomatCodingStandard\Helpers\Annotation\VariableAnnotation;
1011
use function array_filter;
1112
use function array_map;
1213
use function array_merge;
@@ -324,11 +325,30 @@ public static function getParametersAnnotations(File $phpcsFile, int $functionPo
324325
}
325326

326327
/**
327-
* @return array<string, ParameterAnnotation>
328+
* @return array<string, ParameterAnnotation|VariableAnnotation>
328329
*/
329330
public static function getValidParametersAnnotations(File $phpcsFile, int $functionPointer): array
330331
{
332+
$tokens = $phpcsFile->getTokens();
333+
331334
$parametersAnnotations = [];
335+
336+
if (self::getName($phpcsFile, $functionPointer) === '__construct') {
337+
for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) {
338+
if ($tokens[$i]['code'] !== T_VARIABLE) {
339+
continue;
340+
}
341+
342+
/** @var VariableAnnotation[] $varAnnotations */
343+
$varAnnotations = AnnotationHelper::getAnnotationsByName($phpcsFile, $i, '@var');
344+
if ($varAnnotations === []) {
345+
continue;
346+
}
347+
348+
$parametersAnnotations[$tokens[$i]['content']] = $varAnnotations[0];
349+
}
350+
}
351+
332352
foreach (self::getParametersAnnotations($phpcsFile, $functionPointer) as $parameterAnnotation) {
333353
if ($parameterAnnotation->getContent() === null) {
334354
continue;
@@ -345,12 +365,30 @@ public static function getValidParametersAnnotations(File $phpcsFile, int $funct
345365
}
346366

347367
/**
348-
* @return array<string, ParameterAnnotation>
368+
* @return array<string, ParameterAnnotation|VariableAnnotation>
349369
*/
350370
public static function getValidPrefixedParametersAnnotations(File $phpcsFile, int $functionPointer): array
351371
{
372+
$tokens = $phpcsFile->getTokens();
373+
352374
$parametersAnnotations = [];
353375
foreach (AnnotationHelper::PREFIXES as $prefix) {
376+
if (self::getName($phpcsFile, $functionPointer) === '__construct') {
377+
for ($i = $tokens[$functionPointer]['parenthesis_opener'] + 1; $i < $tokens[$functionPointer]['parenthesis_closer']; $i++) {
378+
if ($tokens[$i]['code'] !== T_VARIABLE) {
379+
continue;
380+
}
381+
382+
/** @var VariableAnnotation[] $varAnnotations */
383+
$varAnnotations = AnnotationHelper::getAnnotationsByName($phpcsFile, $i, sprintf('@%s-var', $prefix));
384+
if ($varAnnotations === []) {
385+
continue;
386+
}
387+
388+
$parametersAnnotations[$tokens[$i]['content']] = $varAnnotations[0];
389+
}
390+
}
391+
354392
/** @var ParameterAnnotation[] $annotations */
355393
$annotations = AnnotationHelper::getAnnotationsByName($phpcsFile, $functionPointer, sprintf('@%s-param', $prefix));
356394
foreach ($annotations as $parameterAnnotation) {

SlevomatCodingStandard/Helpers/TypeHintHelper.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,12 @@ public static function convertUnofficialUnionTypeHintToOfficialTypeHints(string
102102

103103
public static function isTypeDefinedInAnnotation(File $phpcsFile, int $pointer, string $typeHint): bool
104104
{
105-
/** @var int $docCommentOpenPointer */
106105
$docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $pointer);
106+
107+
if ($docCommentOpenPointer === null) {
108+
return false;
109+
}
110+
107111
return self::isTemplate($phpcsFile, $docCommentOpenPointer, $typeHint)
108112
|| self::isAlias($phpcsFile, $docCommentOpenPointer, $typeHint);
109113
}

SlevomatCodingStandard/Sniffs/TypeHints/ParameterTypeHintSniff.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
1616
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
1717
use SlevomatCodingStandard\Helpers\Annotation\ParameterAnnotation;
18+
use SlevomatCodingStandard\Helpers\Annotation\VariableAnnotation;
1819
use SlevomatCodingStandard\Helpers\AnnotationHelper;
1920
use SlevomatCodingStandard\Helpers\AnnotationTypeHelper;
2021
use SlevomatCodingStandard\Helpers\DocCommentHelper;
@@ -40,6 +41,7 @@
4041
use function strtolower;
4142
use const T_BITWISE_AND;
4243
use const T_DOC_COMMENT_CLOSE_TAG;
44+
use const T_DOC_COMMENT_OPEN_TAG;
4345
use const T_DOC_COMMENT_STAR;
4446
use const T_ELLIPSIS;
4547
use const T_FUNCTION;
@@ -124,8 +126,8 @@ public function process(File $phpcsFile, $functionPointer): void
124126

125127
/**
126128
* @param (TypeHint|null)[] $parametersTypeHints
127-
* @param ParameterAnnotation[] $parametersAnnotations
128-
* @param ParameterAnnotation[] $prefixedParametersAnnotations
129+
* @param array<string, ParameterAnnotation|VariableAnnotation> $parametersAnnotations
130+
* @param array<string, ParameterAnnotation|VariableAnnotation> $prefixedParametersAnnotations
129131
*/
130132
private function checkTypeHints(
131133
File $phpcsFile,
@@ -384,8 +386,8 @@ private function checkTypeHints(
384386

385387
/**
386388
* @param (TypeHint|null)[] $parametersTypeHints
387-
* @param ParameterAnnotation[] $parametersAnnotations
388-
* @param ParameterAnnotation[] $prefixedParametersAnnotations
389+
* @param array<string, ParameterAnnotation|VariableAnnotation> $parametersAnnotations
390+
* @param array<string, ParameterAnnotation|VariableAnnotation> $prefixedParametersAnnotations
389391
*/
390392
private function checkTraversableTypeHintSpecification(
391393
File $phpcsFile,
@@ -499,7 +501,7 @@ private function checkTraversableTypeHintSpecification(
499501

500502
/**
501503
* @param (TypeHint|null)[] $parametersTypeHints
502-
* @param ParameterAnnotation[] $parametersAnnotations
504+
* @param array<string, ParameterAnnotation|VariableAnnotation> $parametersAnnotations
503505
*/
504506
private function checkUselessAnnotations(
505507
File $phpcsFile,
@@ -554,7 +556,10 @@ private function checkUselessAnnotations(
554556
continue;
555557
}
556558

557-
$docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $functionPointer);
559+
$docCommentOpenPointer = $parameterAnnotation instanceof VariableAnnotation
560+
? TokenHelper::findPrevious($phpcsFile, T_DOC_COMMENT_OPEN_TAG, $parameterAnnotation->getStartPointer() - 1)
561+
: DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $functionPointer);
562+
558563
$starPointer = TokenHelper::findPrevious(
559564
$phpcsFile,
560565
T_DOC_COMMENT_STAR,

tests/Sniffs/TypeHints/ParameterTypeHintSniffTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function testErrors(): void
2828
'traversableTypeHints' => ['Traversable', '\ArrayIterator'],
2929
]);
3030

31-
self::assertSame(47, $report->getErrorCount());
31+
self::assertSame(50, $report->getErrorCount());
3232

3333
self::assertSniffError($report, 6, ParameterTypeHintSniff::CODE_MISSING_ANY_TYPE_HINT);
3434
self::assertSniffError($report, 14, ParameterTypeHintSniff::CODE_MISSING_NATIVE_TYPE_HINT);
@@ -81,6 +81,10 @@ public function testErrors(): void
8181
self::assertSniffError($report, 285, ParameterTypeHintSniff::CODE_USELESS_ANNOTATION);
8282
self::assertSniffError($report, 292, ParameterTypeHintSniff::CODE_MISSING_ANY_TYPE_HINT);
8383

84+
self::assertSniffError($report, 301, ParameterTypeHintSniff::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION);
85+
self::assertSniffError($report, 303, ParameterTypeHintSniff::CODE_USELESS_ANNOTATION);
86+
self::assertSniffError($report, 304, ParameterTypeHintSniff::CODE_USELESS_ANNOTATION);
87+
8488
self::assertAllFixedInFile($report);
8589
}
8690

tests/Sniffs/TypeHints/data/parameterTypeHintErrors.fixed.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,17 @@ private function noTypeHintNoAnnotationWithPhpdoc($a)
282282
}
283283

284284
}
285+
286+
class Promoted
287+
{
288+
289+
public function __construct(
290+
public array $promoted,
291+
/***/
292+
public int $promoted2, /***/ private string $promoted3
293+
)
294+
{
295+
296+
}
297+
298+
}

tests/Sniffs/TypeHints/data/parameterTypeHintErrors.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,17 @@ private function noTypeHintNoAnnotationWithPhpdoc($a)
294294
}
295295

296296
}
297+
298+
class Promoted
299+
{
300+
301+
public function __construct(
302+
public array $promoted,
303+
/** @var int */
304+
public int $promoted2, /** @var string */ private string $promoted3
305+
)
306+
{
307+
308+
}
309+
310+
}

tests/Sniffs/TypeHints/data/parameterTypeHintNoErrors.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?php
1+
<?php // lint >= 8.0
22

33
use Doctrine\Common\Collections\ArrayCollection;
44

@@ -315,3 +315,18 @@ public function withNullableArrayAlias(?array $array)
315315
}
316316

317317
}
318+
319+
class Promoted
320+
{
321+
322+
public function __construct(
323+
/** @var array<int, string> */
324+
public array $promoted,
325+
/** @phpstan-var array<int, string> */
326+
public array $promoted2
327+
)
328+
{
329+
330+
}
331+
332+
}

0 commit comments

Comments
 (0)