Skip to content

Commit

Permalink
Bleeding edge - no implicit wildcard in FileExcluder
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 1, 2024
1 parent 35a2f57 commit e19e6e5
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 13 deletions.
4 changes: 0 additions & 4 deletions build/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,13 @@ parameters:
checkUninitializedProperties: true
checkMissingCallableSignature: true
excludePaths:
- ../src/Reflection/SignatureMap/functionMap.php
- ../src/Reflection/SignatureMap/functionMetadata.php
- ../tests/*/data/*
- ../tests/tmp/*
- ../tests/PHPStan/Analyser/nsrt/*
- ../tests/PHPStan/Analyser/traits/*
- ../tests/notAutoloaded/*
- ../tests/PHPStan/Generics/functions.php
- ../tests/PHPStan/Reflection/UnionTypesTest.php
- ../tests/PHPStan/Reflection/MixedTypeTest.php
- ../tests/PHPStan/Reflection/StaticTypeTest.php
- ../tests/e2e/magic-setter/*
- ../tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php
- ../tests/PHPStan/Command/IgnoredRegexValidatorTest.php
Expand Down
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ parameters:
printfArrayParameters: true
preciseMissingReturn: true
validatePregQuote: true
noImplicitWildcard: true
stubFiles:
- ../stubs/bleedingEdge/Rule.stub
4 changes: 4 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ parameters:
printfArrayParameters: false
preciseMissingReturn: false
validatePregQuote: false
noImplicitWildcard: false
fileExtensions:
- php
checkAdvancedIsset: false
Expand Down Expand Up @@ -269,6 +270,7 @@ extensions:
conditionalTags: PHPStan\DependencyInjection\ConditionalTagsExtension
parametersSchema: PHPStan\DependencyInjection\ParametersSchemaExtension
validateIgnoredErrors: PHPStan\DependencyInjection\ValidateIgnoredErrorsExtension
validateExcludePaths: PHPStan\DependencyInjection\ValidateExcludePathsExtension

rules:
- PHPStan\Rules\Debug\DumpTypeRule
Expand Down Expand Up @@ -676,6 +678,8 @@ services:

-
implement: PHPStan\File\FileExcluderRawFactory
arguments:
noImplicitWildcard: %featureToggles.noImplicitWildcard%

fileExcluderAnalyse:
class: PHPStan\File\FileExcluder
Expand Down
1 change: 1 addition & 0 deletions conf/parametersSchema.neon
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ parametersSchema:
printfArrayParameters: bool()
preciseMissingReturn: bool()
validatePregQuote: bool()
noImplicitWildcard: bool()
])
fileExtensions: listOf(string())
checkAdvancedIsset: bool()
Expand Down
3 changes: 2 additions & 1 deletion src/Analyser/Ignore/IgnoredError.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Nette\Utils\Strings;
use PHPStan\Analyser\Error;
use PHPStan\DependencyInjection\BleedingEdgeToggle;
use PHPStan\File\FileExcluder;
use PHPStan\File\FileHelper;
use PHPStan\ShouldNotHappenException;
Expand Down Expand Up @@ -85,7 +86,7 @@ public static function shouldIgnore(
}

if ($path !== null) {
$fileExcluder = new FileExcluder($fileHelper, [$path]);
$fileExcluder = new FileExcluder($fileHelper, [$path], BleedingEdgeToggle::isBleedingEdge());
$isExcluded = $fileExcluder->isExcludedFromAnalysing($error->getFilePath());
if (!$isExcluded && $error->getTraitFilePath() !== null) {
return $fileExcluder->isExcludedFromAnalysing($error->getTraitFilePath());
Expand Down
10 changes: 9 additions & 1 deletion src/Command/CommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use PHPStan\DependencyInjection\Container;
use PHPStan\DependencyInjection\ContainerFactory;
use PHPStan\DependencyInjection\DuplicateIncludedFilesException;
use PHPStan\DependencyInjection\InvalidExcludePathsException;
use PHPStan\DependencyInjection\InvalidIgnoredErrorPatternsException;
use PHPStan\DependencyInjection\LoaderFactory;
use PHPStan\ExtensionInstaller\GeneratedConfig;
Expand Down Expand Up @@ -355,6 +356,13 @@ public static function begin(
$errorOutput->writeLineFormatted('');
}
throw new InceptionNotSuccessfulException();
} catch (InvalidExcludePathsException $e) {
$errorOutput->writeLineFormatted(sprintf('<error>Invalid %s in excludePaths:</error>', count($e->getErrors()) === 1 ? 'entry' : 'entries'));
foreach ($e->getErrors() as $error) {
$errorOutput->writeLineFormatted($error);
$errorOutput->writeLineFormatted('');
}
throw new InceptionNotSuccessfulException();
} catch (ValidationException $e) {
foreach ($e->getMessages() as $message) {
$errorOutput->writeLineFormatted('<error>Invalid configuration:</error>');
Expand Down Expand Up @@ -583,7 +591,7 @@ public static function begin(

$pathRoutingParser->setAnalysedFiles($files);

$stubFilesExcluder = new FileExcluder($currentWorkingDirectoryFileHelper, $stubFilesProvider->getProjectStubFiles());
$stubFilesExcluder = new FileExcluder($currentWorkingDirectoryFileHelper, $stubFilesProvider->getProjectStubFiles(), true);

$files = array_values(array_filter($files, static fn (string $file) => !$stubFilesExcluder->isExcludedFromAnalysing($file)));

Expand Down
27 changes: 27 additions & 0 deletions src/DependencyInjection/InvalidExcludePathsException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);

namespace PHPStan\DependencyInjection;

use Exception;
use function implode;

class InvalidExcludePathsException extends Exception
{

/**
* @param string[] $errors
*/
public function __construct(private array $errors)
{
parent::__construct(implode("\n", $this->errors));
}

