diff --git a/build/phpstan.neon b/build/phpstan.neon deleted file mode 100644 index 1b1bf800f9..0000000000 --- a/build/phpstan.neon +++ /dev/null @@ -1,112 +0,0 @@ -includes: - - ../vendor/phpstan/phpstan-deprecation-rules/rules.neon - - ../vendor/phpstan/phpstan-nette/rules.neon - - ../vendor/phpstan/phpstan-phpunit/extension.neon - - ../vendor/phpstan/phpstan-phpunit/rules.neon - - ../vendor/phpstan/phpstan-strict-rules/rules.neon - - ../conf/bleedingEdge.neon - - ../phpstan-baseline.neon - - ../phpstan-baseline.php - - ignore-by-php-version.neon.php - - ignore-by-architecture.neon.php - -parameters: - level: 8 - paths: - - PHPStan - - ../src - - ../tests - bootstrapFiles: - - ../tests/phpstan-bootstrap.php - cache: - nodesByStringCountMax: 128 - checkUninitializedProperties: true - checkMissingCallableSignature: true - excludePaths: - - ../tests/*/data/* - - ../tests/tmp/* - - ../tests/PHPStan/Analyser/nsrt/* - - ../tests/PHPStan/Analyser/traits/* - - ../tests/notAutoloaded/* - - ../tests/PHPStan/Reflection/UnionTypesTest.php - - ../tests/PHPStan/Reflection/MixedTypeTest.php - - ../tests/e2e/magic-setter/* - - ../tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php - - ../tests/PHPStan/Command/IgnoredRegexValidatorTest.php - - ../src/Command/IgnoredRegexValidator.php - exceptions: - uncheckedExceptionClasses: - - 'PHPStan\ShouldNotHappenException' - - 'Symfony\Component\Console\Exception\InvalidArgumentException' - - 'PHPStan\BetterReflection\SourceLocator\Exception\InvalidFileLocation' - - 'PHPStan\BetterReflection\SourceLocator\Exception\InvalidArgumentException' - - 'Symfony\Component\Finder\Exception\DirectoryNotFoundException' - - 'InvalidArgumentException' - - 'PHPStan\DependencyInjection\ParameterNotFoundException' - - 'PHPStan\DependencyInjection\DuplicateIncludedFilesException' - - 'PHPStan\Analyser\UndefinedVariableException' - - 'RuntimeException' - - 'Nette\Neon\Exception' - - 'Nette\Utils\JsonException' - - 'PHPStan\File\CouldNotReadFileException' - - 'PHPStan\File\CouldNotWriteFileException' - - 'PHPStan\Parser\ParserErrorsException' - - 'ReflectionException' - - 'Nette\Utils\AssertionException' - - 'PHPStan\File\PathNotFoundException' - - 'PHPStan\Broker\ClassNotFoundException' - - 'PHPStan\Broker\FunctionNotFoundException' - - 'PHPStan\Broker\ConstantNotFoundException' - - 'PHPStan\Reflection\MissingMethodFromReflectionException' - - 'PHPStan\Reflection\MissingPropertyFromReflectionException' - - 'PHPStan\Reflection\MissingConstantFromReflectionException' - - 'PHPStan\Type\CircularTypeAliasDefinitionException' - - 'PHPStan\Broker\ClassAutoloadingException' - - 'LogicException' - - 'Error' - check: - missingCheckedExceptionInThrows: true - tooWideThrowType: true - ignoreErrors: - - '#^Dynamic call to static method PHPUnit\\Framework\\\S+\(\)\.$#' - - '#should be contravariant with parameter \$node \(PhpParser\\Node\) of method PHPStan\\Rules\\Rule::processNode\(\)$#' - - '#Variable property access on PhpParser\\Node#' - - '#Test::data[a-zA-Z0-9_]+\(\) return type has no value type specified in iterable type#' - - - message: '#Fetching class constant class of deprecated class DeprecatedAnnotations\\DeprecatedFoo.#' - path: ../tests/PHPStan/Reflection/Annotations/DeprecatedAnnotationsTest.php - - - message: '#Fetching class constant class of deprecated class DeprecatedAnnotations\\DeprecatedWithMultipleTags.#' - path: ../tests/PHPStan/Reflection/Annotations/DeprecatedAnnotationsTest.php - - - message: '#^Variable property access on T of PHPStan\\Rules\\RuleError\.$#' - path: ../src/Rules/RuleErrorBuilder.php - - - message: "#^Parameter \\#1 (?:\\$argument|\\$objectOrClass) of class ReflectionClass constructor expects class\\-string\\\\|PHPStan\\\\ExtensionInstaller\\\\GeneratedConfig, string given\\.$#" - count: 1 - path: ../src/Command/CommandHelper.php - - - message: "#^Parameter \\#1 (?:\\$argument|\\$objectOrClass) of class ReflectionClass constructor expects class\\-string\\\\|PHPStan\\\\ExtensionInstaller\\\\GeneratedConfig, string given\\.$#" - count: 1 - path: ../src/Diagnose/PHPStanDiagnoseExtension.php - - '#^Short ternary operator is not allowed#' - reportStaticMethodSignatures: true - tmpDir: %rootDir%/tmp - stubFiles: - - stubs/ReactChildProcess.stub - - stubs/ReactStreams.stub - - stubs/NetteDIContainer.stub - - stubs/PhpParserName.stub - -rules: - - PHPStan\Build\FinalClassRule - -services: - - - class: PHPStan\Build\ServiceLocatorDynamicReturnTypeExtension - tags: - - phpstan.broker.dynamicMethodReturnTypeExtension - - - class: PHPStan\Build\ContainerDynamicReturnTypeExtension - tags: - - phpstan.broker.dynamicMethodReturnTypeExtension diff --git a/build/phpstan.php b/build/phpstan.php new file mode 100644 index 0000000000..7dbf380e6c --- /dev/null +++ b/build/phpstan.php @@ -0,0 +1,135 @@ +::processNode\(\)$#', + '#Variable property access on PhpParser\\\Node#', + '#Test::data[a-zA-Z0-9_]+\(\) return type has no value type specified in iterable type#', + new IgnoreErrorsConfig( + message: '#Fetching class constant class of deprecated class DeprecatedAnnotations\\\DeprecatedFoo.#', + path: __DIR__ . '/../tests/PHPStan/Reflection/Annotations/DeprecatedAnnotationsTest.php', + ), + new IgnoreErrorsConfig( + message: '#Fetching class constant class of deprecated class DeprecatedAnnotations\\\DeprecatedWithMultipleTags.#', + path: __DIR__ . '/../tests/PHPStan/Reflection/Annotations/DeprecatedAnnotationsTest.php', + ), + new IgnoreErrorsConfig( + message: '#^Variable property access on T of PHPStan\\\Rules\\\RuleError\.$#', + path: __DIR__ . '/../src/Rules/RuleErrorBuilder.php', + ), + new IgnoreErrorsConfig( + message: '#^Parameter \#1 (?:\$argument|\$objectOrClass) of class ReflectionClass constructor expects class\-string\\|PHPStan\\\ExtensionInstaller\\\GeneratedConfig, string given\.$#', + count: 1, + path: __DIR__ . '/../src/Command/CommandHelper.php', + ), + new IgnoreErrorsConfig( + message: '#^Parameter \#1 (?:\$argument|\$objectOrClass) of class ReflectionClass constructor expects class\-string\\|PHPStan\\\ExtensionInstaller\\\GeneratedConfig, string given\.$#', + count: 1, + path: __DIR__ . '/../src/Diagnose/PHPStanDiagnoseExtension.php', + ), + '#^Short ternary operator is not allowed#', + ], + reportStaticMethodSignatures: true, + tmpDir: '%rootDir%/tmp', + stubFiles: [ + __DIR__ . '/stubs/ReactChildProcess.stub', + __DIR__ . '/stubs/ReactStreams.stub', + __DIR__ . '/stubs/NetteDIContainer.stub', + __DIR__ . '/stubs/PhpParserName.stub', + ], + ), + _extra: [ + 'services' => [ + [ + 'class' => 'PHPStan\Build\ServiceLocatorDynamicReturnTypeExtension', + 'tags' => ['phpstan.broker.dynamicMethodReturnTypeExtension'], + ], + [ + 'class' => 'PHPStan\Build\ContainerDynamicReturnTypeExtension', + 'tags' => ['phpstan.broker.dynamicMethodReturnTypeExtension'], + ], + ] + ] +); diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4da2feb998..2f767f4968 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1 +1 @@ -includes: [ build/phpstan.neon ] +includes: [ build/phpstan.php ] diff --git a/src/DependencyInjection/ContainerFactory.php b/src/DependencyInjection/ContainerFactory.php index 75c79d6678..6ca911112d 100644 --- a/src/DependencyInjection/ContainerFactory.php +++ b/src/DependencyInjection/ContainerFactory.php @@ -3,7 +3,6 @@ namespace PHPStan\DependencyInjection; use Nette\Bootstrap\Extensions\PhpExtension; -use Nette\DI\Config\Adapters\PhpAdapter; use Nette\DI\Definitions\Statement; use Nette\DI\Extensions\ExtensionsExtension; use Nette\DI\Helpers; diff --git a/src/DependencyInjection/LoaderFactory.php b/src/DependencyInjection/LoaderFactory.php index 6053ca5828..1dd8615c92 100644 --- a/src/DependencyInjection/LoaderFactory.php +++ b/src/DependencyInjection/LoaderFactory.php @@ -23,6 +23,7 @@ public function createLoader(): Loader $loader = new NeonLoader($this->fileHelper, $this->generateBaselineFile); $loader->addAdapter('dist', NeonAdapter::class); $loader->addAdapter('neon', NeonAdapter::class); + $loader->addAdapter('php', PhpAdapter::class); $loader->setParameters([ 'rootDir' => $this->rootDir, 'currentWorkingDirectory' => $this->currentWorkingDirectory, diff --git a/src/DependencyInjection/PhpAdapter.php b/src/DependencyInjection/PhpAdapter.php new file mode 100644 index 0000000000..a09facb214 --- /dev/null +++ b/src/DependencyInjection/PhpAdapter.php @@ -0,0 +1,29 @@ + + */ + public function load(string $file): array + { + $configuration = require $file; + if (is_array($configuration)) { + return $configuration; + } elseif ($configuration instanceof ProjectConfig) { + return $configuration->toArray(); + } + + throw new RuntimeException('Unexpected configuration type: ' . get_debug_type($configuration)); + } + +} diff --git a/src/DependencyInjection/ProjectConfig/Arrayable.php b/src/DependencyInjection/ProjectConfig/Arrayable.php new file mode 100644 index 0000000000..35d27d1657 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/Arrayable.php @@ -0,0 +1,11 @@ + */ + public function toArray(): array; + +} diff --git a/src/DependencyInjection/ProjectConfig/ArrayableTrait.php b/src/DependencyInjection/ProjectConfig/ArrayableTrait.php new file mode 100644 index 0000000000..da0f63aa63 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/ArrayableTrait.php @@ -0,0 +1,40 @@ + $value) { + if ($value === Undefined::Undefined) { + continue; + } + if ($value instanceof Arrayable) { + $config[$key] = $value->toArray(); + } elseif (is_array($value)) { + $config[$key] = array_map( + static fn (mixed $item): mixed => $item instanceof Arrayable ? $item->toArray() : $item, + $value, + ); + } else { + $config[$key] = $value; + } + } + return $config; + } + +} diff --git a/src/DependencyInjection/ProjectConfig/CacheConfig.php b/src/DependencyInjection/ProjectConfig/CacheConfig.php new file mode 100644 index 0000000000..7e775f315f --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/CacheConfig.php @@ -0,0 +1,16 @@ +|Undefined */ + private readonly array|Undefined $uncheckedExceptionRegexes = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $uncheckedExceptionClasses = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $checkedExceptionRegexes = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $checkedExceptionClasses = Undefined::Undefined, + private readonly CheckConfig|Undefined $check = Undefined::Undefined, + ) + { + } + +} diff --git a/src/DependencyInjection/ProjectConfig/ExcludePathsConfig.php b/src/DependencyInjection/ProjectConfig/ExcludePathsConfig.php new file mode 100644 index 0000000000..77e6105a98 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/ExcludePathsConfig.php @@ -0,0 +1,19 @@ +|Undefined */ + private readonly array|Undefined $analyse = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $analyseAndScan = Undefined::Undefined, + ) + { + } + +} diff --git a/src/DependencyInjection/ProjectConfig/FeatureTogglesConfig.php b/src/DependencyInjection/ProjectConfig/FeatureTogglesConfig.php new file mode 100644 index 0000000000..bbc761dea5 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/FeatureTogglesConfig.php @@ -0,0 +1,20 @@ + */ + private readonly array|Undefined $skipCheckGenericClasses = Undefined::Undefined, + private readonly bool|Undefined $stricterFunctionMap = Undefined::Undefined, + ) + { + } + +} diff --git a/src/DependencyInjection/ProjectConfig/IgnoreErrorsConfig.php b/src/DependencyInjection/ProjectConfig/IgnoreErrorsConfig.php new file mode 100644 index 0000000000..c72bf95f36 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/IgnoreErrorsConfig.php @@ -0,0 +1,24 @@ + */ + private readonly array|Undefined $messages = Undefined::Undefined, + private readonly int|Undefined $count = Undefined::Undefined, + private readonly string|Undefined $path = Undefined::Undefined, + /** @var list */ + private readonly array|Undefined $paths = Undefined::Undefined, + private readonly string|Undefined $identifier = Undefined::Undefined, + private readonly bool|Undefined $reportUnmatched = Undefined::Undefined, + ) + { + } + +} diff --git a/src/DependencyInjection/ProjectConfig/ParallelConfig.php b/src/DependencyInjection/ProjectConfig/ParallelConfig.php new file mode 100644 index 0000000000..e50a017a41 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/ParallelConfig.php @@ -0,0 +1,20 @@ +|Undefined */ + private readonly array|Undefined $bootstrapFiles = Undefined::Undefined, + private readonly ExcludePathsConfig|Undefined $excludePaths = Undefined::Undefined, + private readonly int|string|Undefined $level = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $paths = Undefined::Undefined, + private readonly ExceptionsConfig|Undefined $exceptions = Undefined::Undefined, + private readonly FeatureTogglesConfig|Undefined $featureToggles = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $fileExtensions = Undefined::Undefined, + private readonly bool|Undefined $checkAdvancedIsset = Undefined::Undefined, + private readonly bool|Undefined $reportAlwaysTrueInLastCondition = Undefined::Undefined, + private readonly bool|Undefined $checkClassCaseSensitivity = Undefined::Undefined, + private readonly bool|Undefined $checkExplicitMixed = Undefined::Undefined, + private readonly bool|Undefined $checkImplicitMixed = Undefined::Undefined, + private readonly bool|Undefined $checkFunctionArgumentTypes = Undefined::Undefined, + private readonly bool|Undefined $checkFunctionNameCase = Undefined::Undefined, + private readonly bool|Undefined $checkInternalClassCaseSensitivity = Undefined::Undefined, + private readonly bool|Undefined $checkMissingCallableSignature = Undefined::Undefined, + private readonly bool|Undefined $checkMissingVarTagTypehint = Undefined::Undefined, + private readonly bool|Undefined $checkArgumentsPassedByReference = Undefined::Undefined, + private readonly bool|Undefined $checkMaybeUndefinedVariables = Undefined::Undefined, + private readonly bool|Undefined $checkNullables = Undefined::Undefined, + private readonly bool|Undefined $checkThisOnly = Undefined::Undefined, + private readonly bool|Undefined $checkUnionTypes = Undefined::Undefined, + private readonly bool|Undefined $checkBenevolentUnionTypes = Undefined::Undefined, + private readonly bool|Undefined $checkExplicitMixedMissingReturn = Undefined::Undefined, + private readonly bool|Undefined $checkPhpDocMissingReturn = Undefined::Undefined, + private readonly bool|Undefined $checkPhpDocMethodSignatures = Undefined::Undefined, + private readonly bool|Undefined $checkExtraArguments = Undefined::Undefined, + private readonly bool|Undefined $checkMissingTypehints = Undefined::Undefined, + private readonly bool|Undefined $checkTooWideReturnTypesInProtectedAndPublicMethods = Undefined::Undefined, + private readonly bool|Undefined $checkUninitializedProperties = Undefined::Undefined, + private readonly bool|Undefined $checkDynamicProperties = Undefined::Undefined, + private readonly bool|Undefined $deprecationRulesInstalled = Undefined::Undefined, + private readonly bool|Undefined $inferPrivatePropertyTypeFromConstructor = Undefined::Undefined, + private readonly TipsConfig|Undefined $tips = Undefined::Undefined, + private readonly bool|Undefined $tipsOfTheDay = Undefined::Undefined, + private readonly bool|Undefined $reportMaybes = Undefined::Undefined, + private readonly bool|Undefined $reportMaybesInMethodSignatures = Undefined::Undefined, + private readonly bool|Undefined $reportMaybesInPropertyPhpDocTypes = Undefined::Undefined, + private readonly bool|Undefined $reportStaticMethodSignatures = Undefined::Undefined, + private readonly bool|Undefined $reportWrongPhpDocTypeInVarTag = Undefined::Undefined, + private readonly bool|Undefined $reportAnyTypeWideningInVarTag = Undefined::Undefined, + private readonly bool|Undefined $reportPossiblyNonexistentGeneralArrayOffset = Undefined::Undefined, + private readonly bool|Undefined $reportPossiblyNonexistentConstantArrayOffset = Undefined::Undefined, + private readonly bool|Undefined $checkMissingOverrideMethodAttribute = Undefined::Undefined, + private readonly ParallelConfig|Undefined $parallelConfig = Undefined::Undefined, + private readonly int|PhpVersionConfig|Undefined|null $phpVersion = Undefined::Undefined, + private readonly bool|Undefined $polluteScopeWithLoopInitialAssignments = Undefined::Undefined, + private readonly bool|Undefined $polluteScopeWithAlwaysIterableForeach = Undefined::Undefined, + private readonly bool|Undefined $polluteScopeWithBlock = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $propertyAlwaysWrittenTags = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $propertyAlwaysReadTags = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $additionalConstructors = Undefined::Undefined, + private readonly bool|Undefined $treatPhpDocTypesAsCertain = Undefined::Undefined, + private readonly bool|Undefined $usePathConstantsAsConstantString = Undefined::Undefined, + private readonly bool|Undefined $rememberPossiblyImpureFunctionValues = Undefined::Undefined, + private readonly bool|Undefined $reportMagicMethods = Undefined::Undefined, + private readonly bool|Undefined $reportMagicProperties = Undefined::Undefined, + /** @var list */ + private readonly array|Undefined $ignoreErrors = Undefined::Undefined, + private readonly int|Undefined $internalErrorsCountLimit = Undefined::Undefined, + private readonly CacheConfig|Undefined $cache = Undefined::Undefined, + private readonly bool|Undefined $reportUnmatchedIgnoredErrors = Undefined::Undefined, + /** @var array|Undefined */ + private readonly array|Undefined $typeAliases = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $universalObjectCratesClasses = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $stubFiles = Undefined::Undefined, + /** @var array>|Undefined */ + private readonly array|Undefined $earlyTerminatingMethodCalls = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $earlyTerminatingFunctionCalls = Undefined::Undefined, + private readonly string|Undefined $resultCachePath = Undefined::Undefined, + private readonly bool|Undefined $resultCacheChecksProjectExtensionFilesDependencies = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $dynamicConstantNames = Undefined::Undefined, + private readonly bool|Undefined|null $customRulesetUsed = Undefined::Undefined, + private readonly string|Undefined $rootDir = Undefined::Undefined, + private readonly string|Undefined $tmpDir = Undefined::Undefined, + private readonly string|Undefined $currentWorkingDirectory = Undefined::Undefined, + private readonly bool|Undefined $cliArgumentsVariablesRegistered = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $mixinExcludeClasses = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $scanFiles = Undefined::Undefined, + /** @var list|Undefined */ + private readonly array|Undefined $scanDirectories = Undefined::Undefined, + private readonly string|Undefined|null $editorUrl = Undefined::Undefined, + private readonly string|Undefined|null $editorUrlTitle = Undefined::Undefined, + private readonly string|Undefined|null $errorFormat = Undefined::Undefined, + private readonly ProConfig|Undefined $pro = Undefined::Undefined, + /** @var array|Undefined */ + private readonly array|Undefined $env = Undefined::Undefined, + private readonly string|Undefined $sysGetTempDir = Undefined::Undefined, + private readonly bool|Undefined $sourceLocatorPlaygroundMode = Undefined::Undefined, + /** @var array */ + // phpcs:ignore Consistence.NamingConventions.ValidVariableName.NotCamelCaps + private readonly array|Undefined $_extra = Undefined::Undefined, + ) + { + } + +} diff --git a/src/DependencyInjection/ProjectConfig/PhpVersionConfig.php b/src/DependencyInjection/ProjectConfig/PhpVersionConfig.php new file mode 100644 index 0000000000..d045deda0d --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/PhpVersionConfig.php @@ -0,0 +1,17 @@ + */ + private readonly array|Undefined $dnsServers = Undefined::Undefined, + private readonly string|Undefined $tmpDir = Undefined::Undefined, + ) + { + } + +} diff --git a/src/DependencyInjection/ProjectConfig/ProjectConfig.php b/src/DependencyInjection/ProjectConfig/ProjectConfig.php new file mode 100644 index 0000000000..aa5fa8d9a9 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/ProjectConfig.php @@ -0,0 +1,26 @@ + */ + private readonly array|Undefined $includes = Undefined::Undefined, + private readonly ParametersConfig|Undefined $parameters = Undefined::Undefined, + /** @var list>> */ + private readonly array|Undefined $rules = Undefined::Undefined, + /** @var array */ + // phpcs:ignore Consistence.NamingConventions.ValidVariableName.NotCamelCaps + private readonly array|Undefined $_extra = Undefined::Undefined, + ) + { + } + +} diff --git a/src/DependencyInjection/ProjectConfig/TipsConfig.php b/src/DependencyInjection/ProjectConfig/TipsConfig.php new file mode 100644 index 0000000000..6984191bd7 --- /dev/null +++ b/src/DependencyInjection/ProjectConfig/TipsConfig.php @@ -0,0 +1,16 @@ +