Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ parameters:
- 'PHPStan\Reflection\MissingPropertyFromReflectionException'
- 'PHPStan\Reflection\MissingConstantFromReflectionException'
- 'PHPStan\Type\CircularTypeAliasDefinitionException'
- 'PHPStan\Reflection\MissingStaticAccessorInstanceException'
- 'LogicException'
- 'Error'
check:
Expand Down
2 changes: 0 additions & 2 deletions src/DependencyInjection/ContainerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\ObjectType;
use function array_diff_key;
use function array_intersect;
use function array_key_exists;
Expand Down Expand Up @@ -195,7 +194,6 @@ public static function postInitializeContainer(Container $container): void

ReflectionProviderStaticAccessor::registerInstance($container->getByType(ReflectionProvider::class));
PhpVersionStaticAccessor::registerInstance($container->getByType(PhpVersion::class));
ObjectType::resetCaches();
$container->getService('typeSpecifier');

BleedingEdgeToggle::setBleedingEdge($container->getParameter('featureToggles')['bleedingEdge']);
Expand Down
198 changes: 111 additions & 87 deletions src/DependencyInjection/ValidateIgnoredErrorsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPStan\PhpDocParser\ParserConfig;
use PHPStan\Reflection\InitializerExprTypeResolver;
use PHPStan\Reflection\MissingStaticAccessorInstanceException;
use PHPStan\Reflection\PhpVersionStaticAccessor;
use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider;
use PHPStan\Reflection\ReflectionProvider\DummyReflectionProvider;
Expand Down Expand Up @@ -66,120 +67,143 @@ public function loadConfiguration(): void
$parser = Llk::load(new Read(__DIR__ . '/../../resources/RegexGrammar.pp'));
$reflectionProvider = new DummyReflectionProvider();
$reflectionProviderProvider = new DirectReflectionProviderProvider($reflectionProvider);

try {
$originalReflectionProvider = ReflectionProviderStaticAccessor::getInstance();
} catch (MissingStaticAccessorInstanceException) {
$originalReflectionProvider = null;
}

try {
$originalPhpVersion = PhpVersionStaticAccessor::getInstance();
} catch (MissingStaticAccessorInstanceException) {
$originalPhpVersion = null;
}

ReflectionProviderStaticAccessor::registerInstance($reflectionProvider);
PhpVersionStaticAccessor::registerInstance(new PhpVersion(PHP_VERSION_ID));
$composerPhpVersionFactory = new ComposerPhpVersionFactory([]);
$constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, $composerPhpVersionFactory, null);

$phpDocParserConfig = new ParserConfig([]);
$ignoredRegexValidator = new IgnoredRegexValidator(
$parser,
new TypeStringResolver(
new Lexer($phpDocParserConfig),
new TypeParser($phpDocParserConfig, new ConstExprParser($phpDocParserConfig)),
new TypeNodeResolver(
new DirectTypeNodeResolverExtensionRegistryProvider(
new class implements TypeNodeResolverExtensionRegistry {

public function getExtensions(): array

try {
$composerPhpVersionFactory = new ComposerPhpVersionFactory([]);
$constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, $composerPhpVersionFactory, null);

$phpDocParserConfig = new ParserConfig([]);
$ignoredRegexValidator = new IgnoredRegexValidator(
$parser,
new TypeStringResolver(
new Lexer($phpDocParserConfig),
new TypeParser($phpDocParserConfig, new ConstExprParser($phpDocParserConfig)),
new TypeNodeResolver(
new DirectTypeNodeResolverExtensionRegistryProvider(
new class implements TypeNodeResolverExtensionRegistry {

public function getExtensions(): array
{
return [];
}

},
),
$reflectionProviderProvider,
new DirectTypeAliasResolverProvider(new class implements TypeAliasResolver {

public function hasTypeAlias(string $aliasName, ?string $classNameScope): bool
{
return [];
return false;
}

},
),
$reflectionProviderProvider,
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;
}
public function resolveTypeAlias(string $aliasName, NameScope $nameScope): ?Type
{
return null;
}

}),
$constantResolver,
new InitializerExprTypeResolver($constantResolver, $reflectionProviderProvider, new PhpVersion(PHP_VERSION_ID), new class implements OperatorTypeSpecifyingExtensionRegistryProvider {
}),
$constantResolver,
new InitializerExprTypeResolver($constantResolver, $reflectionProviderProvider, new PhpVersion(PHP_VERSION_ID), new class implements OperatorTypeSpecifyingExtensionRegistryProvider {

public function getRegistry(): OperatorTypeSpecifyingExtensionRegistry
{
return new OperatorTypeSpecifyingExtensionRegistry([]);
}
public function getRegistry(): OperatorTypeSpecifyingExtensionRegistry
{
return new OperatorTypeSpecifyingExtensionRegistry([]);
}

}, new OversizedArrayBuilder(), true),
}, new OversizedArrayBuilder(), true),
),
),
),
);
);