/**
* @return string[]
*/
public function getErrors(): array
{
return $this->errors;
}

}
2 changes: 1 addition & 1 deletion src/DependencyInjection/NeonAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
class NeonAdapter implements Adapter
{

public const CACHE_KEY = 'v25-nette-di-again';
public const CACHE_KEY = 'v26-no-implicit-wildcard';

private const PREVENT_MERGING_SUFFIX = '!';

Expand Down
68 changes: 68 additions & 0 deletions src/DependencyInjection/ValidateExcludePathsExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php declare(strict_types = 1);

namespace PHPStan\DependencyInjection;

use Nette\DI\CompilerExtension;
use PHPStan\File\FileExcluder;
use function array_key_exists;
use function array_merge;
use function array_unique;
use function count;
use function is_dir;
use function is_file;
use function sprintf;

class ValidateExcludePathsExtension extends CompilerExtension
{

/**
* @throws InvalidExcludePathsException
*/
public function loadConfiguration(): void
{
$builder = $this->getContainerBuilder();
if (!$builder->parameters['__validate']) {
return;
}

$excludePaths = $builder->parameters['excludePaths'];
if ($excludePaths === null) {
return;
}

$noImplicitWildcard = $builder->parameters['featureToggles']['noImplicitWildcard'];
if (!$noImplicitWildcard) {
return;
}

$paths = [];
if (array_key_exists('analyse', $excludePaths)) {
$paths = $excludePaths['analyse'];
}
if (array_key_exists('analyseAndScan', $excludePaths)) {
$paths = array_merge($paths, $excludePaths['analyseAndScan']);
}

$errors = [];
foreach (array_unique($paths) as $path) {
if (is_dir($path)) {
continue;
}
if (is_file($path)) {
continue;
}
if (FileExcluder::isFnmatchPattern($path)) {
continue;
}

$errors[] = sprintf('Path %s is neither a directory, nor a file path, nor a fnmatch pattern.', $path);
}

if (count($errors) === 0) {
return;
}

throw new InvalidExcludePathsException($errors);
}

}
35 changes: 35 additions & 0 deletions src/DependencyInjection/ValidateIgnoredErrorsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Analyser\NameScope;
use PHPStan\Command\IgnoredRegexValidator;
use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
use PHPStan\File\FileExcluder;
use PHPStan\Php\PhpVersion;
use PHPStan\PhpDoc\DirectTypeNodeResolverExtensionRegistryProvider;
use PHPStan\PhpDoc\TypeNodeResolver;
Expand All @@ -34,6 +35,8 @@
use function count;
use function implode;
use function is_array;
use function is_dir;
use function is_file;
use function sprintf;
use const PHP_VERSION_ID;

Expand All @@ -55,6 +58,8 @@ public function loadConfiguration(): void
return;
}

