Skip to content

Commit

Permalink
Report unmatched ignores (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal authored Jan 11, 2024
1 parent b1a8986 commit 8067649
Show file tree
Hide file tree
Showing 11 changed files with 468 additions and 113 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ return $config
// but you may want to ignore those packages manually to be sure
->enableAnalysisOfUnusedDevDependencies()

// do not report ignores that never matched any error
->disableReportingUnmatchedIgnores()

// disable detection of dev dependencies in production code globally
->ignoreErrors([ErrorType::DEV_DEPENDENCY_IN_PROD])

Expand Down
12 changes: 6 additions & 6 deletions bin/composer-dependency-analyser
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ Options:
Ignore options:
(or use --config for better granularity)
--ignore-unknown-classes Ignore when class is not found in classmap
--ignore-unused-deps Ignore all unused dependency issues
--ignore-shadow-deps Ignore all shadow dependency issues
--ignore-dev-in-prod-deps Ignore all dev dependency in production code issues
--ignore-prod-only-in-dev-deps Ignore all prod dependency used only in dev paths issues
--ignore-unknown-classes Ignore when class is not found in classmap
--ignore-unused-deps Ignore all unused dependency issues
--ignore-shadow-deps Ignore all shadow dependency issues
--ignore-dev-in-prod-deps Ignore all dev dependency in production code issues
--ignore-prod-only-in-dev-deps Ignore all prod dependency used only in dev paths issues
EOD;
Expand Down Expand Up @@ -197,7 +197,7 @@ $stopwatch = new Stopwatch();
$analyser = new Analyser($stopwatch, $config, $vendorDir, $loaders[$vendorDir]->getClassMap(), $composerJson->dependencies);
$result = $analyser->run();

$exitCode = $printer->printResult($result, isset($providedOptions['verbose']));
$exitCode = $printer->printResult($result, isset($providedOptions['verbose']), $config->shouldReportUnmatchedIgnoredErrors());
exit($exitCode);


17 changes: 10 additions & 7 deletions src/Analyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public function run(): AnalysisResult
$usedPackages = [];
$prodPackagesUsedInProdPath = [];

$ignoreList = $this->config->getIgnoreList();

foreach ($this->config->getPathsToScan() as $scanPath) {
foreach ($this->listPhpFilesIn($scanPath->getPath()) as $filePath) {
if ($this->config->isExcludedFilepath($filePath)) {
Expand All @@ -121,8 +123,8 @@ public function run(): AnalysisResult
if (!$this->isInClassmap($usedSymbol)) {
if (
!$this->isConstOrFunction($usedSymbol)
&& !$this->config->shouldIgnoreUnknownClass($usedSymbol)
&& !$this->config->shouldIgnoreError(ErrorType::UNKNOWN_CLASS, $filePath, null)
&& !$ignoreList->shouldIgnoreUnknownClass($usedSymbol)
&& !$ignoreList->shouldIgnoreError(ErrorType::UNKNOWN_CLASS, $filePath, null)
) {
foreach ($lineNumbers as $lineNumber) {
$classmapErrors[$usedSymbol][] = new SymbolUsage($filePath, $lineNumber);
Expand All @@ -142,7 +144,7 @@ public function run(): AnalysisResult

if (
$this->isShadowDependency($packageName)
&& !$this->config->shouldIgnoreError(ErrorType::SHADOW_DEPENDENCY, $filePath, $packageName)
&& !$ignoreList->shouldIgnoreError(ErrorType::SHADOW_DEPENDENCY, $filePath, $packageName)
) {
foreach ($lineNumbers as $lineNumber) {
$shadowErrors[$packageName][$usedSymbol][] = new SymbolUsage($filePath, $lineNumber);
Expand All @@ -152,7 +154,7 @@ public function run(): AnalysisResult
if (
!$scanPath->isDev()
&& $this->isDevDependency($packageName)
&& !$this->config->shouldIgnoreError(ErrorType::DEV_DEPENDENCY_IN_PROD, $filePath, $packageName)
&& !$ignoreList->shouldIgnoreError(ErrorType::DEV_DEPENDENCY_IN_PROD, $filePath, $packageName)
) {
foreach ($lineNumbers as $lineNumber) {
$devInProdErrors[$packageName][$usedSymbol][] = new SymbolUsage($filePath, $lineNumber);
Expand Down Expand Up @@ -203,7 +205,7 @@ public function run(): AnalysisResult
);

foreach ($unusedDependencies as $unusedDependency) {
if (!$this->config->shouldIgnoreError(ErrorType::UNUSED_DEPENDENCY, null, $unusedDependency)) {
if (!$ignoreList->shouldIgnoreError(ErrorType::UNUSED_DEPENDENCY, null, $unusedDependency)) {
$unusedErrors[] = $unusedDependency;
}
}
Expand All @@ -219,7 +221,7 @@ public function run(): AnalysisResult
);

foreach ($prodPackagesUsedOnlyInDev as $prodPackageUsedOnlyInDev) {
if (!$this->config->shouldIgnoreError(ErrorType::PROD_DEPENDENCY_ONLY_IN_DEV, null, $prodPackageUsedOnlyInDev)) {
if (!$ignoreList->shouldIgnoreError(ErrorType::PROD_DEPENDENCY_ONLY_IN_DEV, null, $prodPackageUsedOnlyInDev)) {
$prodOnlyInDevErrors[] = $prodPackageUsedOnlyInDev;
}
}
Expand All @@ -237,7 +239,8 @@ public function run(): AnalysisResult
$shadowErrors,
$devInProdErrors,
$prodOnlyInDevErrors,
$unusedErrors
$unusedErrors,
$ignoreList->getUnusedIgnores()
);
}

Expand Down
100 changes: 24 additions & 76 deletions src/Config/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@
namespace ShipMonk\ComposerDependencyAnalyser\Config;

use LogicException;
use function array_flip;
use ShipMonk\ComposerDependencyAnalyser\Config\Ignore\IgnoreList;
use function array_keys;
use function array_merge;
use function get_defined_constants;
use function in_array;
use function preg_last_error;
use function preg_match;
use function realpath;
use function strpos;

Expand All @@ -26,6 +23,11 @@ class Configuration
*/
private $reportUnusedDevDependencies = false;

/**
* @var bool
*/
private $reportUnmatchedIgnores = true;

/**
* @var list<string>
*/
Expand Down Expand Up @@ -80,6 +82,12 @@ public function disableComposerAutoloadPathScan(): self
return $this;
}

public function disableReportingUnmatchedIgnores(): self
{
$this->reportUnmatchedIgnores = false;
return $this;
}

/**
* @return $this
*/
Expand Down Expand Up @@ -259,7 +267,16 @@ public function ignoreUnknownClassesRegex(string $classNameRegex): self
return $this;
}

// getters below
public function getIgnoreList(): IgnoreList
{
return new IgnoreList(
$this->ignoredErrors,
$this->ignoredErrorsOnPath,
$this->ignoredErrorsOnPackage,
$this->ignoredUnknownClasses,
$this->ignoredUnknownClassesRegexes
);
}

/**
* @return list<string>
Expand Down Expand Up @@ -311,78 +328,9 @@ public function shouldReportUnusedDevDependencies(): bool
return $this->reportUnusedDevDependencies;
}

public function shouldIgnoreUnknownClass(string $class): bool
{
if (in_array($class, $this->ignoredUnknownClasses, true)) {
return true;
}

foreach ($this->ignoredUnknownClassesRegexes as $regex) {
$matches = preg_match($regex, $class);

if ($matches === false) {
/** @var array<string, int> $pcreConstants */
$pcreConstants = get_defined_constants(true)['pcre'] ?? [];
$error = array_flip($pcreConstants)[preg_last_error()] ?? 'unknown error';
throw new LogicException("Invalid regex: '$regex', error: $error");
}

if ($matches === 1) {
return true;
}
}

return false;
}

/**
* @param ErrorType::* $errorType
*/
public function shouldIgnoreError(string $errorType, ?string $filePath, ?string $packageName): bool
{
if ($this->shouldIgnoreErrorGlobally($errorType)) {
return true;
}

if ($filePath !== null && $this->shouldIgnoreErrorOnPath($errorType, $filePath)) {
return true;
}

if ($packageName !== null && $this->shouldIgnoreErrorOnPackage($errorType, $packageName)) {
return true;
}

return false;
}

/**
* @param ErrorType::* $errorType
*/
private function shouldIgnoreErrorGlobally(string $errorType): bool
{
return in_array($errorType, $this->ignoredErrors, true);
}

/**
* @param ErrorType::* $errorType
*/
private function shouldIgnoreErrorOnPath(string $errorType, string $filePath): bool
{
foreach ($this->ignoredErrorsOnPath as $path => $errorTypes) {
if ($this->isFilepathWithinPath($filePath, $path)) {
return in_array($errorType, $errorTypes, true);
}
}

return false;
}

/**
* @param ErrorType::* $errorType
*/
private function shouldIgnoreErrorOnPackage(string $errorType, string $packageName): bool
public function shouldReportUnmatchedIgnoredErrors(): bool
{
return in_array($errorType, $this->ignoredErrorsOnPackage[$packageName] ?? [], true);
return $this->reportUnmatchedIgnores;
}

public function isExcludedFilepath(string $filePath): bool
Expand Down
Loading

0 comments on commit 8067649

Please sign in to comment.