$errors = [];
foreach ($ignoreErrors as $ignoreError) {
if (is_array($ignoreError)) {
if (isset($ignoreError['count'])) {
continue; // ignoreError coming from baseline will be correct
}
if (isset($ignoreError['messages'])) {
$ignoreMessages = $ignoreError['messages'];
} elseif (isset($ignoreError['message'])) {
$ignoreMessages = [$ignoreError['message']];
$errors = [];
foreach ($ignoreErrors as $ignoreError) {
if (is_array($ignoreError)) {
if (isset($ignoreError['count'])) {
continue; // ignoreError coming from baseline will be correct
}
if (isset($ignoreError['messages'])) {
$ignoreMessages = $ignoreError['messages'];
} elseif (isset($ignoreError['message'])) {
$ignoreMessages = [$ignoreError['message']];
} else {
continue;
}
} else {
continue;
$ignoreMessages = [$ignoreError];
}
} else {
$ignoreMessages = [$ignoreError];
}

foreach ($ignoreMessages as $ignoreMessage) {
$error = $this->validateMessage($ignoredRegexValidator, $ignoreMessage);
if ($error === null) {
continue;
foreach ($ignoreMessages as $ignoreMessage) {
$error = $this->validateMessage($ignoredRegexValidator, $ignoreMessage);
if ($error === null) {
continue;
}
$errors[] = $error;
}
$errors[] = $error;
}
}

$reportUnmatched = (bool) $builder->parameters['reportUnmatchedIgnoredErrors'];
$reportUnmatched = (bool) $builder->parameters['reportUnmatchedIgnoredErrors'];

if ($reportUnmatched) {
foreach ($ignoreErrors as $ignoreError) {
if (!is_array($ignoreError)) {
continue;
}
if ($reportUnmatched) {
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;
}
if (isset($ignoreError['path'])) {
$ignorePaths = [$ignoreError['path']];
} elseif (isset($ignoreError['paths'])) {
$ignorePaths = $ignoreError['paths'];
} else {
continue;
}

foreach ($ignorePaths as $ignorePath) {
if (FileExcluder::isAbsolutePath($ignorePath)) {
if (is_dir($ignorePath)) {
continue;
foreach ($ignorePaths as $ignorePath) {
if (FileExcluder::isAbsolutePath($ignorePath)) {
if (is_dir($ignorePath)) {
continue;
}
if (is_file($ignorePath)) {
continue;
}
}
if (is_file($ignorePath)) {
if (FileExcluder::isFnmatchPattern($ignorePath)) {
continue;
}
}
if (FileExcluder::isFnmatchPattern($ignorePath)) {
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);
}
}
}
}

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

throw new InvalidIgnoredErrorPatternsException($errors);
throw new InvalidIgnoredErrorPatternsException($errors);
} finally {
if ($originalReflectionProvider !== null) {
ReflectionProviderStaticAccessor::registerInstance($originalReflectionProvider);
}
if ($originalPhpVersion !== null) {
PhpVersionStaticAccessor::registerInstance($originalPhpVersion);
}
Comment on lines +200 to +205
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as the code stands it looks like PhpVersionStaticAccessor::registerInstance() might be called without also calling ReflectionProviderStaticAccessor::registerInstance, which might mean PhpVersionStaticAccessor::registerInstance() should also clear the cache

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm doing ObjectType::resetCaches only in ReflectionProvider accessor because that's what it depends on. I don't think there's any cache depending on PhpVersionStaticAccessor?

}
}

private function validateMessage(IgnoredRegexValidator $ignoredRegexValidator, string $ignoreMessage): ?string
Expand Down
83 changes: 42 additions & 41 deletions src/PhpDoc/StubValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@
use PHPStan\Rules\Properties\MissingPropertyTypehintRule;
use PHPStan\Rules\Registry as RuleRegistry;
use PHPStan\Type\FileTypeMapper;
use PHPStan\Type\ObjectType;
use Throwable;
use function array_fill_keys;
use function count;
Expand Down Expand Up @@ -125,56 +124,58 @@ public function validate(array $stubFiles, bool $debug): array

$originalReflectionProvider = ReflectionProviderStaticAccessor::getInstance();
$originalPhpVersion = PhpVersionStaticAccessor::getInstance();
$container = $this->derivativeContainerFactory->create([
__DIR__ . '/../../conf/config.stubValidator.neon',
]);

$ruleRegistry = $this->getRuleRegistry($container);
$collectorRegistry = $this->getCollectorRegistry($container);
try {
$container = $this->derivativeContainerFactory->create([
__DIR__ . '/../../conf/config.stubValidator.neon',
]);

$fileAnalyser = $container->getByType(FileAnalyser::class);
$ruleRegistry = $this->getRuleRegistry($container);
$collectorRegistry = $this->getCollectorRegistry($container);

$nodeScopeResolver = $container->getByType(NodeScopeResolver::class);
$nodeScopeResolver->setAnalysedFiles($stubFiles);
$fileAnalyser = $container->getByType(FileAnalyser::class);

$pathRoutingParser = $container->getService('pathRoutingParser');
$pathRoutingParser->setAnalysedFiles($stubFiles);
$nodeScopeResolver = $container->getByType(NodeScopeResolver::class);
$nodeScopeResolver->setAnalysedFiles($stubFiles);

$analysedFiles = array_fill_keys($stubFiles, true);
$pathRoutingParser = $container->getService('pathRoutingParser');
$pathRoutingParser->setAnalysedFiles($stubFiles);

$errors = [];
foreach ($stubFiles as $stubFile) {
try {
$tmpErrors = $fileAnalyser->analyseFile(
$stubFile,
$analysedFiles,
$ruleRegistry,
$collectorRegistry,
static function (): void {
},
)->getErrors();
foreach ($tmpErrors as $tmpError) {
$errors[] = $tmpError->withoutTip()->doNotIgnore();
}
} catch (Throwable $e) {
if ($debug) {
throw $e;
}
$analysedFiles = array_fill_keys($stubFiles, true);

$errors = [];
foreach ($stubFiles as $stubFile) {
try {
$tmpErrors = $fileAnalyser->analyseFile(
$stubFile,
$analysedFiles,
$ruleRegistry,
$collectorRegistry,
static function (): void {
},
)->getErrors();
foreach ($tmpErrors as $tmpError) {
$errors[] = $tmpError->withoutTip()->doNotIgnore();
}
} catch (Throwable $e) {
if ($debug) {
throw $e;
}

$internalErrorMessage = sprintf('Internal error: %s', $e->getMessage());
$errors[] = (new Error($internalErrorMessage, $stubFile, canBeIgnored: $e))
->withIdentifier('phpstan.internal')
->withMetadata([
InternalError::STACK_TRACE_METADATA_KEY => InternalError::prepareTrace($e),
InternalError::STACK_TRACE_AS_STRING_METADATA_KEY => $e->getTraceAsString(),
]);
$internalErrorMessage = sprintf('Internal error: %s', $e->getMessage());
$errors[] = (new Error($internalErrorMessage, $stubFile, canBeIgnored: $e))
->withIdentifier('phpstan.internal')
->withMetadata([
InternalError::STACK_TRACE_METADATA_KEY => InternalError::prepareTrace($e),
InternalError::STACK_TRACE_AS_STRING_METADATA_KEY => $e->getTraceAsString(),
]);
}
}
} finally {
ReflectionProviderStaticAccessor::registerInstance($originalReflectionProvider);
PhpVersionStaticAccessor::registerInstance($originalPhpVersion);
}

ReflectionProviderStaticAccessor::registerInstance($originalReflectionProvider);
PhpVersionStaticAccessor::registerInstance($originalPhpVersion);
ObjectType::resetCaches();

return $errors;
}

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

namespace PHPStan\Reflection;

use Exception;

final class MissingStaticAccessorInstanceException extends Exception
{

}
3 changes: 1 addition & 2 deletions src/Reflection/PhpVersionStaticAccessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace PHPStan\Reflection;

use PHPStan\Php\PhpVersion;
use PHPStan\ShouldNotHappenException;

final class PhpVersionStaticAccessor
{
Expand All @@ -22,7 +21,7 @@ public static function registerInstance(PhpVersion $phpVersion): void
public static function getInstance(): PhpVersion
{
if (self::$instance === null) {
throw new ShouldNotHappenException();
throw new MissingStaticAccessorInstanceException();
}
return self::$instance;
}
Expand Down
Loading
Loading