Chris@0: getTokens(); Chris@0: Chris@0: // Only check use statements in the global scope. Chris@0: if (empty($tokens[$stackPtr]['conditions']) === false) { Chris@0: return; Chris@0: } Chris@0: Chris@0: // Seek to the end of the statement and get the string before the semi colon. Chris@0: $semiColon = $phpcsFile->findEndOfStatement($stackPtr); Chris@0: if ($tokens[$semiColon]['code'] !== T_SEMICOLON) { Chris@0: return; Chris@0: } Chris@0: Chris@0: $classPtr = $phpcsFile->findPrevious( Chris@17: Tokens::$emptyTokens, Chris@0: ($semiColon - 1), Chris@0: null, Chris@0: true Chris@0: ); Chris@0: Chris@0: if ($tokens[$classPtr]['code'] !== T_STRING) { Chris@0: return; Chris@0: } Chris@0: Chris@0: // Search where the class name is used. PHP treats class names case Chris@0: // insensitive, that's why we cannot search for the exact class name string Chris@0: // and need to iterate over all T_STRING tokens in the file. Chris@0: $classUsed = $phpcsFile->findNext(T_STRING, ($classPtr + 1)); Chris@0: $lowerClassName = strtolower($tokens[$classPtr]['content']); Chris@0: Chris@0: // Check if the referenced class is in the same namespace as the current Chris@0: // file. If it is then the use statement is not necessary. Chris@0: $namespacePtr = $phpcsFile->findPrevious([T_NAMESPACE], $stackPtr); Chris@0: // Check if the use statement does aliasing with the "as" keyword. Aliasing Chris@0: // is allowed even in the same namespace. Chris@0: $aliasUsed = $phpcsFile->findPrevious(T_AS, ($classPtr - 1), $stackPtr); Chris@0: Chris@0: if ($namespacePtr !== false && $aliasUsed === false) { Chris@0: $nsEnd = $phpcsFile->findNext( Chris@0: [ Chris@0: T_NS_SEPARATOR, Chris@0: T_STRING, Chris@0: T_WHITESPACE, Chris@0: ], Chris@0: ($namespacePtr + 1), Chris@0: null, Chris@0: true Chris@0: ); Chris@0: $namespace = trim($phpcsFile->getTokensAsString(($namespacePtr + 1), ($nsEnd - $namespacePtr - 1))); Chris@0: Chris@0: $useNamespacePtr = $phpcsFile->findNext([T_STRING], ($stackPtr + 1)); Chris@0: $useNamespaceEnd = $phpcsFile->findNext( Chris@0: [ Chris@0: T_NS_SEPARATOR, Chris@0: T_STRING, Chris@0: ], Chris@0: ($useNamespacePtr + 1), Chris@0: null, Chris@0: true Chris@0: ); Chris@0: $use_namespace = rtrim($phpcsFile->getTokensAsString($useNamespacePtr, ($useNamespaceEnd - $useNamespacePtr - 1)), '\\'); Chris@0: Chris@0: if (strcasecmp($namespace, $use_namespace) === 0) { Chris@0: $classUsed = false; Chris@0: } Chris@0: }//end if Chris@0: Chris@0: while ($classUsed !== false) { Chris@0: if (strtolower($tokens[$classUsed]['content']) === $lowerClassName) { Chris@0: // If the name is used in a PHP 7 function return type declaration Chris@0: // stop. Chris@0: if ($tokens[$classUsed]['code'] === T_RETURN_TYPE) { Chris@0: return; Chris@0: } Chris@0: Chris@0: $beforeUsage = $phpcsFile->findPrevious( Chris@17: Tokens::$emptyTokens, Chris@0: ($classUsed - 1), Chris@0: null, Chris@0: true Chris@0: ); Chris@0: // If a backslash is used before the class name then this is some other Chris@0: // use statement. Chris@0: if ($tokens[$beforeUsage]['code'] !== T_USE && $tokens[$beforeUsage]['code'] !== T_NS_SEPARATOR) { Chris@0: return; Chris@0: } Chris@0: Chris@0: // Trait use statement within a class. Chris@0: if ($tokens[$beforeUsage]['code'] === T_USE && empty($tokens[$beforeUsage]['conditions']) === false) { Chris@0: return; Chris@0: } Chris@0: }//end if Chris@0: Chris@0: $classUsed = $phpcsFile->findNext([T_STRING, T_RETURN_TYPE], ($classUsed + 1)); Chris@0: }//end while Chris@0: Chris@0: $warning = 'Unused use statement'; Chris@0: $fix = $phpcsFile->addFixableWarning($warning, $stackPtr, 'UnusedUse'); Chris@0: if ($fix === true) { Chris@0: // Remove the whole use statement line. Chris@0: $phpcsFile->fixer->beginChangeset(); Chris@0: for ($i = $stackPtr; $i <= $semiColon; $i++) { Chris@0: $phpcsFile->fixer->replaceToken($i, ''); Chris@0: } Chris@0: Chris@0: // Also remove whitespace after the semicolon (new lines). Chris@0: while (isset($tokens[$i]) === true && $tokens[$i]['code'] === T_WHITESPACE) { Chris@0: $phpcsFile->fixer->replaceToken($i, ''); Chris@0: if (strpos($tokens[$i]['content'], $phpcsFile->eolChar) !== false) { Chris@0: break; Chris@0: } Chris@0: Chris@0: $i++; Chris@0: } Chris@0: Chris@0: // Replace @var data types in doc comments with the fully qualified class Chris@0: // name. Chris@0: $useNamespacePtr = $phpcsFile->findNext([T_STRING], ($stackPtr + 1)); Chris@0: $useNamespaceEnd = $phpcsFile->findNext( Chris@0: [ Chris@0: T_NS_SEPARATOR, Chris@0: T_STRING, Chris@0: ], Chris@0: ($useNamespacePtr + 1), Chris@0: null, Chris@0: true Chris@0: ); Chris@0: $fullNamespace = $phpcsFile->getTokensAsString($useNamespacePtr, ($useNamespaceEnd - $useNamespacePtr)); Chris@0: Chris@0: $tag = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($stackPtr + 1)); Chris@0: Chris@0: while ($tag !== false) { Chris@0: if (($tokens[$tag]['content'] === '@var' || $tokens[$tag]['content'] === '@return') Chris@0: && isset($tokens[($tag + 1)]) === true Chris@0: && $tokens[($tag + 1)]['code'] === T_DOC_COMMENT_WHITESPACE Chris@0: && isset($tokens[($tag + 2)]) === true Chris@0: && $tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING Chris@0: && strpos($tokens[($tag + 2)]['content'], $tokens[$classPtr]['content']) === 0 Chris@0: ) { Chris@0: $replacement = '\\'.$fullNamespace.substr($tokens[($tag + 2)]['content'], strlen($tokens[$classPtr]['content'])); Chris@0: $phpcsFile->fixer->replaceToken(($tag + 2), $replacement); Chris@0: } Chris@0: Chris@0: $tag = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($tag + 1)); Chris@0: } Chris@0: Chris@0: $phpcsFile->fixer->endChangeset(); Chris@0: }//end if Chris@0: Chris@0: }//end process() Chris@0: Chris@0: Chris@0: }//end class