From 0c679b53226e987783ffc11bc0832621305cfb29 Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 17 May 2019 01:11:41 +0200 Subject: [PATCH] FQCN improvements. --- .../NoInlineFullyQualifiedClassNameSniff.php | 219 +++++++++++++++++- 1 file changed, 218 insertions(+), 1 deletion(-) diff --git a/PSR2R/Sniffs/Namespaces/NoInlineFullyQualifiedClassNameSniff.php b/PSR2R/Sniffs/Namespaces/NoInlineFullyQualifiedClassNameSniff.php index 3f79e71..cea2e61 100644 --- a/PSR2R/Sniffs/Namespaces/NoInlineFullyQualifiedClassNameSniff.php +++ b/PSR2R/Sniffs/Namespaces/NoInlineFullyQualifiedClassNameSniff.php @@ -62,14 +62,19 @@ public function process(File $phpcsFile, $stackPtr) { $this->loadStatements($phpcsFile); $this->findSentinel($phpcsFile); - if ($tokens[$stackPtr]['code'] === T_CLASS) { + if ($tokens[$stackPtr]['code'] === T_CLASS || $tokens[$stackPtr]['code'] === T_INTERFACE || $tokens[$stackPtr]['code'] === T_TRAIT) { $this->checkUseForClass($phpcsFile, $stackPtr); } elseif ($tokens[$stackPtr]['code'] === T_NEW) { $this->checkUseForNew($phpcsFile, $stackPtr); } elseif ($tokens[$stackPtr]['code'] === T_DOUBLE_COLON) { $this->checkUseForStatic($phpcsFile, $stackPtr); + } elseif ($tokens[$stackPtr]['code'] === T_INSTANCEOF) { + $this->checkUseForInstanceOf($phpcsFile, $stackPtr); + } elseif ($tokens[$stackPtr]['code'] === T_CATCH || $tokens[$stackPtr]['code'] === T_CALLABLE) { + $this->checkUseForCatchOrCallable($phpcsFile, $stackPtr); } else { $this->checkUseForSignature($phpcsFile, $stackPtr); + $this->checkUseForReturnTypeHint($phpcsFile, $stackPtr); } $this->insertUseWhenSentinel($phpcsFile, $stackPtr); } @@ -575,6 +580,218 @@ protected function checkUseForStatic(File $phpcsFile, $stackPtr) { $phpcsFile->fixer->endChangeset(); } + /** + * @param \PHP_CodeSniffer\Files\File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + protected function checkUseForInstanceOf(File $phpcsFile, $stackPtr) { + $tokens = $phpcsFile->getTokens(); + + $classNameIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true); + + $lastIndex = null; + $i = $classNameIndex; + $extractedUseStatement = ''; + $lastSeparatorIndex = null; + while (true) { + if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING], $tokens[$i])) { + break; + } + $lastIndex = $i; + $extractedUseStatement .= $tokens[$i]['content']; + + if ($this->isGivenKind([T_NS_SEPARATOR], $tokens[$i])) { + $lastSeparatorIndex = $i; + } + ++$i; + } + + if ($lastIndex === null || $lastSeparatorIndex === null) { + return; + } + + $extractedUseStatement = ltrim($extractedUseStatement, '\\'); + + $className = ''; + for ($i = $lastSeparatorIndex + 1; $i <= $lastIndex; ++$i) { + $className .= $tokens[$i]['content']; + } + + $error = 'Use statement ' . $extractedUseStatement . ' for class ' . $className . ' should be in use block.'; + $fix = $phpcsFile->addFixableError($error, $stackPtr, 'InstanceOf'); + if (!$fix) { + return; + } + + $phpcsFile->fixer->beginChangeset(); + + $addedUseStatement = $this->addUseStatement($className, $extractedUseStatement); + $firstSeparatorIndex = $classNameIndex; + + for ($k = $lastSeparatorIndex; $k > $firstSeparatorIndex; --$k) { + $phpcsFile->fixer->replaceToken($k, ''); + } + $phpcsFile->fixer->replaceToken($firstSeparatorIndex, ''); + + if ($addedUseStatement['alias'] !== null) { + $phpcsFile->fixer->replaceToken($lastIndex, $addedUseStatement['alias']); + for ($k = $lastSeparatorIndex + 1; $k < $lastIndex; ++$k) { + $phpcsFile->fixer->replaceToken($k, ''); + } + } + + $phpcsFile->fixer->endChangeset(); + } + + /** + * @param \PHP_CodeSniffer\Files\File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + public function checkUseForCatchOrCallable(File $phpcsFile, $stackPtr) { + $tokens = $phpcsFile->getTokens(); + + $openParenthesisIndex = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr + 1); + $closeParenthesisIndex = $tokens[$openParenthesisIndex]['parenthesis_closer']; + $classNameIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $openParenthesisIndex + 1, null, true); + + $lastIndex = null; + $i = $classNameIndex; + $extractedUseStatement = ''; + $lastSeparatorIndex = null; + while ($i < $closeParenthesisIndex) { + if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING], $tokens[$i])) { + break; + } + $lastIndex = $i; + $extractedUseStatement .= $tokens[$i]['content']; + + if ($this->isGivenKind([T_NS_SEPARATOR], $tokens[$i])) { + $lastSeparatorIndex = $i; + } + ++$i; + } + + if ($lastIndex === null || $lastSeparatorIndex === null) { + return; + } + + $extractedUseStatement = ltrim($extractedUseStatement, '\\'); + + $className = ''; + for ($k = $lastSeparatorIndex + 1; $k <= $lastIndex; ++$k) { + $className .= $tokens[$k]['content']; + } + + $error = 'Use statement ' . $extractedUseStatement . ' for class ' . $className . ' should be in use block.'; + $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Catch'); + if (!$fix) { + return; + } + + $startIndex = $openParenthesisIndex; + + $phpcsFile->fixer->beginChangeset(); + + $firstSeparatorIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $startIndex + 1, null, true); + + $addedUseStatement = $this->addUseStatement($className, $extractedUseStatement); + + for ($k = $lastSeparatorIndex; $k > $firstSeparatorIndex; --$k) { + $phpcsFile->fixer->replaceToken($k, ''); + } + $phpcsFile->fixer->replaceToken($firstSeparatorIndex, ''); + + if ($addedUseStatement['alias'] !== null) { + $phpcsFile->fixer->replaceToken($firstSeparatorIndex + 1, $addedUseStatement['alias']); + for ($i = $firstSeparatorIndex + 2; $i <= $lastIndex; ++$i) { + $phpcsFile->fixer->replaceToken($i, ''); + } + } + + $phpcsFile->fixer->endChangeset(); + } + + /** + * @param \PHP_CodeSniffer\Files\File $phpcsFile + * @param int $stackPtr + * + * @return void + */ + protected function checkUseForReturnTypeHint(File $phpcsFile, $stackPtr) { + $tokens = $phpcsFile->getTokens(); + + $openParenthesisIndex = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr + 1); + $closeParenthesisIndex = $tokens[$openParenthesisIndex]['parenthesis_closer']; + + $colonIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $closeParenthesisIndex + 1, null, true); + if (!$colonIndex) { + return; + } + + $startIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $colonIndex + 1, $colonIndex + 3, true); + if (!$startIndex) { + return; + } + + if ($tokens[$startIndex]['type'] === 'T_NULLABLE') { + $startIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $startIndex + 1, $startIndex + 3, true); + } + + $lastIndex = null; + $j = $startIndex; + $extractedUseStatement = ''; + $lastSeparatorIndex = null; + while (true) { + if (!$this->isGivenKind([T_NS_SEPARATOR, T_STRING, T_RETURN_TYPE], $tokens[$j])) { + break; + } + + $lastIndex = $j; + $extractedUseStatement .= $tokens[$j]['content']; + if ($this->isGivenKind([T_NS_SEPARATOR], $tokens[$j])) { + $lastSeparatorIndex = $j; + } + ++$j; + } + + if ($lastIndex === null || $lastSeparatorIndex === null) { + return; + } + + $extractedUseStatement = ltrim($extractedUseStatement, '\\'); + $className = ''; + for ($k = $lastSeparatorIndex + 1; $k <= $lastIndex; ++$k) { + $className .= $tokens[$k]['content']; + } + + $error = 'Use statement ' . $extractedUseStatement . ' for class ' . $className . ' should be in use block.'; + $fix = $phpcsFile->addFixableError($error, $colonIndex, 'ReturnSignature'); + if (!$fix) { + return; + } + + $phpcsFile->fixer->beginChangeset(); + + $firstSeparatorIndex = $phpcsFile->findNext(Tokens::$emptyTokens, $startIndex, null, true); + + $addedUseStatement = $this->addUseStatement($className, $extractedUseStatement); + + for ($k = $lastSeparatorIndex; $k > $firstSeparatorIndex; --$k) { + $phpcsFile->fixer->replaceToken($k, ''); + } + $phpcsFile->fixer->replaceToken($firstSeparatorIndex, ''); + + if ($addedUseStatement['alias'] !== null) { + $phpcsFile->fixer->replaceToken($lastIndex, $addedUseStatement['alias']); + } + + $phpcsFile->fixer->endChangeset(); + } + /** * @param \PHP_CodeSniffer\Files\File $phpcsFile * @param int $stackPtr