Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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