33namespace ImportDetection \Sniffs \Imports ;
44
55use ImportDetection \Symbol ;
6- use ImportDetection \ImportedSymbol ;
76use ImportDetection \SniffHelpers ;
87use ImportDetection \FileSymbolRecord ;
98use PHP_CodeSniffer \Sniffs \Sniff ;
@@ -22,67 +21,71 @@ public function process(File $phpcsFile, $stackPtr) {
2221 $ helper = new SniffHelpers ();
2322 $ tokens = $ phpcsFile ->getTokens ();
2423 $ token = $ tokens [$ stackPtr ];
24+ // Keep one set of symbol records per file
2525 $ this ->symbolRecordsByFile [$ phpcsFile ->path ] = $ this ->symbolRecordsByFile [$ phpcsFile ->path ] ?? new FileSymbolRecord ;
2626 if ($ token ['type ' ] === 'T_WHITESPACE ' ) {
2727 $ this ->debug ('found whitespace ' );
2828 return $ this ->processEndOfFile ($ phpcsFile , $ stackPtr );
2929 }
3030 if ($ token ['type ' ] === 'T_USE ' ) {
3131 $ this ->debug ('found import ' );
32+ if (in_array ($ helper ->getImportType ($ phpcsFile , $ stackPtr ), ['function ' , 'const ' , 'class ' ])) {
33+ $ this ->recordImportedSymbols ($ phpcsFile , $ stackPtr );
34+ }
3235 return $ this ->processUse ($ phpcsFile , $ stackPtr );
3336 }
3437 $ symbol = $ helper ->getFullSymbol ($ phpcsFile , $ stackPtr );
3538 // If the symbol has been seen before (if this is a duplicate), ignore it
3639 if (in_array ($ symbol , $ this ->symbolRecordsByFile [$ phpcsFile ->path ]->seenSymbols )) {
37- $ this ->debug ('found duplicate symbol ' . $ symbol ->getName ());
40+ $ this ->debug ('found duplicate symbol: ' . $ symbol ->getName ());
3841 return ;
3942 }
4043 $ this ->symbolRecordsByFile [$ phpcsFile ->path ]->seenSymbols [] = $ symbol ;
4144 // If the symbol is in the ignore list, ignore it
4245 if ($ this ->isSymbolIgnored ($ symbol )) {
43- $ this ->debug ('found ignored symbol ' . $ symbol ->getName ());
46+ $ this ->debug ('found ignored symbol: ' . $ symbol ->getName ());
4447 $ this ->markSymbolUsed ($ phpcsFile , $ symbol );
4548 return ;
4649 }
4750 // If the symbol is a fully-qualified namespace, ignore it
4851 if ($ symbol ->isAbsoluteNamespace ()) {
49- $ this ->debug ('found absolute namespaced symbol ' . $ symbol ->getName ());
52+ $ this ->debug ('found absolute namespaced symbol: ' . $ symbol ->getName ());
5053 return ;
5154 }
5255 // If this symbol is a definition, ignore it
5356 if ($ helper ->isSymbolADefinition ($ phpcsFile , $ symbol )) {
54- $ this ->debug ('found definition symbol ' . $ symbol ->getName ());
57+ $ this ->debug ('found definition symbol: ' . $ symbol ->getName ());
5558 return ;
5659 }
5760 // If this symbol is a static reference or an object reference, ignore it
5861 if ($ helper ->isStaticReference ($ phpcsFile , $ stackPtr ) || $ helper ->isObjectReference ($ phpcsFile , $ stackPtr )) {
59- $ this ->debug ('found static symbol ' . $ symbol ->getName ());
62+ $ this ->debug ('found static symbol: ' . $ symbol ->getName ());
6063 return ;
6164 }
6265 // If this symbol is a namespace definition, ignore it
6366 if ($ helper ->isWithinNamespaceStatement ($ phpcsFile , $ symbol ->getSymbolPosition ())) {
64- $ this ->debug ('found namespace definition symbol ' . $ symbol ->getName ());
67+ $ this ->debug ('found namespace definition symbol: ' . $ symbol ->getName ());
6568 return ;
6669 }
6770 // If this symbol is an import, ignore it
6871 if ($ helper ->isWithinImportStatement ($ phpcsFile , $ symbol ->getSymbolPosition ())) {
69- $ this ->debug ('found symbol inside an import ' . $ symbol ->getName ());
72+ $ this ->debug ('found symbol inside an import: ' . $ symbol ->getName ());
7073 return ;
7174 }
7275 // If the symbol is predefined, ignore it
7376 if ($ helper ->isPredefinedConstant ($ phpcsFile , $ stackPtr ) || $ helper ->isBuiltInFunction ($ phpcsFile , $ stackPtr )) {
74- $ this ->debug ('found predefined symbol ' . $ symbol ->getName ());
77+ $ this ->debug ('found predefined symbol: ' . $ symbol ->getName ());
7578 return ;
7679 }
7780 // If this symbol is a predefined typehint, ignore it
7881 if ($ helper ->isPredefinedTypehint ($ phpcsFile , $ stackPtr )) {
79- $ this ->debug ('found typehint symbol ' . $ symbol ->getName ());
82+ $ this ->debug ('found typehint symbol: ' . $ symbol ->getName ());
8083 return ;
8184 }
8285 // If the symbol's namespace is imported or defined, ignore it
8386 // If the symbol has no namespace and is itself is imported or defined, ignore it
8487 if ($ this ->isSymbolDefined ($ phpcsFile , $ symbol )) {
85- $ this ->debug ('found defined symbol ' . $ symbol ->getName ());
88+ $ this ->debug ('found defined symbol: ' . $ symbol ->getName ());
8689 $ this ->markSymbolUsed ($ phpcsFile , $ symbol );
8790 return ;
8891 }
@@ -150,30 +153,37 @@ private function processUse(File $phpcsFile, $stackPtr) {
150153 }
151154 }
152155
153- private function recordImportedSymbols (File $ phpcsFile , int $ stackPtr , array $ importNames ) {
154- foreach ($ importNames as $ symbol ) {
155- $ this ->symbolRecordsByFile [$ phpcsFile ->path ]->importedSymbolRecords [] = new ImportedSymbol ($ stackPtr , $ symbol );
156- }
156+ private function recordImportedSymbols (File $ phpcsFile , int $ stackPtr ) {
157+ $ helper = new SniffHelpers ();
158+ $ symbols = $ helper ->getImportedSymbolsFromImportStatement ($ phpcsFile , $ stackPtr );
159+ $ this ->debug ('recording imported symbols: ' . implode (', ' , array_map (function (Symbol $ symbol ): string {
160+ return $ symbol ->getName ();
161+ }, $ symbols )));
162+ $ symbols = array_map (function ($ symbol ) {
163+ if ($ this ->isSymbolIgnored ($ symbol )) {
164+ $ this ->debug ('found ignored imported symbol: ' . $ symbol ->getName ());
165+ $ symbol ->markUsed ();
166+ }
167+ return $ symbol ;
168+ }, $ symbols );
169+ $ this ->symbolRecordsByFile [$ phpcsFile ->path ]->addImportedSymbolRecords ($ symbols );
157170 }
158171
159172 private function saveFunctionImport (File $ phpcsFile , $ stackPtr ) {
160173 $ helper = new SniffHelpers ();
161174 $ importNames = $ helper ->getImportNames ($ phpcsFile , $ stackPtr );
162- $ this ->recordImportedSymbols ($ phpcsFile , $ stackPtr , $ importNames );
163175 $ this ->symbolRecordsByFile [$ phpcsFile ->path ]->addImportedFunctions ($ importNames );
164176 }
165177
166178 private function saveConstImport (File $ phpcsFile , $ stackPtr ) {
167179 $ helper = new SniffHelpers ();
168180 $ importNames = $ helper ->getImportNames ($ phpcsFile , $ stackPtr );
169- $ this ->recordImportedSymbols ($ phpcsFile , $ stackPtr , $ importNames );
170181 $ this ->symbolRecordsByFile [$ phpcsFile ->path ]->addImportedConsts ($ importNames );
171182 }
172183
173184 private function saveClassImport (File $ phpcsFile , $ stackPtr ) {
174185 $ helper = new SniffHelpers ();
175186 $ importNames = $ helper ->getImportNames ($ phpcsFile , $ stackPtr );
176- $ this ->recordImportedSymbols ($ phpcsFile , $ stackPtr , $ importNames );
177187 $ this ->symbolRecordsByFile [$ phpcsFile ->path ]->addImportedClasses ($ importNames );
178188 }
179189
@@ -228,16 +238,20 @@ private function isConstDefined(File $phpcsFile, string $functionName): bool {
228238 }
229239
230240 private function markSymbolUsed (File $ phpcsFile , Symbol $ symbol ) {
231- $ record = $ this ->getSymbolRecord ($ phpcsFile , $ symbol );
241+ $ record = $ this ->getRecordedImportedSymbolMatchingSymbol ($ phpcsFile , $ symbol );
232242 if (! $ record ) {
243+ // Symbol records only exist for imported symbols, so if a used symbol
244+ // has not been imported we don't need to mark anything.
245+ $ this ->debug ("ignoring marking symbol used since it was never imported: {$ symbol ->getName ()}" );
233246 return ;
234247 }
235248 $ record ->markUsed ();
236249 }
237250
238- private function getSymbolRecord (File $ phpcsFile , Symbol $ symbol ) {
251+ private function getRecordedImportedSymbolMatchingSymbol (File $ phpcsFile , Symbol $ symbol ) {
239252 foreach ($ this ->symbolRecordsByFile [$ phpcsFile ->path ]->importedSymbolRecords as $ record ) {
240- if ($ record ->getName () === $ symbol ->getTopLevelNamespace ()) {
253+ $ this ->debug ("comparing symbol {$ symbol ->getTopLevelNamespace ()} to alias {$ record ->getAlias ()}" );
254+ if ($ record ->getAlias () === $ symbol ->getTopLevelNamespace ()) {
241255 return $ record ;
242256 }
243257 }
@@ -253,8 +267,9 @@ private function processEndOfFile(File $phpcsFile, int $stackPtr) {
253267 // For each import, if the Symbol was not used, mark a warning
254268 foreach ($ this ->symbolRecordsByFile [$ phpcsFile ->path ]->importedSymbolRecords as $ record ) {
255269 if (! $ record ->isUsed ()) {
270+ $ this ->debug ("found unused symbol: {$ record ->getName ()}" );
256271 $ error = "Found unused symbol ' {$ record ->getName ()}'. " ;
257- $ phpcsFile ->addWarning ($ error , $ record ->getPtr (), 'Import ' );
272+ $ phpcsFile ->addWarning ($ error , $ record ->getSymbolPosition (), 'Import ' );
258273 }
259274 }
260275 }
0 commit comments