Skip to content

Commit 7cbc088

Browse files
committed
Use Symbol in place of ImportedSymbol
This refactors the sniff to store symbol imports as instances of `Symbol` rather than `ImportedSymbol`. `Symbol` is much more intelligent and can keep track of the different parts of a symbol, rather than only the imported alias. It can also identify full namespace paths even for grouped imports. More importantly, this marks each imported symbol as "used" directly as soon as it is imported if it matches one of our ignored patterns. This prevents issues where the pattern match would fail for symbols in an import statement since `markSymbolUsed()` first tries to retrieve the appropriate symbol from the records based on its top-level namespace. That technique works fine for regular symbol use (eg: when looking to see if `Foo\bar()` is imported, we need to look for a statement like `use Lib\Classes\Foo`) but will fail for the symbol in the import itself. In the latter case, if we mark it as "used" before it's stored in the records, then we don't have to look it up at all.
1 parent 90b5d69 commit 7cbc088

File tree

2 files changed

+27
-44
lines changed

2 files changed

+27
-44
lines changed

ImportDetection/ImportedSymbol.php

Lines changed: 0 additions & 32 deletions
This file was deleted.

ImportDetection/Sniffs/Imports/RequireImportsSniff.php

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace ImportDetection\Sniffs\Imports;
44

55
use ImportDetection\Symbol;
6-
use ImportDetection\ImportedSymbol;
76
use ImportDetection\SniffHelpers;
87
use ImportDetection\FileSymbolRecord;
98
use PHP_CodeSniffer\Sniffs\Sniff;
@@ -22,13 +21,17 @@ 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);
@@ -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

Comments
 (0)