Skip to content

Commit 41196b4

Browse files
committed
SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation: Fixed false positives for global constants
1 parent 4eeb22f commit 41196b4

File tree

6 files changed

+82
-37
lines changed

6 files changed

+82
-37
lines changed

SlevomatCodingStandard/Helpers/NamespaceHelper.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use function array_reverse;
77
use function array_slice;
88
use function count;
9+
use function defined;
910
use function explode;
1011
use function implode;
1112
use function in_array;
@@ -173,6 +174,11 @@ public static function resolveName(File $phpcsFile, string $nameAsReferencedInFi
173174
}
174175

175176
$name = sprintf('%s%s', self::NAMESPACE_SEPARATOR, $nameAsReferencedInFile);
177+
178+
if ($type === ReferencedName::TYPE_CONSTANT && defined($name)) {
179+
return $name;
180+
}
181+
176182
$namespaceName = self::findCurrentNamespaceName($phpcsFile, $currentPointer);
177183
if ($namespaceName !== null) {
178184
$name = sprintf('%s%s%s', self::NAMESPACE_SEPARATOR, $namespaceName, $name);

SlevomatCodingStandard/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniff.php

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use SlevomatCodingStandard\Helpers\AnnotationHelper;
1212
use SlevomatCodingStandard\Helpers\AnnotationTypeHelper;
1313
use SlevomatCodingStandard\Helpers\FixerHelper;
14+
use SlevomatCodingStandard\Helpers\NamespaceHelper;
15+
use SlevomatCodingStandard\Helpers\ReferencedName;
1416
use SlevomatCodingStandard\Helpers\TypeHelper;
1517
use SlevomatCodingStandard\Helpers\TypeHintHelper;
1618
use function sprintf;
@@ -101,19 +103,31 @@ public function process(File $phpcsFile, $docCommentOpenPointer): void
101103

102104
foreach (AnnotationHelper::getAnnotationConstantExpressions($annotation) as $constantExpression) {
103105
foreach (AnnotationConstantExpressionHelper::getConstantFetchNodes($constantExpression) as $constantFetchNode) {
104-
$typeHint = $constantFetchNode->className;
106+
$isClassConstant = $constantFetchNode->className !== '';
107+
108+
$typeHint = $isClassConstant
109+
? $constantFetchNode->className
110+
: $constantFetchNode->name;
111+
112+
$fullyQualifiedTypeHint = $isClassConstant
113+
? NamespaceHelper::resolveClassName(
114+
$phpcsFile,
115+
$typeHint,
116+
$annotation->getStartPointer()
117+
) : NamespaceHelper::resolveName(
118+
$phpcsFile,
119+
$typeHint,
120+
ReferencedName::TYPE_CONSTANT,
121+
$annotation->getStartPointer()
122+
);
105123

106-
$fullyQualifiedTypeHint = TypeHintHelper::getFullyQualifiedTypeHint(
107-
$phpcsFile,
108-
$annotation->getStartPointer(),
109-
$typeHint
110-
);
111124
if ($fullyQualifiedTypeHint === $typeHint) {
112125
continue;
113126
}
114127

115128
$fix = $phpcsFile->addFixableError(sprintf(
116-
'Class name %s in %s should be referenced via a fully qualified name.',
129+
'%s name %s in %s should be referenced via a fully qualified name.',
130+
$isClassConstant ? 'Class' : 'Constant',
117131
$fullyQualifiedTypeHint,
118132
$annotationName
119133
), $annotation->getStartPointer(), self::CODE_NON_FULLY_QUALIFIED_CLASS_NAME);
@@ -122,11 +136,15 @@ public function process(File $phpcsFile, $docCommentOpenPointer): void
122136
continue;
123137
}
124138

139+
$fixedConstantFetchNode = $isClassConstant
140+
? new ConstFetchNode($fullyQualifiedTypeHint, $constantFetchNode->name)
141+
: new ConstFetchNode('', $fullyQualifiedTypeHint);
142+
125143
$fixedAnnotationContent = AnnotationHelper::fixAnnotationConstantFetchNode(
126144
$phpcsFile,
127145
$annotation,
128146
$constantFetchNode,
129-
new ConstFetchNode($fullyQualifiedTypeHint, $constantFetchNode->name)
147+
$fixedConstantFetchNode
130148
);
131149

132150
$phpcsFile->fixer->beginChangeset();

tests/Sniffs/Namespaces/FullyQualifiedClassNameInAnnotationSniffTest.php

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public function testErrors(): void
1717
{
1818
$report = self::checkFile(__DIR__ . '/data/fullyQualifiedClassNameInAnnotationErrors.php');
1919

20-
self::assertSame(87, $report->getErrorCount());
20+
self::assertSame(89, $report->getErrorCount());
2121

2222
self::assertSniffError(
2323
$report,
@@ -324,178 +324,190 @@ public function testErrors(): void
324324
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
325325
'Class name \Traversable in @method should be referenced via a fully qualified name'
326326
);
327+
self::assertSniffError(
328+
$report,
329+
162,
330+
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
331+
'Constant name \SORT_DESC in @method should be referenced via a fully qualified name'
332+
);
333+
self::assertSniffError(
334+
$report,
335+
162,
336+
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
337+
'Constant name \SORT_NUMERIC in @method should be referenced via a fully qualified name'
338+
);
327339

328340
self::assertSniffError(
329341
$report,
330-
169,
342+
170,
331343
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
332344
'Class name \DateTime in @template should be referenced via a fully qualified name'
333345
);
334346
self::assertSniffError(
335347
$report,
336-
170,
348+
171,
337349
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
338350
'Class name \XXX\DateTimeInterface in @template should be referenced via a fully qualified name'
339351
);
340352
self::assertSniffError(
341353
$report,
342-
171,
354+
172,
343355
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
344356
'Class name \DateTimeImmutable in @template should be referenced via a fully qualified name'
345357
);
346358
self::assertSniffError(
347359
$report,
348-
172,
360+
173,
349361
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
350362
'Class name \DateTimeImmutable in @template-covariant should be referenced via a fully qualified name'
351363
);
352364
self::assertSniffError(
353365
$report,
354-
173,
366+
174,
355367
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
356368
'Class name \Iterator in @template-extends should be referenced via a fully qualified name'
357369
);
358370
self::assertSniffError(
359371
$report,
360-
173,
372+
174,
361373
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
362374
'Class name \DateTimeImmutable in @template-extends should be referenced via a fully qualified name'
363375
);
364376
self::assertSniffError(
365377
$report,
366-
174,
378+
175,
367379
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
368380
'Class name \Iterator in @template-implements should be referenced via a fully qualified name'
369381
);
370382
self::assertSniffError(
371383
$report,
372-
174,
384+
175,
373385
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
374386
'Class name \DateTimeImmutable in @template-implements should be referenced via a fully qualified name'
375387
);
376388
self::assertSniffError(
377389
$report,
378-
175,
390+
176,
379391
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
380392
'Class name \Iterator in @template-use should be referenced via a fully qualified name'
381393
);
382394
self::assertSniffError(
383395
$report,
384-
175,
396+
176,
385397
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
386398
'Class name \DateTimeImmutable in @template-use should be referenced via a fully qualified name'
387399
);
388400
self::assertSniffError(
389401
$report,
390-
181,
402+
182,
391403
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
392404
'Class name \XXX\TemplateThatDoesNotExist in @phpstan-return should be referenced via a fully qualified name'
393405
);
394406

395407
self::assertSniffError(
396408
$report,
397-
191,
409+
192,
398410
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
399411
'Class name \YYY\PropertyUsed in @mixin should be referenced via a fully qualified name'
400412
);
401413

402414
self::assertSniffError(
403415
$report,
404-
202,
416+
203,
405417
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
406418
'Class name \DateTimeImmutable in @param should be referenced via a fully qualified name'
407419
);
408420
self::assertSniffError(
409421
$report,
410-
209,
422+
210,
411423
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
412424
'Class name \DateTimeImmutable in @param should be referenced via a fully qualified name'
413425
);
414426
self::assertSniffError(
415427
$report,
416-
216,
428+
217,
417429
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
418430
'Class name \DateTimeImmutable in @param should be referenced via a fully qualified name'
419431
);
420432
self::assertSniffError(
421433
$report,
422-
216,
434+
217,
423435
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
424436
'Class name \DateTime in @param should be referenced via a fully qualified name'
425437
);
426438

427439
self::assertSniffError(
428440
$report,
429-
248,
441+
249,
430442
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
431443
'Class name \YYY\Partial\Conditional1 in @return should be referenced via a fully qualified name'
432444
);
433445
self::assertSniffError(
434446
$report,
435-
248,
447+
249,
436448
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
437449
'Class name \YYY\Partial\Conditional2 in @return should be referenced via a fully qualified name'
438450
);
439451
self::assertSniffError(
440452
$report,
441-
248,
453+
249,
442454
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
443455
'Class name \YYY\Partial\Conditional3 in @return should be referenced via a fully qualified name'
444456
);
445457
self::assertSniffError(
446458
$report,
447-
248,
459+
249,
448460
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
449461
'Class name \YYY\Partial\Conditional4 in @return should be referenced via a fully qualified name'
450462
);
451463
self::assertSniffError(
452464
$report,
453-
248,
465+
249,
454466
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
455467
'Class name \YYY\Partial\Conditional5 in @return should be referenced via a fully qualified name'
456468
);
457469
self::assertSniffError(
458470
$report,
459-
248,
471+
249,
460472
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
461473
'Class name \YYY\Partial\Conditional6 in @return should be referenced via a fully qualified name'
462474
);
463475
self::assertSniffError(
464476
$report,
465-
248,
477+
249,
466478
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
467479
'Class name \YYY\Partial\Conditional7 in @return should be referenced via a fully qualified name'
468480
);
469481

470482
self::assertSniffError(
471483
$report,
472-
255,
484+
256,
473485
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
474486
'Class name \YYY\Partial\Conditional8 in @return should be referenced via a fully qualified name'
475487
);
476488
self::assertSniffError(
477489
$report,
478-
255,
490+
256,
479491
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
480492
'Class name \YYY\Partial\Conditional9 in @return should be referenced via a fully qualified name'
481493
);
482494
self::assertSniffError(
483495
$report,
484-
255,
496+
256,
485497
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
486498
'Class name \YYY\Partial\Conditional10 in @return should be referenced via a fully qualified name'
487499
);
488500

489501
self::assertSniffError(
490502
$report,
491-
267,
503+
268,
492504
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
493505
'Class name \DateTime in @param-out should be referenced via a fully qualified name'
494506
);
495507

496508
self::assertSniffError(
497509
$report,
498-
279,
510+
280,
499511
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
500512
'Class name \DateTime in @phpstan-self-out should be referenced via a fully qualified name'
501513
);

tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.fixed.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public function returnsCallable()
159159
/**
160160
* @method method1(string $parameter = \Iterator::class)
161161
* @method method2(array $parameter = [\Iterator::class => \Traversable::class], $parameter2)
162+
* @method sortBy(callable $path, int $order = \SORT_DESC, int $sort = \SORT_NUMERIC)
162163
*/
163164
class ConstantExpression
164165
{

tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationErrors.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public function returnsCallable()
159159
/**
160160
* @method method1(string $parameter = Iterator::class)
161161
* @method method2(array $parameter = [Iterator::class => Traversable::class], $parameter2)
162+
* @method sortBy(callable $path, int $order = SORT_DESC, int $sort = SORT_NUMERIC)
162163
*/
163164
class ConstantExpression
164165
{

tests/Sniffs/Namespaces/data/fullyQualifiedClassNameInAnnotationNoErrors.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,10 @@ public function withNever()
176176
}
177177

178178
}
179+
180+
/**
181+
* @method sortBy(callable $path, int $order = \SORT_DESC, int $sort = \SORT_NUMERIC)
182+
*/
183+
class ConstantExpression
184+
{
185+
}

0 commit comments

Comments
 (0)