$noImplicitWildcard = $builder->parameters['featureToggles']['noImplicitWildcard'];

/** @throws void */
$parser = Llk::load(new Read(__DIR__ . '/../../resources/RegexGrammar.pp'));
$reflectionProvider = new DummyReflectionProvider();
Expand Down Expand Up @@ -131,6 +136,36 @@ public function getRegistry(): OperatorTypeSpecifyingExtensionRegistry
}
}

if ($noImplicitWildcard) {
foreach ($ignoreErrors as $ignoreError) {
if (!is_array($ignoreError)) {
continue;
}

if (isset($ignoreError['path'])) {
$ignorePaths = [$ignoreError['path']];
} elseif (isset($ignoreError['paths'])) {
$ignorePaths = $ignoreError['paths'];
} else {
continue;
}

foreach ($ignorePaths as $ignorePath) {
if (is_dir($ignorePath)) {
continue;
}
if (is_file($ignorePath)) {
continue;
}
if (FileExcluder::isFnmatchPattern($ignorePath)) {
continue;
}

$errors[] = sprintf('Path %s is neither a directory, nor a file path, nor a fnmatch pattern.', $ignorePath);
}
}
}

if (count($errors) === 0) {
return;
}
Expand Down
49 changes: 45 additions & 4 deletions src/File/FileExcluder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use function fnmatch;
use function in_array;
use function is_dir;
use function is_file;
use function preg_match;
use function str_starts_with;
use function strlen;
Expand All @@ -15,12 +17,26 @@ class FileExcluder
{

/**
* Directories to exclude from analysing
* Paths to exclude from analysing
*
* @var string[]
*/
private array $literalAnalyseExcludes = [];

/**
* Directories to exclude from analysing
*
* @var string[]
*/
private array $literalAnalyseDirectoryExcludes = [];

/**
* Files to exclude from analysing
*
* @var string[]
*/
private array $literalAnalyseFilesExcludes = [];

/**
* fnmatch() patterns to use for excluding files and directories from analysing
* @var string[]
Expand All @@ -35,6 +51,7 @@ class FileExcluder
public function __construct(
FileHelper $fileHelper,
array $analyseExcludes,
private bool $noImplicitWildcard,
)
{
foreach ($analyseExcludes as $exclude) {
Expand All @@ -47,10 +64,22 @@ public function __construct(
$normalized .= DIRECTORY_SEPARATOR;
}

if ($this->isFnmatchPattern($normalized)) {
if (self::isFnmatchPattern($normalized)) {
$this->fnmatchAnalyseExcludes[] = $normalized;
} else {
$this->literalAnalyseExcludes[] = $fileHelper->absolutizePath($normalized);
if ($this->noImplicitWildcard) {
if (is_file($normalized)) {
$this->literalAnalyseFilesExcludes[] = $normalized;
} elseif (is_dir($normalized)) {
if (!$trailingDirSeparator) {
$normalized .= DIRECTORY_SEPARATOR;
}

$this->literalAnalyseDirectoryExcludes[] = $normalized;
}
} else {
$this->literalAnalyseExcludes[] = $fileHelper->absolutizePath($normalized);
}
}
}

Expand All @@ -69,6 +98,18 @@ public function isExcludedFromAnalysing(string $file): bool
return true;
}
}
if ($this->noImplicitWildcard) {
foreach ($this->literalAnalyseDirectoryExcludes as $exclude) {
if (str_starts_with($file, $exclude)) {
return true;
}
}
foreach ($this->literalAnalyseFilesExcludes as $exclude) {
if ($file === $exclude) {
return true;
}
}
}
foreach ($this->fnmatchAnalyseExcludes as $exclude) {
if (fnmatch($exclude, $file, $this->fnmatchFlags)) {
return true;
Expand All @@ -78,7 +119,7 @@ public function isExcludedFromAnalysing(string $file): bool
return false;
}

private function isFnmatchPattern(string $path): bool
public static function isFnmatchPattern(string $path): bool
{
return preg_match('~[*?[\]]~', $path) > 0;
}
Expand Down
Loading

0 comments on commit e19e6e5

Please sign in to comment.