From 800701e5c271b10f39786335b7d68f75eb4fb49c Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 16 Feb 2020 09:27:36 +0100 Subject: [PATCH] DisallowUseClass/Function/Const: make the error codes more modular. This adds four new error codes to each of the sniffs: - `FoundSameNamespace`, `FoundSameNamespaceWithAlias` for `use` statements importing from the same namespace; - `FoundGlobalNamespace`, `FoundGlobalNamespaceWithAlias` for `use` statements importing from the global namespace, like import statements for PHP native classes, functions and constants. In all other circumstances, the existing error codes `FoundWithAlias` and `FoundWithoutAlias` will continue to be used. This commit also contains three bug fixes applied to each of these sniffs: * In PHP, both namespace names, as well as aliases, are case-insensitive, so any comparison of them should be done in a case-insensitive manner. * An import from the global namespace would previously always be seen as non-aliased, even when it was aliased. * Tolerance for `use` import statements with leading backslashes. Includes: * Additional metrics about the import source: different namespace / same namespace / global namespace. * Additional unit tests. --- .../UseStatements/DisallowUseClassSniff.php | 132 ++++++++++++++++-- .../UseStatements/DisallowUseConstSniff.php | 132 ++++++++++++++++-- .../DisallowUseFunctionSniff.php | 132 ++++++++++++++++-- ...est.inc => DisallowUseClassUnitTest.1.inc} | 9 +- .../DisallowUseClassUnitTest.2.inc | 43 ++++++ .../DisallowUseClassUnitTest.3.inc | 68 +++++++++ .../DisallowUseClassUnitTest.4.inc | 6 + .../DisallowUseClassUnitTest.php | 93 ++++++++++-- ...est.inc => DisallowUseConstUnitTest.1.inc} | 7 + .../DisallowUseConstUnitTest.2.inc | 40 ++++++ .../DisallowUseConstUnitTest.3.inc | 67 +++++++++ .../DisallowUseConstUnitTest.4.inc | 6 + .../DisallowUseConstUnitTest.php | 84 +++++++++-- ....inc => DisallowUseFunctionUnitTest.1.inc} | 8 ++ .../DisallowUseFunctionUnitTest.2.inc | 43 ++++++ .../DisallowUseFunctionUnitTest.3.inc | 69 +++++++++ .../DisallowUseFunctionUnitTest.4.inc | 6 + .../DisallowUseFunctionUnitTest.php | 92 ++++++++++-- 18 files changed, 957 insertions(+), 80 deletions(-) rename Universal/Tests/UseStatements/{DisallowUseClassUnitTest.inc => DisallowUseClassUnitTest.1.inc} (61%) create mode 100644 Universal/Tests/UseStatements/DisallowUseClassUnitTest.2.inc create mode 100644 Universal/Tests/UseStatements/DisallowUseClassUnitTest.3.inc create mode 100644 Universal/Tests/UseStatements/DisallowUseClassUnitTest.4.inc rename Universal/Tests/UseStatements/{DisallowUseConstUnitTest.inc => DisallowUseConstUnitTest.1.inc} (62%) create mode 100644 Universal/Tests/UseStatements/DisallowUseConstUnitTest.2.inc create mode 100644 Universal/Tests/UseStatements/DisallowUseConstUnitTest.3.inc create mode 100644 Universal/Tests/UseStatements/DisallowUseConstUnitTest.4.inc rename Universal/Tests/UseStatements/{DisallowUseFunctionUnitTest.inc => DisallowUseFunctionUnitTest.1.inc} (62%) create mode 100644 Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.2.inc create mode 100644 Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.3.inc create mode 100644 Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.4.inc diff --git a/Universal/Sniffs/UseStatements/DisallowUseClassSniff.php b/Universal/Sniffs/UseStatements/DisallowUseClassSniff.php index 4ada653..c767d30 100644 --- a/Universal/Sniffs/UseStatements/DisallowUseClassSniff.php +++ b/Universal/Sniffs/UseStatements/DisallowUseClassSniff.php @@ -14,6 +14,7 @@ use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Util\Tokens; +use PHPCSUtils\Utils\Namespaces; use PHPCSUtils\Utils\UseStatements; /** @@ -28,6 +29,42 @@ class DisallowUseClassSniff implements Sniff { + /** + * Name of the "Use import source" metric. + * + * @since 1.0.0 + * + * @var string + */ + const METRIC_NAME_SRC = 'Use import statement source for class/interface/trait'; + + /** + * Name of the "Use import with/without alias" metric. + * + * @since 1.0.0 + * + * @var string + */ + const METRIC_NAME_ALIAS = 'Use import statement for class/interface/trait'; + + /** + * Keep track of which file is being scanned. + * + * @since 1.0.0 + * + * @var string + */ + private $currentFile = ''; + + /** + * Keep track of the current namespace. + * + * @since 1.0.0 + * + * @var string + */ + private $currentNamespace = ''; + /** * Returns an array of tokens this test wants to listen for. * @@ -37,7 +74,10 @@ class DisallowUseClassSniff implements Sniff */ public function register() { - return [\T_USE]; + return [ + \T_USE, + \T_NAMESPACE, + ]; } /** @@ -53,6 +93,26 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { + $file = $phpcsFile->getFilename(); + if ($file !== $this->currentFile) { + // Reset the current namespace for each new file. + $this->currentFile = $file; + $this->currentNamespace = ''; + } + + $tokens = $phpcsFile->getTokens(); + + // Get the name of the current namespace. + if ($tokens[$stackPtr]['code'] === \T_NAMESPACE) { + $namespaceName = Namespaces::getDeclaredName($phpcsFile, $stackPtr); + if ($namespaceName !== false) { + $this->currentNamespace = $namespaceName; + } + + return; + } + + // Ok, so this is a T_USE token. try { $statements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); } catch (RuntimeException $e) { @@ -65,7 +125,6 @@ public function process(File $phpcsFile, $stackPtr) return; } - $tokens = $phpcsFile->getTokens(); $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); foreach ($statements['name'] as $alias => $fullName) { @@ -79,29 +138,74 @@ public function process(File $phpcsFile, $stackPtr) $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($reportPtr + 1), $endOfStatement, true); if ($next !== false && $tokens[$next]['code'] === \T_NS_SEPARATOR) { - // Namespace level with same name. Continue searching + // Namespace level with same name. Continue searching. continue; } break; } while (true); - $error = 'Use import statements for classes/traits/interfaces are not allowed.'; - $error .= ' Found import statement for: "%s"'; - $data = [$fullName, $alias]; + /* + * Build the error message and code. + * + * Check whether this is a non-namespaced (global) import and check whether this is an + * import from within the same namespace. + * + * Takes incorrect use statements with leading backslash into account. + * Takes case-INsensitivity of namespaces names into account. + * + * The "GlobalNamespace" error code takes precedence over the "SameNamespace" error code + * in case this is a non-namespaced file. + */ + + $error = 'Use import statements for class/interface/trait%s are not allowed.'; + $error .= ' Found import statement for: "%s"'; + $errorCode = 'Found'; + $data = [ + '', + $fullName, + ]; + + $globalNamespace = false; + $sameNamespace = false; + if (\strpos($fullName, '\\', 1) === false) { + $globalNamespace = true; + $errorCode = 'FromGlobalNamespace'; + $data[0] = ' from the global namespace'; - $offsetFromEnd = (\strlen($alias) + 1); - if (\substr($fullName, -$offsetFromEnd) === '\\' . $alias) { - $phpcsFile->recordMetric($reportPtr, 'Use import statement for class/interface/trait', 'without alias'); + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'global namespace'); + } elseif ($this->currentNamespace !== '' + && (\stripos($fullName, $this->currentNamespace . '\\') === 0 + || \stripos($fullName, '\\' . $this->currentNamespace . '\\') === 0) + ) { + $sameNamespace = true; + $errorCode = 'FromSameNamespace'; + $data[0] = ' from the same namespace'; - $phpcsFile->addError($error, $reportPtr, 'FoundWithoutAlias', $data); - continue; + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'same namespace'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'different namespace'); } - $phpcsFile->recordMetric($reportPtr, 'Use import statement for class/interface/trait', 'with alias'); + $hasAlias = false; + $lastLeaf = \strtolower(\substr($fullName, -(\strlen($alias) + 1))); + $aliasLC = \strtolower($alias); + if ($lastLeaf !== $aliasLC && $lastLeaf !== '\\' . $aliasLC) { + $hasAlias = true; + $error .= ' with alias: "%s"'; + $errorCode .= 'WithAlias'; + $data[] = $alias; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'with alias'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'without alias'); + } + + if ($errorCode === 'Found') { + $errorCode = 'FoundWithoutAlias'; + } - $error .= ' with alias: "%s"'; - $phpcsFile->addError($error, $reportPtr, 'FoundWithAlias', $data); + $phpcsFile->addError($error, $reportPtr, $errorCode, $data); } } } diff --git a/Universal/Sniffs/UseStatements/DisallowUseConstSniff.php b/Universal/Sniffs/UseStatements/DisallowUseConstSniff.php index 77a4a32..8584b30 100644 --- a/Universal/Sniffs/UseStatements/DisallowUseConstSniff.php +++ b/Universal/Sniffs/UseStatements/DisallowUseConstSniff.php @@ -14,6 +14,7 @@ use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Util\Tokens; +use PHPCSUtils\Utils\Namespaces; use PHPCSUtils\Utils\UseStatements; /** @@ -28,6 +29,42 @@ class DisallowUseConstSniff implements Sniff { + /** + * Name of the "Use import source" metric. + * + * @since 1.0.0 + * + * @var string + */ + const METRIC_NAME_SRC = 'Use import statement source for constant'; + + /** + * Name of the "Use import with/without alias" metric. + * + * @since 1.0.0 + * + * @var string + */ + const METRIC_NAME_ALIAS = 'Use import statement for constant'; + + /** + * Keep track of which file is being scanned. + * + * @since 1.0.0 + * + * @var string + */ + private $currentFile = ''; + + /** + * Keep track of the current namespace. + * + * @since 1.0.0 + * + * @var string + */ + private $currentNamespace = ''; + /** * Returns an array of tokens this test wants to listen for. * @@ -37,7 +74,10 @@ class DisallowUseConstSniff implements Sniff */ public function register() { - return [\T_USE]; + return [ + \T_USE, + \T_NAMESPACE, + ]; } /** @@ -53,6 +93,26 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { + $file = $phpcsFile->getFilename(); + if ($file !== $this->currentFile) { + // Reset the current namespace for each new file. + $this->currentFile = $file; + $this->currentNamespace = ''; + } + + $tokens = $phpcsFile->getTokens(); + + // Get the name of the current namespace. + if ($tokens[$stackPtr]['code'] === \T_NAMESPACE) { + $namespaceName = Namespaces::getDeclaredName($phpcsFile, $stackPtr); + if ($namespaceName !== false) { + $this->currentNamespace = $namespaceName; + } + + return; + } + + // Ok, so this is a T_USE token. try { $statements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); } catch (RuntimeException $e) { @@ -65,7 +125,6 @@ public function process(File $phpcsFile, $stackPtr) return; } - $tokens = $phpcsFile->getTokens(); $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); foreach ($statements['const'] as $alias => $fullName) { @@ -79,29 +138,74 @@ public function process(File $phpcsFile, $stackPtr) $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($reportPtr + 1), $endOfStatement, true); if ($next !== false && $tokens[$next]['code'] === \T_NS_SEPARATOR) { - // Namespace level with same name. Continue searching + // Namespace level with same name. Continue searching. continue; } break; } while (true); - $error = 'Use import statements for constants are not allowed.'; - $error .= ' Found import statement for: "%s"'; - $data = [$fullName, $alias]; + /* + * Build the error message and code. + * + * Check whether this is a non-namespaced (global) import and check whether this is an + * import from within the same namespace. + * + * Takes incorrect use statements with leading backslash into account. + * Takes case-INsensitivity of namespaces names into account. + * + * The "GlobalNamespace" error code takes precedence over the "SameNamespace" error code + * in case this is a non-namespaced file. + */ + + $error = 'Use import statements for constants%s are not allowed.'; + $error .= ' Found import statement for: "%s"'; + $errorCode = 'Found'; + $data = [ + '', + $fullName, + ]; + + $globalNamespace = false; + $sameNamespace = false; + if (\strpos($fullName, '\\', 1) === false) { + $globalNamespace = true; + $errorCode = 'FromGlobalNamespace'; + $data[0] = ' from the global namespace'; - $offsetFromEnd = (\strlen($alias) + 1); - if (\substr($fullName, -$offsetFromEnd) === '\\' . $alias) { - $phpcsFile->recordMetric($reportPtr, 'Use import statement for constant', 'without alias'); + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'global namespace'); + } elseif ($this->currentNamespace !== '' + && (\stripos($fullName, $this->currentNamespace . '\\') === 0 + || \stripos($fullName, '\\' . $this->currentNamespace . '\\') === 0) + ) { + $sameNamespace = true; + $errorCode = 'FromSameNamespace'; + $data[0] = ' from the same namespace'; - $phpcsFile->addError($error, $reportPtr, 'FoundWithoutAlias', $data); - continue; + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'same namespace'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'different namespace'); } - $phpcsFile->recordMetric($reportPtr, 'Use import statement for constant', 'with alias'); + $hasAlias = false; + $lastLeaf = \strtolower(\substr($fullName, -(\strlen($alias) + 1))); + $aliasLC = \strtolower($alias); + if ($lastLeaf !== $aliasLC && $lastLeaf !== '\\' . $aliasLC) { + $hasAlias = true; + $error .= ' with alias: "%s"'; + $errorCode .= 'WithAlias'; + $data[] = $alias; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'with alias'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'without alias'); + } + + if ($errorCode === 'Found') { + $errorCode = 'FoundWithoutAlias'; + } - $error .= ' with alias: "%s"'; - $phpcsFile->addError($error, $reportPtr, 'FoundWithAlias', $data); + $phpcsFile->addError($error, $reportPtr, $errorCode, $data); } } } diff --git a/Universal/Sniffs/UseStatements/DisallowUseFunctionSniff.php b/Universal/Sniffs/UseStatements/DisallowUseFunctionSniff.php index 0b5d7ea..dc27096 100644 --- a/Universal/Sniffs/UseStatements/DisallowUseFunctionSniff.php +++ b/Universal/Sniffs/UseStatements/DisallowUseFunctionSniff.php @@ -14,6 +14,7 @@ use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Util\Tokens; +use PHPCSUtils\Utils\Namespaces; use PHPCSUtils\Utils\UseStatements; /** @@ -28,6 +29,42 @@ class DisallowUseFunctionSniff implements Sniff { + /** + * Name of the "Use import source" metric. + * + * @since 1.0.0 + * + * @var string + */ + const METRIC_NAME_SRC = 'Use import statement source for functions'; + + /** + * Name of the "Use import with/without alias" metric. + * + * @since 1.0.0 + * + * @var string + */ + const METRIC_NAME_ALIAS = 'Use import statement for functions'; + + /** + * Keep track of which file is being scanned. + * + * @since 1.0.0 + * + * @var string + */ + private $currentFile = ''; + + /** + * Keep track of the current namespace. + * + * @since 1.0.0 + * + * @var string + */ + private $currentNamespace = ''; + /** * Returns an array of tokens this test wants to listen for. * @@ -37,7 +74,10 @@ class DisallowUseFunctionSniff implements Sniff */ public function register() { - return [\T_USE]; + return [ + \T_USE, + \T_NAMESPACE, + ]; } /** @@ -53,6 +93,26 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { + $file = $phpcsFile->getFilename(); + if ($file !== $this->currentFile) { + // Reset the current namespace for each new file. + $this->currentFile = $file; + $this->currentNamespace = ''; + } + + $tokens = $phpcsFile->getTokens(); + + // Get the name of the current namespace. + if ($tokens[$stackPtr]['code'] === \T_NAMESPACE) { + $namespaceName = Namespaces::getDeclaredName($phpcsFile, $stackPtr); + if ($namespaceName !== false) { + $this->currentNamespace = $namespaceName; + } + + return; + } + + // Ok, so this is a T_USE token. try { $statements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr); } catch (RuntimeException $e) { @@ -65,7 +125,6 @@ public function process(File $phpcsFile, $stackPtr) return; } - $tokens = $phpcsFile->getTokens(); $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1)); foreach ($statements['function'] as $alias => $fullName) { @@ -79,29 +138,74 @@ public function process(File $phpcsFile, $stackPtr) $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($reportPtr + 1), $endOfStatement, true); if ($next !== false && $tokens[$next]['code'] === \T_NS_SEPARATOR) { - // Namespace level with same name. Continue searching + // Namespace level with same name. Continue searching. continue; } break; } while (true); - $error = 'Use import statements for functions are not allowed.'; - $error .= ' Found import statement for: "%s"'; - $data = [$fullName, $alias]; + /* + * Build the error message and code. + * + * Check whether this is a non-namespaced (global) import and check whether this is an + * import from within the same namespace. + * + * Takes incorrect use statements with leading backslash into account. + * Takes case-INsensitivity of namespaces names into account. + * + * The "GlobalNamespace" error code takes precedence over the "SameNamespace" error code + * in case this is a non-namespaced file. + */ + + $error = 'Use import statements for functions%s are not allowed.'; + $error .= ' Found import statement for: "%s"'; + $errorCode = 'Found'; + $data = [ + '', + $fullName, + ]; + + $globalNamespace = false; + $sameNamespace = false; + if (\strpos($fullName, '\\', 1) === false) { + $globalNamespace = true; + $errorCode = 'FromGlobalNamespace'; + $data[0] = ' from the global namespace'; - $offsetFromEnd = (\strlen($alias) + 1); - if (\substr($fullName, -$offsetFromEnd) === '\\' . $alias) { - $phpcsFile->recordMetric($reportPtr, 'Use import statement for functions', 'without alias'); + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'global namespace'); + } elseif ($this->currentNamespace !== '' + && (\stripos($fullName, $this->currentNamespace . '\\') === 0 + || \stripos($fullName, '\\' . $this->currentNamespace . '\\') === 0) + ) { + $sameNamespace = true; + $errorCode = 'FromSameNamespace'; + $data[0] = ' from the same namespace'; - $phpcsFile->addError($error, $reportPtr, 'FoundWithoutAlias', $data); - continue; + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'same namespace'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_SRC, 'different namespace'); } - $phpcsFile->recordMetric($reportPtr, 'Use import statement for functions', 'with alias'); + $hasAlias = false; + $lastLeaf = \strtolower(\substr($fullName, -(\strlen($alias) + 1))); + $aliasLC = \strtolower($alias); + if ($lastLeaf !== $aliasLC && $lastLeaf !== '\\' . $aliasLC) { + $hasAlias = true; + $error .= ' with alias: "%s"'; + $errorCode .= 'WithAlias'; + $data[] = $alias; + + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'with alias'); + } else { + $phpcsFile->recordMetric($reportPtr, self::METRIC_NAME_ALIAS, 'without alias'); + } + + if ($errorCode === 'Found') { + $errorCode = 'FoundWithoutAlias'; + } - $error .= ' with alias: "%s"'; - $phpcsFile->addError($error, $reportPtr, 'FoundWithAlias', $data); + $phpcsFile->addError($error, $reportPtr, $errorCode, $data); } } } diff --git a/Universal/Tests/UseStatements/DisallowUseClassUnitTest.inc b/Universal/Tests/UseStatements/DisallowUseClassUnitTest.1.inc similarity index 61% rename from Universal/Tests/UseStatements/DisallowUseClassUnitTest.inc rename to Universal/Tests/UseStatements/DisallowUseClassUnitTest.1.inc index bb94e3b..63ed088 100644 --- a/Universal/Tests/UseStatements/DisallowUseClassUnitTest.inc +++ b/Universal/Tests/UseStatements/DisallowUseClassUnitTest.1.inc @@ -10,7 +10,7 @@ use My\NS\SomeClass as OtherClass; use Vendor\Foo\ClassA as ClassABC, Vendor\Bar\InterfaceB, - Vendor\Bar\Bar, // Testing finding the correct line to report on. + Vendor\Bar\Bar, // Testing finding the correct line to report on. Vendor\Baz\ClassC; use some\namespacing\{ @@ -28,6 +28,13 @@ use Some\NS\{ AnotherLevel, // Error. }; +// Test handling of alias as part of the last leaf of the imported name, including case-insensitivity. +// While aliasing to itself doesn't make much sense, the sniff should handle it correctly. +use My\AwesomeClassA as classa; +use My\AwesomeClassB as AweSomeclassB; +use AwesomeClassC as someclassc; // Alias for global import. +use AwesomeClassD as AWESOMECLASSD; // Alias for global import to same name. + // Ignore as not import use. $closure = function() use($bar) { return $bar; diff --git a/Universal/Tests/UseStatements/DisallowUseClassUnitTest.2.inc b/Universal/Tests/UseStatements/DisallowUseClassUnitTest.2.inc new file mode 100644 index 0000000..1d25581 --- /dev/null +++ b/Universal/Tests/UseStatements/DisallowUseClassUnitTest.2.inc @@ -0,0 +1,43 @@ + => */ - public function getErrorList() + public function getErrorList($testFile = '') { - return [ - 8 => 1, - 9 => 1, - 11 => 1, - 12 => 1, - 13 => 1, - 14 => 1, - 17 => 1, - 18 => 1, - 19 => 1, - 24 => 1, - 28 => 1, - ]; + switch ($testFile) { + case 'DisallowUseClassUnitTest.1.inc': + return [ + 8 => 1, + 9 => 1, // WithAlias. + 11 => 1, // WithAlias. + 12 => 1, + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, + 19 => 1, // WithAlias. + 24 => 1, + 28 => 1, + 33 => 1, // WithAlias. + 34 => 1, + 35 => 1, // GlobalNamespaceWithAlias. + 36 => 1, // GlobalNamespace. Note: alias same as name, so not counted as aliased. + ]; + + case 'DisallowUseClassUnitTest.2.inc': + return [ + 9 => 1, // SameNamespace. + 10 => 1, // SameNamespaceWithAlias. + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, // WithAlias. + 19 => 1, + 22 => 1, // GlobalNamespace. + 31 => 1, + 32 => 1, // WithAlias. + 35 => 1, + 37 => 1, + 39 => 1, // SameNamespaceWithAlias. + 40 => 1, // SameNamespace. + 43 => 1, // GlobalNamespace. + ]; + + case 'DisallowUseClassUnitTest.3.inc': + return [ + 9 => 1, // SameNamespace. + 10 => 1, // SameNamespaceWithAlias. + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, // WithAlias. + 19 => 1, + 22 => 1, // GlobalNamespace. + 31 => 1, + 32 => 1, // WithAlias. + 35 => 1, + 37 => 1, // WithAlias. + 38 => 1, + 41 => 1, // GlobalNamespace. + 50 => 1, + 51 => 1, // WithAlias. + 54 => 1, + 56 => 1, + 58 => 1, // SameNamespaceWithAlias. + 59 => 1, // SameNamespace. + 62 => 1, // GlobalNamespace. + 68 => 1, // SameNamespace. Note: well, not really, but parse error. + ]; + + case 'DisallowUseClassUnitTest.4.inc': + return [ + 6 => 1, + ]; + + default: + return []; + } } /** diff --git a/Universal/Tests/UseStatements/DisallowUseConstUnitTest.inc b/Universal/Tests/UseStatements/DisallowUseConstUnitTest.1.inc similarity index 62% rename from Universal/Tests/UseStatements/DisallowUseConstUnitTest.inc rename to Universal/Tests/UseStatements/DisallowUseConstUnitTest.1.inc index f03f5b5..c47915e 100644 --- a/Universal/Tests/UseStatements/DisallowUseConstUnitTest.inc +++ b/Universal/Tests/UseStatements/DisallowUseConstUnitTest.1.inc @@ -25,6 +25,13 @@ use Some\NS\{ AnotherLevel, }; +// Test handling of alias as part of the last leaf of the imported name, including case-insensitivity. +// While aliasing to itself doesn't make much sense, the sniff should handle it correctly. +use const My\PHP_MINOR_VERSION as Minor_version; +use const My\PHP_MAJOR_VERSION as Php_Major_Version; +use const PHP_VERSION_ID as version_id; // Alias for global import. +use const PHP_VERSION as php_version; // Alias for global import to same name. + // Ignore as not import use. $closure = function() use($bar) { return $bar; diff --git a/Universal/Tests/UseStatements/DisallowUseConstUnitTest.2.inc b/Universal/Tests/UseStatements/DisallowUseConstUnitTest.2.inc new file mode 100644 index 0000000..80e4254 --- /dev/null +++ b/Universal/Tests/UseStatements/DisallowUseConstUnitTest.2.inc @@ -0,0 +1,40 @@ + => */ - public function getErrorList() + public function getErrorList($testFile = '') { - return [ - 8 => 1, - 9 => 1, - 11 => 1, - 12 => 1, - 15 => 1, - 16 => 1, - 23 => 1, - ]; + switch ($testFile) { + case 'DisallowUseConstUnitTest.1.inc': + return [ + 8 => 1, + 9 => 1, // WithAlias. + 11 => 1, + 12 => 1, // WithAlias. + 15 => 1, // WithAlias. + 16 => 1, + 23 => 1, // WithAlias. + 30 => 1, // WithAlias. + 31 => 1, // Note: alias same as name, so not counted as aliased. + 32 => 1, // GlobalNamespaceWithAlias. + 33 => 1, // GlobalNamespace. Note: alias same as name, so not counted as aliased. + ]; + + case 'DisallowUseConstUnitTest.2.inc': + return [ + 9 => 1, // SameNamespace. + 10 => 1, // SameNamespaceWithAlias. + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, // WithAlias. + 21 => 1, // GlobalNamespace. + 29 => 1, + 30 => 1, // WithAlias. + 33 => 1, + 36 => 1, // SameNamespace. + 37 => 1, // SameNamespaceWithAlias. + 40 => 1, // GlobalNamespace. + ]; + + case 'DisallowUseConstUnitTest.3.inc': + return [ + 9 => 1, // SameNamespace. + 10 => 1, // SameNamespaceWithAlias. + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, // WithAlias. + 21 => 1, // GlobalNamespace. + + 30 => 1, + 31 => 1, // WithAlias. + 34 => 1, + 36 => 1, + 37 => 1, // WithAlias. + 38 => 1, + 41 => 1, // GlobalNamespace. + + 50 => 1, + 51 => 1, // WithAlias. + 54 => 1, + 57 => 1, // SameNamespace. + 58 => 1, // SameNamespaceWithAlias. + 61 => 1, // GlobalNamespace. + 67 => 1, // SameNamespace. Note: well, not really, but parse error. + ]; + + case 'DisallowUseConstUnitTest.4.inc': + return [ + 6 => 1, + ]; + + default: + return []; + } } /** diff --git a/Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.inc b/Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.1.inc similarity index 62% rename from Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.inc rename to Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.1.inc index 9518677..05e8032 100644 --- a/Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.inc +++ b/Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.1.inc @@ -27,6 +27,14 @@ use Some\NS\{ AnotherLevel, }; +// Test handling of alias as part of the last leaf of the imported name, including case-insensitivity +// and tolerance for leading backslashes. +// While aliasing to itself doesn't make much sense, the sniff should handle it correctly. +use function My\preg_match as Match; +use function My\preg_replace as Preg_Replace; +use function str_pos as Pos; // Alias for global import. +use function \str_pad as STR_PAD; // Alias for global import to same name. + // Ignore as not import use. $closure = function() use($bar) { return $bar; diff --git a/Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.2.inc b/Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.2.inc new file mode 100644 index 0000000..1f6c7d2 --- /dev/null +++ b/Universal/Tests/UseStatements/DisallowUseFunctionUnitTest.2.inc @@ -0,0 +1,43 @@ + => */ - public function getErrorList() + public function getErrorList($testFile = '') { - return [ - 8 => 1, - 9 => 1, - 11 => 1, - 12 => 1, - 13 => 1, - 16 => 1, - 17 => 1, - 18 => 1, - 24 => 1, - 26 => 1, - ]; + switch ($testFile) { + case 'DisallowUseFunctionUnitTest.1.inc': + return [ + 8 => 1, + 9 => 1, // WithAlias. + 11 => 1, + 12 => 1, // WithAlias. + 13 => 1, + 16 => 1, + 17 => 1, // WithAlias. + 18 => 1, + 24 => 1, + 26 => 1, + 33 => 1, // WithAlias. + 34 => 1, // Note: alias same as name, so not counted as aliased. + 35 => 1, // GlobalNamespaceWithAlias. + 36 => 1, // GlobalNamespace. Note: alias same as name, so not counted as aliased. + ]; + + case 'DisallowUseFunctionUnitTest.2.inc': + return [ + 9 => 1, // SameNamespace. + 10 => 1, // SameNamespaceWithAlias. + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, // WithAlias. + 19 => 1, + 22 => 1, // GlobalNamespace. + 31 => 1, + 32 => 1, // WithAlias. + 35 => 1, + 37 => 1, + 39 => 1, // SameNamespaceWithAlias. + 40 => 1, // SameNamespace. + 43 => 1, // GlobalNamespace. + ]; + + case 'DisallowUseFunctionUnitTest.3.inc': + return [ + 9 => 1, // SameNamespace. + 10 => 1, // SameNamespaceWithAlias. + 13 => 1, + 14 => 1, + 17 => 1, + 18 => 1, // WithAlias. + 19 => 1, + 22 => 1, // GlobalNamespace. + 31 => 1, + 32 => 1, // WithAlias. + 35 => 1, + 37 => 1, + 38 => 1, // WithAlias. + 39 => 1, + 42 => 1, // GlobalNamespace. + 51 => 1, + 52 => 1, // WithAlias. + 55 => 1, + 57 => 1, + 59 => 1, // SameNamespaceWithAlias. + 60 => 1, // SameNamespace. + 63 => 1, // GlobalNamespace. + 69 => 1, // SameNamespace. Note: well, not really, but parse error. + ]; + + case 'DisallowUseFunctionUnitTest.4.inc': + return [ + 6 => 1, // WithAlias. + ]; + + default: + return []; + } } /**