From 87b2d38ce5f29ee00868e70b2905a91a6f6a90c8 Mon Sep 17 00:00:00 2001 From: Emanuele Panzeri Date: Tue, 18 Mar 2025 11:13:42 +0100 Subject: [PATCH 1/4] Excluded path: include example for optional path Make more clear how to mark an excluded path optional, with the use of `(?)` suffix --- src/Command/CommandHelper.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index 499ebb48e8..470af30583 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -380,7 +380,8 @@ public static function begin( } $errorOutput->writeLineFormatted('If the excluded path can sometimes exist, append (?)'); - $errorOutput->writeLineFormatted('to its config entry to mark it as optional.'); + $errorOutput->writeLineFormatted('to its config entry to mark it as optional. Example:'); + $errorOutput->writeLineFormatted(' - path/something (?)'); $errorOutput->writeLineFormatted(''); throw new InceptionNotSuccessfulException(); From ef72f8afb632a25650e64e00291889f7a90802ae Mon Sep 17 00:00:00 2001 From: thePanz Date: Wed, 19 Mar 2025 00:42:22 +0100 Subject: [PATCH 2/4] Exclude path: include example of suggested optional paths from current configuration --- src/Command/CommandHelper.php | 17 ++++-- .../InvalidExcludePathsException.php | 11 +++- .../ValidateExcludePathsExtension.php | 58 +++++++++---------- .../ValidateIgnoredErrorsExtension.php | 2 +- 4 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index 470af30583..dc71bdd679 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -379,10 +379,19 @@ public static function begin( $errorOutput->writeLineFormatted(''); } - $errorOutput->writeLineFormatted('If the excluded path can sometimes exist, append (?)'); - $errorOutput->writeLineFormatted('to its config entry to mark it as optional. Example:'); - $errorOutput->writeLineFormatted(' - path/something (?)'); - $errorOutput->writeLineFormatted(''); + $suggestOptional = $e->getSuggestOptional(); + if (count($suggestOptional) > 0) { + $errorOutput->writeLineFormatted('If the excluded path can sometimes exist, append (?)'); + $errorOutput->writeLineFormatted('to its config entry to mark it as optional. Example:'); + $errorOutput->writeLineFormatted('excludePaths:'); + foreach ($suggestOptional as $key => $suggestOptionalPaths) { + $errorOutput->writeLineFormatted(sprintf(' %s:', $key)); + foreach ($suggestOptionalPaths as $suggestOptionalPath) { + $errorOutput->writeLineFormatted(sprintf(' - %s (?)', $suggestOptionalPath)); + } + } + $errorOutput->writeLineFormatted(''); + } throw new InceptionNotSuccessfulException(); } catch (ValidationException $e) { diff --git a/src/DependencyInjection/InvalidExcludePathsException.php b/src/DependencyInjection/InvalidExcludePathsException.php index 24ecfb9565..b2ae030782 100644 --- a/src/DependencyInjection/InvalidExcludePathsException.php +++ b/src/DependencyInjection/InvalidExcludePathsException.php @@ -10,8 +10,9 @@ final class InvalidExcludePathsException extends Exception /** * @param string[] $errors + * @param array{analyse?: list, analyseAndScan?: list} $suggestOptional */ - public function __construct(private array $errors) + public function __construct(private array $errors, private array $suggestOptional) { parent::__construct(implode("\n", $this->errors)); } @@ -24,4 +25,12 @@ public function getErrors(): array return $this->errors; } + /** + * @return array{analyse?: list, analyseAndScan?: list} + */ + public function getSuggestOptional(): array + { + return $this->suggestOptional; + } + } diff --git a/src/DependencyInjection/ValidateExcludePathsExtension.php b/src/DependencyInjection/ValidateExcludePathsExtension.php index 6d099d1c10..dcb8fa8982 100644 --- a/src/DependencyInjection/ValidateExcludePathsExtension.php +++ b/src/DependencyInjection/ValidateExcludePathsExtension.php @@ -7,7 +7,6 @@ use PHPStan\File\FileExcluder; use function array_key_exists; use function array_map; -use function array_merge; use function count; use function is_dir; use function is_file; @@ -27,41 +26,42 @@ public function loadConfiguration(): void return; } + $newExcludePaths = []; + if (array_key_exists('analyseAndScan', $excludePaths)) { + $newExcludePaths['analyseAndScan'] = $excludePaths['analyseAndScan']; + } + if (array_key_exists('analyse', $excludePaths)) { + $newExcludePaths['analyse'] = $excludePaths['analyse']; + } + $errors = []; + $suggestOptional = []; if ($builder->parameters['__validate']) { - $paths = []; - if (array_key_exists('analyse', $excludePaths)) { - $paths = $excludePaths['analyse']; - } - if (array_key_exists('analyseAndScan', $excludePaths)) { - $paths = array_merge($paths, $excludePaths['analyseAndScan']); - } - foreach ($paths as $path) { - if ($path instanceof OptionalPath) { - continue; - } - if (FileExcluder::isAbsolutePath($path)) { - if (is_dir($path)) { + foreach ($newExcludePaths as $key => $paths) { + foreach ($paths as $path) { + if ($path instanceof OptionalPath) { continue; } - if (is_file($path)) { + if (FileExcluder::isAbsolutePath($path)) { + if (is_dir($path)) { + continue; + } + if (is_file($path)) { + continue; + } + } + if (FileExcluder::isFnmatchPattern($path)) { continue; } - } - if (FileExcluder::isFnmatchPattern($path)) { - continue; - } - $errors[] = sprintf('Path %s is neither a directory, nor a file path, nor a fnmatch pattern.', $path); + $suggestOptional[$key][] = $path; + $errors[] = sprintf('Path "%s" is neither a directory, nor a file path, nor a fnmatch pattern.', $path); + } } } - $newExcludePaths = []; - if (array_key_exists('analyseAndScan', $excludePaths)) { - $newExcludePaths['analyseAndScan'] = $excludePaths['analyseAndScan']; - } - if (array_key_exists('analyse', $excludePaths)) { - $newExcludePaths['analyse'] = $excludePaths['analyse']; + if (count($errors) !== 0) { + throw new InvalidExcludePathsException($errors, $suggestOptional); } foreach ($newExcludePaths as $key => $p) { @@ -72,12 +72,6 @@ public function loadConfiguration(): void } $builder->parameters['excludePaths'] = $newExcludePaths; - - if (count($errors) === 0) { - return; - } - - throw new InvalidExcludePathsException($errors); } } diff --git a/src/DependencyInjection/ValidateIgnoredErrorsExtension.php b/src/DependencyInjection/ValidateIgnoredErrorsExtension.php index 4560fde44c..68a41d3e9f 100644 --- a/src/DependencyInjection/ValidateIgnoredErrorsExtension.php +++ b/src/DependencyInjection/ValidateIgnoredErrorsExtension.php @@ -168,7 +168,7 @@ public function getRegistry(): OperatorTypeSpecifyingExtensionRegistry continue; } - $errors[] = sprintf('Path %s is neither a directory, nor a file path, nor a fnmatch pattern.', $ignorePath); + $errors[] = sprintf('Path "%s" is neither a directory, nor a file path, nor a fnmatch pattern.', $ignorePath); } } } From 4b9ee022ee88d7fea11d327808f0f64b88bc4063 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 19 Mar 2025 11:35:42 +0100 Subject: [PATCH 3/4] Correct config section --- src/Command/CommandHelper.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index dc71bdd679..df078946fc 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -383,11 +383,13 @@ public static function begin( if (count($suggestOptional) > 0) { $errorOutput->writeLineFormatted('If the excluded path can sometimes exist, append (?)'); $errorOutput->writeLineFormatted('to its config entry to mark it as optional. Example:'); - $errorOutput->writeLineFormatted('excludePaths:'); + $errorOutput->writeLineFormatted(''); + $errorOutput->writeLineFormatted('parameters:'); + $errorOutput->writeLineFormatted("\texcludePaths:"); foreach ($suggestOptional as $key => $suggestOptionalPaths) { - $errorOutput->writeLineFormatted(sprintf(' %s:', $key)); + $errorOutput->writeLineFormatted(sprintf("\t\t%s:", $key)); foreach ($suggestOptionalPaths as $suggestOptionalPath) { - $errorOutput->writeLineFormatted(sprintf(' - %s (?)', $suggestOptionalPath)); + $errorOutput->writeLineFormatted(sprintf("\t\t\t- %s (?)", $suggestOptionalPath)); } } $errorOutput->writeLineFormatted(''); From 2370e44cfba7b632cba217b9b173a6aeb912c33e Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 19 Mar 2025 11:43:24 +0100 Subject: [PATCH 4/4] Config path relative to projectConfigFile --- src/Command/CommandHelper.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index df078946fc..a142ecdca8 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -25,6 +25,7 @@ use PHPStan\File\FileExcluder; use PHPStan\File\FileFinder; use PHPStan\File\FileHelper; +use PHPStan\File\ParentDirectoryRelativePathHelper; use PHPStan\File\SimpleRelativePathHelper; use PHPStan\Internal\ComposerHelper; use PHPStan\Internal\DirectoryCreator; @@ -381,6 +382,10 @@ public static function begin( $suggestOptional = $e->getSuggestOptional(); if (count($suggestOptional) > 0) { + $baselinePathHelper = null; + if ($projectConfigFile !== null) { + $baselinePathHelper = new ParentDirectoryRelativePathHelper(dirname($projectConfigFile)); + } $errorOutput->writeLineFormatted('If the excluded path can sometimes exist, append (?)'); $errorOutput->writeLineFormatted('to its config entry to mark it as optional. Example:'); $errorOutput->writeLineFormatted(''); @@ -389,7 +394,12 @@ public static function begin( foreach ($suggestOptional as $key => $suggestOptionalPaths) { $errorOutput->writeLineFormatted(sprintf("\t\t%s:", $key)); foreach ($suggestOptionalPaths as $suggestOptionalPath) { - $errorOutput->writeLineFormatted(sprintf("\t\t\t- %s (?)", $suggestOptionalPath)); + if ($baselinePathHelper === null) { + $errorOutput->writeLineFormatted(sprintf("\t\t\t- %s (?)", $suggestOptionalPath)); + continue; + } + + $errorOutput->writeLineFormatted(sprintf("\t\t\t- %s (?)", $baselinePathHelper->getRelativePath($suggestOptionalPath))); } } $errorOutput->writeLineFormatted('');