Skip to content

Commit

Permalink
SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation…
Browse files Browse the repository at this point in the history
…: Fixed false positives for global constants
  • Loading branch information
kukulich committed Oct 19, 2022
1 parent 4eeb22f commit 41196b4
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 37 deletions.
6 changes: 6 additions & 0 deletions SlevomatCodingStandard/Helpers/NamespaceHelper.php
Expand Up @@ -6,6 +6,7 @@
use function array_reverse;
use function array_slice;
use function count;
use function defined;
use function explode;
use function implode;
use function in_array;
Expand Down Expand Up @@ -173,6 +174,11 @@ public static function resolveName(File $phpcsFile, string $nameAsReferencedInFi
}

$name = sprintf('%s%s', self::NAMESPACE_SEPARATOR, $nameAsReferencedInFile);

if ($type === ReferencedName::TYPE_CONSTANT && defined($name)) {
return $name;
}

$namespaceName = self::findCurrentNamespaceName($phpcsFile, $currentPointer);
if ($namespaceName !== null) {
$name = sprintf('%s%s%s', self::NAMESPACE_SEPARATOR, $namespaceName, $name);
Expand Down
Expand Up @@ -11,6 +11,8 @@
use SlevomatCodingStandard\Helpers\AnnotationHelper;
use SlevomatCodingStandard\Helpers\AnnotationTypeHelper;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\NamespaceHelper;
use SlevomatCodingStandard\Helpers\ReferencedName;
use SlevomatCodingStandard\Helpers\TypeHelper;
use SlevomatCodingStandard\Helpers\TypeHintHelper;
use function sprintf;
Expand Down Expand Up @@ -101,19 +103,31 @@ public function process(File $phpcsFile, $docCommentOpenPointer): void

foreach (AnnotationHelper::getAnnotationConstantExpressions($annotation) as $constantExpression) {
foreach (AnnotationConstantExpressionHelper::getConstantFetchNodes($constantExpression) as $constantFetchNode) {
$typeHint = $constantFetchNode->className;
$isClassConstant = $constantFetchNode->className !== '';

$typeHint = $isClassConstant
? $constantFetchNode->className
: $constantFetchNode->name;

$fullyQualifiedTypeHint = $isClassConstant
? NamespaceHelper::resolveClassName(
$phpcsFile,
$typeHint,
$annotation->getStartPointer()
) : NamespaceHelper::resolveName(
$phpcsFile,
$typeHint,
ReferencedName::TYPE_CONSTANT,
$annotation->getStartPointer()
);

$fullyQualifiedTypeHint = TypeHintHelper::getFullyQualifiedTypeHint(
$phpcsFile,
$annotation->getStartPointer(),
$typeHint
);
if ($fullyQualifiedTypeHint === $typeHint) {
continue;
}

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

$fixedConstantFetchNode = $isClassConstant
? new ConstFetchNode($fullyQualifiedTypeHint, $constantFetchNode->name)
: new ConstFetchNode('', $fullyQualifiedTypeHint);

$fixedAnnotationContent = AnnotationHelper::fixAnnotationConstantFetchNode(
$phpcsFile,
$annotation,
$constantFetchNode,
new ConstFetchNode($fullyQualifiedTypeHint, $constantFetchNode->name)
$fixedConstantFetchNode
);

$phpcsFile->fixer->beginChangeset();
Expand Down
Expand Up @@ -17,7 +17,7 @@ public function testErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/fullyQualifiedClassNameInAnnotationErrors.php');

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

self::assertSniffError(
$report,
Expand Down Expand Up @@ -324,178 +324,190 @@ public function testErrors(): void
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \Traversable in @method should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
162,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Constant name \SORT_DESC in @method should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
162,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Constant name \SORT_NUMERIC in @method should be referenced via a fully qualified name'
);

self::assertSniffError(
$report,
169,
170,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTime in @template should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
170,
171,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \XXX\DateTimeInterface in @template should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
171,
172,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @template should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
172,
173,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @template-covariant should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
173,
174,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \Iterator in @template-extends should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
173,
174,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @template-extends should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
174,
175,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \Iterator in @template-implements should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
174,
175,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @template-implements should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
175,
176,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \Iterator in @template-use should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
175,
176,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @template-use should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
181,
182,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \XXX\TemplateThatDoesNotExist in @phpstan-return should be referenced via a fully qualified name'
);

self::assertSniffError(
$report,
191,
192,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\PropertyUsed in @mixin should be referenced via a fully qualified name'
);

self::assertSniffError(
$report,
202,
203,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @param should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
209,
210,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @param should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
216,
217,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTimeImmutable in @param should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
216,
217,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTime in @param should be referenced via a fully qualified name'
);

self::assertSniffError(
$report,
248,
249,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional1 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
248,
249,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional2 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
248,
249,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional3 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
248,
249,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional4 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
248,
249,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional5 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
248,
249,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional6 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
248,
249,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional7 in @return should be referenced via a fully qualified name'
);

self::assertSniffError(
$report,
255,
256,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional8 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
255,
256,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional9 in @return should be referenced via a fully qualified name'
);
self::assertSniffError(
$report,
255,
256,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \YYY\Partial\Conditional10 in @return should be referenced via a fully qualified name'
);

self::assertSniffError(
$report,
267,
268,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTime in @param-out should be referenced via a fully qualified name'
);

self::assertSniffError(
$report,
279,
280,
FullyQualifiedClassNameInAnnotationSniff::CODE_NON_FULLY_QUALIFIED_CLASS_NAME,
'Class name \DateTime in @phpstan-self-out should be referenced via a fully qualified name'
);
Expand Down
Expand Up @@ -159,6 +159,7 @@ public function returnsCallable()
/**
* @method method1(string $parameter = \Iterator::class)
* @method method2(array $parameter = [\Iterator::class => \Traversable::class], $parameter2)
* @method sortBy(callable $path, int $order = \SORT_DESC, int $sort = \SORT_NUMERIC)
*/
class ConstantExpression
{
Expand Down
Expand Up @@ -159,6 +159,7 @@ public function returnsCallable()
/**
* @method method1(string $parameter = Iterator::class)
* @method method2(array $parameter = [Iterator::class => Traversable::class], $parameter2)
* @method sortBy(callable $path, int $order = SORT_DESC, int $sort = SORT_NUMERIC)
*/
class ConstantExpression
{
Expand Down
Expand Up @@ -176,3 +176,10 @@ public function withNever()
}

}

/**
* @method sortBy(callable $path, int $order = \SORT_DESC, int $sort = \SORT_NUMERIC)
*/
class ConstantExpression
{
}

0 comments on commit 41196b4

Please sign in to comment.