-
Notifications
You must be signed in to change notification settings - Fork 478
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Validate ignoreErrors regexes during DIC compilation (save time in ea…
…ch run)
- Loading branch information
1 parent
3b706cd
commit 692158c
Showing
14 changed files
with
326 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
src/DependencyInjection/InvalidIgnoredErrorPatternsException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 InvalidIgnoredErrorPatternsException 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; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
138 changes: 138 additions & 0 deletions
138
src/DependencyInjection/ValidateIgnoredErrorsExtension.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\DependencyInjection; | ||
|
||
use Hoa\Compiler\Llk\Llk; | ||
use Hoa\File\Read; | ||
use Nette\DI\CompilerExtension; | ||
use Nette\Utils\RegexpException; | ||
use Nette\Utils\Strings; | ||
use PHPStan\Analyser\NameScope; | ||
use PHPStan\Command\IgnoredRegexValidator; | ||
use PHPStan\PhpDoc\DirectTypeNodeResolverExtensionRegistryProvider; | ||
use PHPStan\PhpDoc\TypeNodeResolver; | ||
use PHPStan\PhpDoc\TypeNodeResolverExtensionRegistry; | ||
use PHPStan\PhpDoc\TypeStringResolver; | ||
use PHPStan\PhpDocParser\Lexer\Lexer; | ||
use PHPStan\PhpDocParser\Parser\ConstExprParser; | ||
use PHPStan\PhpDocParser\Parser\TypeParser; | ||
use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider; | ||
use PHPStan\Reflection\ReflectionProvider\DummyReflectionProvider; | ||
use PHPStan\Reflection\ReflectionProviderStaticAccessor; | ||
use PHPStan\Type\DirectTypeAliasResolverProvider; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\TypeAliasResolver; | ||
use function array_keys; | ||
use function array_map; | ||
use function count; | ||
use function implode; | ||
use function is_array; | ||
use function sprintf; | ||
|
||
class ValidateIgnoredErrorsExtension extends CompilerExtension | ||
{ | ||
|
||
/** | ||
* @throws InvalidIgnoredErrorPatternsException | ||
*/ | ||
public function loadConfiguration(): void | ||
{ | ||
$parser = Llk::load(new Read('hoa://Library/Regex/Grammar.pp')); | ||
$reflectionProvider = new DummyReflectionProvider(); | ||
ReflectionProviderStaticAccessor::registerInstance($reflectionProvider); | ||
$ignoredRegexValidator = new IgnoredRegexValidator( | ||
$parser, | ||
new TypeStringResolver( | ||
new Lexer(), | ||
new TypeParser(new ConstExprParser()), | ||
new TypeNodeResolver( | ||
new DirectTypeNodeResolverExtensionRegistryProvider( | ||
new class implements TypeNodeResolverExtensionRegistry { | ||
|
||
public function getExtensions(): array | ||
{ | ||
return []; | ||
} | ||
|
||
}, | ||
), | ||
new DirectReflectionProviderProvider($reflectionProvider), | ||
new DirectTypeAliasResolverProvider(new class implements TypeAliasResolver { | ||
|
||
public function hasTypeAlias(string $aliasName, ?string $classNameScope): bool | ||
{ | ||
return false; | ||
} | ||
|
||
public function resolveTypeAlias(string $aliasName, NameScope $nameScope): ?Type | ||
{ | ||
return null; | ||
} | ||
|
||
}), | ||
), | ||
), | ||
); | ||
|
||
$builder = $this->getContainerBuilder(); | ||
$ignoreErrors = $builder->parameters['ignoreErrors']; | ||
$errors = []; | ||
|
||
foreach ($ignoreErrors as $ignoreError) { | ||
try { | ||
if (is_array($ignoreError)) { | ||
if (isset($ignoreError['count'])) { | ||
continue; // ignoreError coming from baseline will be correct | ||
} | ||
$ignoreMessage = $ignoreError['message']; | ||
} else { | ||
$ignoreMessage = $ignoreError; | ||
} | ||
|
||
Strings::match('', $ignoreMessage); | ||
$validationResult = $ignoredRegexValidator->validate($ignoreMessage); | ||
$ignoredTypes = $validationResult->getIgnoredTypes(); | ||
if (count($ignoredTypes) > 0) { | ||
$errors[] = $this->createIgnoredTypesError($ignoreMessage, $ignoredTypes); | ||
} | ||
|
||
if ($validationResult->hasAnchorsInTheMiddle()) { | ||
$errors[] = $this->createAnchorInTheMiddleError($ignoreMessage); | ||
} | ||
|
||
if ($validationResult->areAllErrorsIgnored()) { | ||
$errors[] = sprintf("Ignored error %s has an unescaped '%s' which leads to ignoring all errors. Use '%s' instead.", $ignoreMessage, $validationResult->getWrongSequence(), $validationResult->getEscapedWrongSequence()); | ||
} | ||
} catch (RegexpException $e) { | ||
$errors[] = $e->getMessage(); | ||
} | ||
} | ||
|
||
if (count($errors) === 0) { | ||
return; | ||
} | ||
|
||
throw new InvalidIgnoredErrorPatternsException($errors); | ||
} | ||
|
||
/** | ||
* @param array<string, string> $ignoredTypes | ||
*/ | ||
private function createIgnoredTypesError(string $regex, array $ignoredTypes): string | ||
{ | ||
return sprintf( | ||
"Ignored error %s has an unescaped '|' which leads to ignoring more errors than intended. Use '\\|' instead.\n%s", | ||
$regex, | ||
sprintf( | ||
"It ignores all errors containing the following types:\n%s", | ||
implode("\n", array_map(static fn (string $typeDescription): string => sprintf('* %s', $typeDescription), array_keys($ignoredTypes))), | ||
), | ||
); | ||
} | ||
|
||
private function createAnchorInTheMiddleError(string $regex): string | ||
{ | ||
return sprintf("Ignored error %s has an unescaped anchor '$' in the middle. This leads to unintended behavior. Use '\\$' instead.", $regex); | ||
} | ||
|
||
} |
17 changes: 17 additions & 0 deletions
17
src/PhpDoc/DirectTypeNodeResolverExtensionRegistryProvider.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\PhpDoc; | ||
|
||
class DirectTypeNodeResolverExtensionRegistryProvider implements TypeNodeResolverExtensionRegistryProvider | ||
{ | ||
|
||
public function __construct(private TypeNodeResolverExtensionRegistry $registry) | ||
{ | ||
} | ||
|
||
public function getRegistry(): TypeNodeResolverExtensionRegistry | ||
{ | ||
return $this->registry; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.