From b742ecb6aea2149a7bff491c88bdaa5115c58728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Hansl=C3=ADk?= Date: Thu, 4 Jan 2018 23:15:17 +0100 Subject: [PATCH] ReferenceUsedNamesOnlySniff: functions and constants support is hopefully complete --- README.md | 4 + .../Helpers/ConstantHelper.php | 60 ++++++++ .../Helpers/FunctionHelper.php | 32 +++++ .../Helpers/ReferencedName.php | 5 + .../ReferenceUsedNamesOnlySniff.php | 128 +++++++++++++++--- build/phpcs.xml | 2 + tests/Helpers/ConstantHelperTest.php | 32 +++++ tests/Helpers/FunctionHelperTest.php | 6 + tests/Helpers/data/constantNames.php | 14 ++ tests/Helpers/data/constantWithNamespace.php | 5 + .../Helpers/data/constantWithoutNamespace.php | 3 + tests/Helpers/data/functionNames.php | 26 ++++ .../ReferenceUsedNamesOnlySniffTest.php | 72 +++++++++- .../collidingFullyQualifiedConstantNames.php | 15 ++ .../collidingFullyQualifiedFunctionNames.php | 18 +++ ...leReferenceViaFullyQualifiedName.fixed.php | 4 + .../fixableReferenceViaFullyQualifiedName.php | 2 + ...fullyQualifiedGlobalClassesInNamespace.php | 3 + ...llyQualifiedGlobalConstantsInNamespace.php | 1 + ...llyQualifiedGlobalFunctionsInNamespace.php | 1 + .../data/shouldBeInUseStatement.php | 2 + 21 files changed, 411 insertions(+), 24 deletions(-) create mode 100644 SlevomatCodingStandard/Helpers/ConstantHelper.php create mode 100644 tests/Helpers/ConstantHelperTest.php create mode 100644 tests/Helpers/data/constantNames.php create mode 100644 tests/Helpers/data/constantWithNamespace.php create mode 100644 tests/Helpers/data/constantWithoutNamespace.php create mode 100644 tests/Helpers/data/functionNames.php create mode 100644 tests/Sniffs/Namespaces/data/collidingFullyQualifiedConstantNames.php create mode 100644 tests/Sniffs/Namespaces/data/collidingFullyQualifiedFunctionNames.php diff --git a/README.md b/README.md index 5288f6a95..da01b4c5a 100644 --- a/README.md +++ b/README.md @@ -335,9 +335,13 @@ new Foo\Bar(); * `namespacesRequiredToUse`: if not set, all namespaces are required to be used. When set, only mentioned namespaces are required to be used. Useful in tandem with UseOnlyWhitelistedNamespaces sniff. * `allowFullyQualifiedNameForCollidingClasses`: allow fully qualified name for a class with a colliding use statement. +* `allowFullyQualifiedNameForCollidingFunctions`: allow fully qualified name for a function with a colliding use statement. +* `allowFullyQualifiedNameForCollidingConstants`: allow fully qualified name for a constant with a colliding use statement. * `allowFullyQualifiedGlobalClasses`: allows using fully qualified classes from global space (i.e. `\DateTimeImmutable`). * `allowFullyQualifiedGlobalFunctions`: allows using fully qualified functions from global space (i.e. `\phpversion()`). * `allowFullyQualifiedGlobalConstants`: allows using fully qualified constants from global space (i.e. `\PHP_VERSION`). +* `allowFallbackGlobalFunctions`: allows using global functions via fallback name without `use` (i.e. `phpversion()`). +* `allowFallbackGlobalConstants`: allows using global constants via fallback name without `use` (i.e. `PHP_VERSION`). #### SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces diff --git a/SlevomatCodingStandard/Helpers/ConstantHelper.php b/SlevomatCodingStandard/Helpers/ConstantHelper.php new file mode 100644 index 000000000..5b34dcb1f --- /dev/null +++ b/SlevomatCodingStandard/Helpers/ConstantHelper.php @@ -0,0 +1,60 @@ +getTokens(); + return $tokens[TokenHelper::findNext($codeSnifferFile, T_STRING, $constantPointer + 1)]['content']; + } + + public static function getFullyQualifiedName(\PHP_CodeSniffer\Files\File $codeSnifferFile, int $constantPointer): string + { + $name = self::getName($codeSnifferFile, $constantPointer); + $namespace = NamespaceHelper::findCurrentNamespaceName($codeSnifferFile, $constantPointer); + + return $namespace !== null ? sprintf('%s%s%s%s', NamespaceHelper::NAMESPACE_SEPARATOR, $namespace, NamespaceHelper::NAMESPACE_SEPARATOR, $name) : $name; + } + + /** + * @param \PHP_CodeSniffer\Files\File $codeSnifferFile + * @return string[] + */ + public static function getAllNames(\PHP_CodeSniffer\Files\File $codeSnifferFile): array + { + $previousConstantPointer = 0; + + return array_map( + function (int $constantPointer) use ($codeSnifferFile): string { + return self::getName($codeSnifferFile, $constantPointer); + }, + array_filter( + iterator_to_array(self::getAllConstantPointers($codeSnifferFile, $previousConstantPointer)), + function (int $constantPointer) use ($codeSnifferFile): bool { + foreach (array_reverse($codeSnifferFile->getTokens()[$constantPointer]['conditions']) as $conditionTokenCode) { + if (in_array($conditionTokenCode, [T_CLASS, T_INTERFACE, T_TRAIT, T_ANON_CLASS], true)) { + return false; + } + } + + return true; + } + ) + ); + } + + private static function getAllConstantPointers(\PHP_CodeSniffer\Files\File $codeSnifferFile, int &$previousConstantPointer): \Generator + { + do { + $nextConstantPointer = TokenHelper::findNext($codeSnifferFile, T_CONST, $previousConstantPointer + 1); + if ($nextConstantPointer !== null) { + $previousConstantPointer = $nextConstantPointer; + yield $nextConstantPointer; + } + } while ($nextConstantPointer !== null); + } + +} diff --git a/SlevomatCodingStandard/Helpers/FunctionHelper.php b/SlevomatCodingStandard/Helpers/FunctionHelper.php index 976a0fc14..883d33601 100644 --- a/SlevomatCodingStandard/Helpers/FunctionHelper.php +++ b/SlevomatCodingStandard/Helpers/FunctionHelper.php @@ -218,4 +218,36 @@ public static function findReturnAnnotation(\PHP_CodeSniffer\Files\File $codeSni return $returnAnnotations[0]; } + /** + * @param \PHP_CodeSniffer\Files\File $codeSnifferFile + * @return string[] + */ + public static function getAllFunctionNames(\PHP_CodeSniffer\Files\File $codeSnifferFile): array + { + $previousFunctionPointer = 0; + + return array_map( + function (int $functionPointer) use ($codeSnifferFile): string { + return self::getName($codeSnifferFile, $functionPointer); + }, + array_filter( + iterator_to_array(self::getAllFunctionOrMethodPointers($codeSnifferFile, $previousFunctionPointer)), + function (int $functionOrMethodPointer) use ($codeSnifferFile): bool { + return !self::isMethod($codeSnifferFile, $functionOrMethodPointer); + } + ) + ); + } + + private static function getAllFunctionOrMethodPointers(\PHP_CodeSniffer\Files\File $codeSnifferFile, int &$previousFunctionPointer): \Generator + { + do { + $nextFunctionPointer = TokenHelper::findNext($codeSnifferFile, T_FUNCTION, $previousFunctionPointer + 1); + if ($nextFunctionPointer !== null) { + $previousFunctionPointer = $nextFunctionPointer; + yield $nextFunctionPointer; + } + } while ($nextFunctionPointer !== null); + } + } diff --git a/SlevomatCodingStandard/Helpers/ReferencedName.php b/SlevomatCodingStandard/Helpers/ReferencedName.php index ce0595cc5..077030902 100644 --- a/SlevomatCodingStandard/Helpers/ReferencedName.php +++ b/SlevomatCodingStandard/Helpers/ReferencedName.php @@ -49,6 +49,11 @@ public function getEndPointer(): int return $this->endPointer; } + public function isClass(): bool + { + return $this->type === self::TYPE_DEFAULT; + } + public function isConstant(): bool { return $this->type === self::TYPE_CONSTANT; diff --git a/SlevomatCodingStandard/Sniffs/Namespaces/ReferenceUsedNamesOnlySniff.php b/SlevomatCodingStandard/Sniffs/Namespaces/ReferenceUsedNamesOnlySniff.php index 8d11811dd..dd6a85522 100644 --- a/SlevomatCodingStandard/Sniffs/Namespaces/ReferenceUsedNamesOnlySniff.php +++ b/SlevomatCodingStandard/Sniffs/Namespaces/ReferenceUsedNamesOnlySniff.php @@ -3,6 +3,8 @@ namespace SlevomatCodingStandard\Sniffs\Namespaces; use SlevomatCodingStandard\Helpers\ClassHelper; +use SlevomatCodingStandard\Helpers\ConstantHelper; +use SlevomatCodingStandard\Helpers\FunctionHelper; use SlevomatCodingStandard\Helpers\NamespaceHelper; use SlevomatCodingStandard\Helpers\ReferencedName; use SlevomatCodingStandard\Helpers\ReferencedNameHelper; @@ -19,6 +21,8 @@ class ReferenceUsedNamesOnlySniff implements \PHP_CodeSniffer\Sniffs\Sniff public const CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME_WITHOUT_NAMESPACE = 'ReferenceViaFullyQualifiedNameWithoutNamespace'; + public const CODE_REFERENCE_VIA_FALLBACK_GLOBAL_NAME = 'ReferenceViaFallbackGlobalName'; + public const CODE_PARTIAL_USE = 'PartialUse'; /** @var string[] */ @@ -36,9 +40,15 @@ class ReferenceUsedNamesOnlySniff implements \PHP_CodeSniffer\Sniffs\Sniff /** @var bool */ public $allowFullyQualifiedGlobalFunctions = false; + /** @var bool */ + public $allowFallbackGlobalFunctions = true; + /** @var bool */ public $allowFullyQualifiedGlobalConstants = false; + /** @var bool */ + public $allowFallbackGlobalConstants = true; + /** @var string[] */ public $specialExceptionNames = []; @@ -67,6 +77,12 @@ class ReferenceUsedNamesOnlySniff implements \PHP_CodeSniffer\Sniffs\Sniff /** @var bool */ public $allowFullyQualifiedNameForCollidingClasses = false; + /** @var bool */ + public $allowFullyQualifiedNameForCollidingFunctions = false; + + /** @var bool */ + public $allowFullyQualifiedNameForCollidingConstants = false; + /** * @return mixed[] */ @@ -140,17 +156,51 @@ public function process(\PHP_CodeSniffer\Files\File $phpcsFile, $openTagPointer) $tokens = $phpcsFile->getTokens(); $referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer); + $useStatements = UseStatementHelper::getUseStatements($phpcsFile, $openTagPointer); + $definedClassesIndex = array_flip(array_map(function (string $className): string { return strtolower($className); }, ClassHelper::getAllNames($phpcsFile))); + $definedFunctionsIndex = array_flip(array_map(function (string $functionName): string { + return strtolower($functionName); + }, FunctionHelper::getAllFunctionNames($phpcsFile))); + $definedConstantsIndex = array_flip(ConstantHelper::getAllNames($phpcsFile)); if ($this->allowFullyQualifiedNameForCollidingClasses) { - $referencesIndex = array_flip( + $classReferencesIndex = array_flip( + array_map( + function (ReferencedName $referencedName): string { + return strtolower($referencedName->getNameAsReferencedInFile()); + }, + array_filter($referencedNames, function (ReferencedName $referencedName): bool { + return $referencedName->isClass(); + }) + ) + ); + } + + if ($this->allowFullyQualifiedNameForCollidingFunctions) { + $functionReferencesIndex = array_flip( array_map( function (ReferencedName $referencedName): string { return strtolower($referencedName->getNameAsReferencedInFile()); }, - $referencedNames + array_filter($referencedNames, function (ReferencedName $referencedName): bool { + return $referencedName->isFunction(); + }) + ) + ); + } + + if ($this->allowFullyQualifiedNameForCollidingConstants) { + $constantReferencesIndex = array_flip( + array_map( + function (ReferencedName $referencedName): string { + return $referencedName->getNameAsReferencedInFile(); + }, + array_filter($referencedNames, function (ReferencedName $referencedName): bool { + return $referencedName->isConstant(); + }) ) ); } @@ -159,16 +209,33 @@ function (ReferencedName $referencedName): string { $name = $referencedName->getNameAsReferencedInFile(); $nameStartPointer = $referencedName->getStartPointer(); $canonicalName = NamespaceHelper::normalizeToCanonicalName($name); - - if ($this->allowFullyQualifiedNameForCollidingClasses) { - $unqualifiedClassName = strtolower(NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name)); - if (isset($referencesIndex[$unqualifiedClassName]) || array_key_exists($unqualifiedClassName, $definedClassesIndex ?? [])) { + $unqualifiedName = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name); + + $isFullyQualified = NamespaceHelper::isFullyQualifiedName($name); + $isGlobalFallback = !$isFullyQualified + && !NamespaceHelper::hasNamespace($name) + && !array_key_exists(UseStatement::getUniqueId($referencedName->getType(), $name), $useStatements); + $isGlobalFunctionFallback = $referencedName->isFunction() && $isGlobalFallback; + $isGlobalConstantFallback = $referencedName->isConstant() && $isGlobalFallback; + + if ($referencedName->isClass() && $this->allowFullyQualifiedNameForCollidingClasses) { + $lowerCasedUnqualifiedClassName = strtolower($unqualifiedName); + if (isset($classReferencesIndex[$lowerCasedUnqualifiedClassName]) || array_key_exists($lowerCasedUnqualifiedClassName, $definedClassesIndex)) { + continue; + } + } elseif ($referencedName->isFunction() && $this->allowFullyQualifiedNameForCollidingFunctions) { + $lowerCasedUnqualifiedFunctionName = strtolower($unqualifiedName); + if (isset($functionReferencesIndex[$lowerCasedUnqualifiedFunctionName]) || array_key_exists($lowerCasedUnqualifiedFunctionName, $definedFunctionsIndex)) { + continue; + } + } elseif ($referencedName->isConstant() && $this->allowFullyQualifiedNameForCollidingConstants) { + if (isset($constantReferencesIndex[$unqualifiedName]) || array_key_exists($unqualifiedName, $definedConstantsIndex)) { continue; } } - if (NamespaceHelper::isFullyQualifiedName($name)) { - if (!$this->isClassRequiredToBeUsed($name)) { + if ($isFullyQualified || $isGlobalFunctionFallback || $isGlobalConstantFallback) { + if ($isFullyQualified && !$this->isRequiredToBeUsed($name)) { continue; } @@ -185,12 +252,15 @@ function (ReferencedName $referencedName): string { $previousKeywordPointer = TokenHelper::findPreviousExcluding($phpcsFile, array_merge(TokenHelper::$nameTokenCodes, [T_WHITESPACE, T_COMMA]), $nameStartPointer - 1); if (!in_array($tokens[$previousKeywordPointer]['code'], $this->getFullyQualifiedKeywords(), true)) { if ( - !NamespaceHelper::hasNamespace($name) + $isFullyQualified + && !NamespaceHelper::hasNamespace($name) && NamespaceHelper::findCurrentNamespaceName($phpcsFile, $nameStartPointer) === null ) { + $label = sprintf($referencedName->isConstant() ? 'Constant %s' : ($referencedName->isFunction() ? 'Function %s()' : 'Class %s'), $name); + $fix = $phpcsFile->addFixableError(sprintf( - 'Type %s should not be referenced via a fully qualified name, but via an unqualified name without the leading \\, because the file does not have a namespace and the type cannot be put in a use statement.', - $name + '%s should not be referenced via a fully qualified name, but via an unqualified name without the leading \\, because the file does not have a namespace and the type cannot be put in a use statement.', + $label ), $nameStartPointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME_WITHOUT_NAMESPACE); if ($fix) { $phpcsFile->fixer->beginChangeset(); @@ -201,9 +271,9 @@ function (ReferencedName $referencedName): string { $shouldBeUsed = NamespaceHelper::hasNamespace($name); if (!$shouldBeUsed) { if ($referencedName->isFunction()) { - $shouldBeUsed = !$this->allowFullyQualifiedGlobalFunctions; + $shouldBeUsed = $isFullyQualified ? !$this->allowFullyQualifiedGlobalFunctions : !$this->allowFallbackGlobalFunctions; } elseif ($referencedName->isConstant()) { - $shouldBeUsed = !$this->allowFullyQualifiedGlobalConstants; + $shouldBeUsed = $isFullyQualified ? !$this->allowFullyQualifiedGlobalConstants : !$this->allowFallbackGlobalConstants; } else { $shouldBeUsed = !$this->allowFullyQualifiedGlobalClasses; } @@ -213,27 +283,41 @@ function (ReferencedName $referencedName): string { continue; } - $useStatements = UseStatementHelper::getUseStatements($phpcsFile, $openTagPointer); $nameToReference = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name); - $canonicalNameToReference = strtolower($nameToReference); + $canonicalNameToReference = $referencedName->isConstant() ? $nameToReference : strtolower($nameToReference); $canBeFixed = true; foreach ($useStatements as $useStatement) { + if ($useStatement->getType() !== $referencedName->getType()) { + continue; + } + + if ($useStatement->getFullyQualifiedTypeName() === $canonicalName) { + continue; + } + if ( - $useStatement->getType() === $referencedName->getType() - && $useStatement->getFullyQualifiedTypeName() !== $canonicalName - && ($useStatement->getCanonicalNameAsReferencedInFile() === $canonicalNameToReference || array_key_exists($canonicalNameToReference, $definedClassesIndex)) + $useStatement->getCanonicalNameAsReferencedInFile() === $canonicalNameToReference + || ($referencedName->isClass() && array_key_exists($canonicalNameToReference, $definedClassesIndex)) + || ($referencedName->isFunction() && array_key_exists($canonicalNameToReference, $definedFunctionsIndex)) + || ($referencedName->isConstant() && array_key_exists($canonicalNameToReference, $definedConstantsIndex)) ) { $canBeFixed = false; break; } } - $errorMessage = sprintf('Type %s should not be referenced via a fully qualified name, but via a use statement.', $name); + $label = sprintf($referencedName->isConstant() ? 'Constant %s' : ($referencedName->isFunction() ? 'Function %s()' : 'Class %s'), $name); + $errorCode = $isGlobalConstantFallback || $isGlobalFunctionFallback + ? self::CODE_REFERENCE_VIA_FALLBACK_GLOBAL_NAME + : self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME; + $errorMessage = $isGlobalConstantFallback || $isGlobalFunctionFallback + ? sprintf('%s should not be referenced via a fallback global name, but via a use statement.', $label) + : sprintf('%s should not be referenced via a fully qualified name, but via a use statement.', $label); if ($canBeFixed) { - $fix = $phpcsFile->addFixableError($errorMessage, $nameStartPointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME); + $fix = $phpcsFile->addFixableError($errorMessage, $nameStartPointer, $errorCode); } else { - $phpcsFile->addError($errorMessage, $nameStartPointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME); + $phpcsFile->addError($errorMessage, $nameStartPointer, $errorCode); $fix = false; } @@ -286,7 +370,7 @@ function (ReferencedName $referencedName): string { } } - private function isClassRequiredToBeUsed(string $name): bool + private function isRequiredToBeUsed(string $name): bool { if (count($this->namespacesRequiredToUse) === 0) { return true; diff --git a/build/phpcs.xml b/build/phpcs.xml index f64c3acb8..addd5760a 100644 --- a/build/phpcs.xml +++ b/build/phpcs.xml @@ -49,6 +49,8 @@ + + diff --git a/tests/Helpers/ConstantHelperTest.php b/tests/Helpers/ConstantHelperTest.php new file mode 100644 index 000000000..fe2011635 --- /dev/null +++ b/tests/Helpers/ConstantHelperTest.php @@ -0,0 +1,32 @@ +getCodeSnifferFile(__DIR__ . '/data/constantWithNamespace.php'); + + $constantPointer = $this->findConstantPointerByName($codeSnifferFile, 'FOO'); + $this->assertSame('\FooNamespace\FOO', ConstantHelper::getFullyQualifiedName($codeSnifferFile, $constantPointer)); + $this->assertSame('FOO', ConstantHelper::getName($codeSnifferFile, $constantPointer)); + } + + public function testNameWithoutNamespace(): void + { + $codeSnifferFile = $this->getCodeSnifferFile(__DIR__ . '/data/constantWithoutNamespace.php'); + + $constantPointer = $this->findConstantPointerByName($codeSnifferFile, 'FOO'); + $this->assertSame('FOO', ConstantHelper::getFullyQualifiedName($codeSnifferFile, $constantPointer)); + $this->assertSame('FOO', ConstantHelper::getName($codeSnifferFile, $constantPointer)); + } + + public function testGetAllNames(): void + { + $codeSnifferFile = $this->getCodeSnifferFile(__DIR__ . '/data/constantNames.php'); + $this->assertSame(['FOO', 'BOO'], ConstantHelper::getAllNames($codeSnifferFile)); + } + +} diff --git a/tests/Helpers/FunctionHelperTest.php b/tests/Helpers/FunctionHelperTest.php index 40a844691..ce547f94b 100644 --- a/tests/Helpers/FunctionHelperTest.php +++ b/tests/Helpers/FunctionHelperTest.php @@ -367,4 +367,10 @@ public function testAnnotations(): void $this->assertNull(FunctionHelper::findReturnAnnotation($codeSnifferFile, $functionPointer)); } + public function testGetAllFunctionNames(): void + { + $codeSnifferFile = $this->getCodeSnifferFile(__DIR__ . '/data/functionNames.php'); + $this->assertSame(['foo', 'boo'], FunctionHelper::getAllFunctionNames($codeSnifferFile)); + } + } diff --git a/tests/Helpers/data/constantNames.php b/tests/Helpers/data/constantNames.php new file mode 100644 index 000000000..d629793ed --- /dev/null +++ b/tests/Helpers/data/constantNames.php @@ -0,0 +1,14 @@ +checkFile(__DIR__ . '/data/shouldBeInUseStatement.php', [ + 'allowFallbackGlobalFunctions' => false, + ]); + $this->assertSniffError( + $report, + 18, + ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FALLBACK_GLOBAL_NAME, + 'min' + ); + } + + public function testReferencingGlobalConstantViaFallback(): void + { + $report = $this->checkFile(__DIR__ . '/data/shouldBeInUseStatement.php', [ + 'allowFallbackGlobalConstants' => false, + ]); + $this->assertSniffError( + $report, + 19, + ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FALLBACK_GLOBAL_NAME, + 'PHP_VERSION' + ); + } + /** * @dataProvider dataIgnoredNamesForIrrelevantTests * @param string[] $ignoredNames @@ -601,12 +627,14 @@ public function testAllowingFullyQualifiedGlobalConstants(): void $this->assertNoSniffErrorInFile($report); } - public function testFixableReferenceViaFullyQualifiedName(): void + public function testFixableReferenceViaFullyQualifiedOrGlobalFallbackName(): void { $report = $this->checkFile(__DIR__ . '/data/fixableReferenceViaFullyQualifiedName.php', [ 'fullyQualifiedKeywords' => ['T_EXTENDS'], 'allowFullyQualifiedExceptions' => true, - ], [ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME]); + 'allowFallbackGlobalFunctions' => false, + 'allowFallbackGlobalConstants' => false, + ], [ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FALLBACK_GLOBAL_NAME]); $this->assertAllFixedInFile($report); } @@ -707,4 +735,44 @@ public function testCollidingClassNameExtendsDisabled(): void $this->assertSniffError($report, 5, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME); } + public function testCollidingFullyQualifiedFunctionNameAllowed(): void + { + $report = $this->checkFile( + __DIR__ . '/data/collidingFullyQualifiedFunctionNames.php', + ['allowFullyQualifiedNameForCollidingFunctions' => true] + ); + $this->assertNoSniffErrorInFile($report); + } + + public function testCollidingFullyQualifiedFunctionNameDisallowed(): void + { + $report = $this->checkFile( + __DIR__ . '/data/collidingFullyQualifiedFunctionNames.php', + ['allowFullyQualifiedNameForCollidingFunctions' => false] + ); + + $this->assertSame(1, $report->getErrorCount()); + $this->assertSniffError($report, 15, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME); + } + + public function testCollidingFullyQualifiedConstantNameAllowed(): void + { + $report = $this->checkFile( + __DIR__ . '/data/collidingFullyQualifiedConstantNames.php', + ['allowFullyQualifiedNameForCollidingConstants' => true] + ); + $this->assertNoSniffErrorInFile($report); + } + + public function testCollidingFullyQualifiedConstantNameDisallowed(): void + { + $report = $this->checkFile( + __DIR__ . '/data/collidingFullyQualifiedConstantNames.php', + ['allowFullyQualifiedNameForCollidingConstants' => false] + ); + + $this->assertSame(1, $report->getErrorCount()); + $this->assertSniffError($report, 12, ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME); + } + } diff --git a/tests/Sniffs/Namespaces/data/collidingFullyQualifiedConstantNames.php b/tests/Sniffs/Namespaces/data/collidingFullyQualifiedConstantNames.php new file mode 100644 index 000000000..c002c15db --- /dev/null +++ b/tests/Sniffs/Namespaces/data/collidingFullyQualifiedConstantNames.php @@ -0,0 +1,15 @@ +