diff --git a/config/phpstan/static-reflection.neon b/config/phpstan/static-reflection.neon index 8b71677c6c8..740ca36b7dd 100644 --- a/config/phpstan/static-reflection.neon +++ b/config/phpstan/static-reflection.neon @@ -3,15 +3,10 @@ parameters: featureToggles: disableRuntimeReflectionProvider: false -conditionalTags: - PhpParser\NodeVisitor\ParentConnectingVisitor: - phpstan.parser.richParserNodeVisitor: true - services: - Rector\NodeTypeResolver\Reflection\BetterReflection\RectorBetterReflectionSourceLocatorFactory - Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator\IntermediateSourceLocator - Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider - - PhpParser\NodeVisitor\ParentConnectingVisitor # basically decorates native PHPStan source locator with a dynamic source locator that is also available in Rector DI betterReflectionSourceLocator: diff --git a/packages/NodeTypeResolver/NodeScopeAndMetadataDecorator.php b/packages/NodeTypeResolver/NodeScopeAndMetadataDecorator.php index 44c79c154ec..90b82b52fc2 100644 --- a/packages/NodeTypeResolver/NodeScopeAndMetadataDecorator.php +++ b/packages/NodeTypeResolver/NodeScopeAndMetadataDecorator.php @@ -8,6 +8,7 @@ use PhpParser\NodeTraverser; use PhpParser\NodeVisitor\CloningVisitor; use PhpParser\NodeVisitor\ParentConnectingVisitor; use Rector\Core\PhpParser\NodeTraverser\FileWithoutNamespaceNodeTraverser; +use Rector\Core\PHPStan\NodeVisitor\UnreachableStatementNodeVisitor; use Rector\Core\ValueObject\Application\File; use Rector\NodeTypeResolver\NodeVisitor\FunctionLikeParamArgPositionNodeVisitor; use Rector\NodeTypeResolver\PHPStan\Scope\PHPStanNodeScopeResolver; @@ -28,7 +29,7 @@ final class NodeScopeAndMetadataDecorator * @var \PhpParser\NodeTraverser */ private $nodeTraverser; - public function __construct(CloningVisitor $cloningVisitor, PHPStanNodeScopeResolver $phpStanNodeScopeResolver, ParentConnectingVisitor $parentConnectingVisitor, FunctionLikeParamArgPositionNodeVisitor $functionLikeParamArgPositionNodeVisitor, FileWithoutNamespaceNodeTraverser $fileWithoutNamespaceNodeTraverser) + public function __construct(CloningVisitor $cloningVisitor, PHPStanNodeScopeResolver $phpStanNodeScopeResolver, ParentConnectingVisitor $parentConnectingVisitor, FunctionLikeParamArgPositionNodeVisitor $functionLikeParamArgPositionNodeVisitor, UnreachableStatementNodeVisitor $unreachableStatementNodeVisitor, FileWithoutNamespaceNodeTraverser $fileWithoutNamespaceNodeTraverser) { $this->phpStanNodeScopeResolver = $phpStanNodeScopeResolver; $this->fileWithoutNamespaceNodeTraverser = $fileWithoutNamespaceNodeTraverser; @@ -38,6 +39,7 @@ final class NodeScopeAndMetadataDecorator // this one has to be run again to re-connect parent nodes with new attributes $this->nodeTraverser->addVisitor($parentConnectingVisitor); $this->nodeTraverser->addVisitor($functionLikeParamArgPositionNodeVisitor); + $this->nodeTraverser->addVisitor($unreachableStatementNodeVisitor); } /** * @param Stmt[] $stmts diff --git a/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php index fad97c94c9e..a89a69372d1 100644 --- a/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ b/packages/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php @@ -33,8 +33,6 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\NullableType; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\Declare_; use PhpParser\Node\Stmt\Enum_; use PhpParser\Node\Stmt\EnumCase; use PhpParser\Node\Stmt\Expression; @@ -64,7 +62,6 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\TypeCombinator; use Rector\Caching\Detector\ChangedFilesDetector; use Rector\Caching\FileSystem\DependencyResolver; -use Rector\Core\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Core\Exception\ShouldNotHappenException; use Rector\Core\NodeAnalyzer\ClassAnalyzer; use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace; @@ -300,9 +297,6 @@ final class PHPStanNodeScopeResolver } } } - if ($node instanceof Stmt) { - $this->setChildOfUnreachableStatementNodeAttribute($node, $mutatingScope); - } // special case for unreachable nodes if ($node instanceof UnreachableStatementNode) { $this->processUnreachableStatementNode($node, $filePath, $mutatingScope); @@ -369,22 +363,6 @@ final class PHPStanNodeScopeResolver $expr = $expr->expr; } } - private function setChildOfUnreachableStatementNodeAttribute(Stmt $stmt, MutatingScope $mutatingScope) : void - { - if (!$stmt instanceof StmtsAwareInterface && !$stmt instanceof ClassLike && !$stmt instanceof Declare_) { - return; - } - if ($stmt->getAttribute(AttributeKey::IS_UNREACHABLE) !== \true) { - return; - } - if ($stmt->stmts === null) { - return; - } - foreach ($stmt->stmts as $childStmt) { - $childStmt->setAttribute(AttributeKey::IS_UNREACHABLE, \true); - $childStmt->setAttribute(AttributeKey::SCOPE, $mutatingScope); - } - } private function processArray(Array_ $array, MutatingScope $mutatingScope) : void { foreach ($array->items as $arrayItem) { @@ -441,19 +419,6 @@ final class PHPStanNodeScopeResolver $originalStmt->setAttribute(AttributeKey::IS_UNREACHABLE, \true); $originalStmt->setAttribute(AttributeKey::SCOPE, $mutatingScope); $this->processNodes([$originalStmt], $filePath, $mutatingScope); - $parentNode = $unreachableStatementNode->getAttribute(AttributeKey::PARENT_NODE); - if (!$parentNode instanceof StmtsAwareInterface && !$parentNode instanceof ClassLike && !$parentNode instanceof Declare_) { - return; - } - $stmtKey = $unreachableStatementNode->getAttribute(AttributeKey::STMT_KEY); - $totalKeys = $parentNode->stmts === null ? 0 : \count($parentNode->stmts); - for ($key = $stmtKey + 1; $key < $totalKeys; ++$key) { - if (!isset($parentNode->stmts[$key])) { - continue; - } - $parentNode->stmts[$key]->setAttribute(AttributeKey::IS_UNREACHABLE, \true); - $this->processNodes([$parentNode->stmts[$key]], $filePath, $mutatingScope); - } } private function processProperty(Property $property, MutatingScope $mutatingScope) : void { diff --git a/rules/Php56/NodeAnalyzer/UndefinedVariableResolver.php b/rules/Php56/NodeAnalyzer/UndefinedVariableResolver.php index c1fb2d923db..b227562c436 100644 --- a/rules/Php56/NodeAnalyzer/UndefinedVariableResolver.php +++ b/rules/Php56/NodeAnalyzer/UndefinedVariableResolver.php @@ -73,15 +73,15 @@ final class UndefinedVariableResolver if ($node instanceof FunctionLike && !$node instanceof ArrowFunction) { return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } - if ($node instanceof Foreach_) { + if ($node instanceof Foreach_ || $node instanceof Case_) { // handled above return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } - if ($node instanceof Case_) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } if ($node instanceof Stmt) { $currentStmt = $node; + if ($currentStmt->getAttribute(AttributeKey::IS_UNREACHABLE) === \true) { + return NodeTraverser::STOP_TRAVERSAL; + } } if (!$node instanceof Variable) { $checkedVariables = $this->resolveCheckedVariables($node, $checkedVariables); @@ -91,14 +91,8 @@ final class UndefinedVariableResolver if ($node->name instanceof Variable) { return NodeTraverser::STOP_TRAVERSAL; } - if ($node->getAttribute(AttributeKey::IS_BEING_ASSIGNED) === \true) { - return null; - } $variableName = (string) $this->nodeNameResolver->getName($node); - if ($this->shouldSkipVariable($node, $variableName, $checkedVariables)) { - return null; - } - if ($this->hasVariableTypeOrCurrentStmtUnreachable($node, $variableName, $currentStmt)) { + if ($this->shouldSkipVariable($node, $variableName, $checkedVariables, $currentStmt)) { return null; } /** @var string $variableName */ @@ -187,7 +181,7 @@ final class UndefinedVariableResolver /** * @param string[] $checkedVariables */ - private function shouldSkipVariable(Variable $variable, string $variableName, array &$checkedVariables) : bool + private function shouldSkipVariable(Variable $variable, string $variableName, array &$checkedVariables, ?Stmt $currentStmt) : bool { $variableName = $this->nodeNameResolver->getName($variable); // skip $this, as probably in outer scope @@ -203,7 +197,13 @@ final class UndefinedVariableResolver if ($this->variableAnalyzer->isStaticOrGlobal($variable)) { return \true; } - return \in_array($variableName, $checkedVariables, \true); + if (\in_array($variableName, $checkedVariables, \true)) { + return \true; + } + if ($variable->getAttribute(AttributeKey::IS_BEING_ASSIGNED) === \true) { + return \true; + } + return $this->hasVariableTypeOrCurrentStmtUnreachable($variable, $variableName, $currentStmt); } private function isDifferentWithOriginalNodeOrNoScope(Variable $variable) : bool { diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index 9539d265f49..8dd2b656cc4 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -19,12 +19,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = '708cc4fb49562e19a98849abd50216048b1f48ba'; + public const PACKAGE_VERSION = '93a4b2b15eec15c506ae55c25b23a40dd0390cff'; /** * @api * @var string */ - public const RELEASE_DATE = '2023-07-04 00:25:46'; + public const RELEASE_DATE = '2023-07-04 15:32:33'; /** * @var int */ diff --git a/src/PHPStan/NodeVisitor/UnreachableStatementNodeVisitor.php b/src/PHPStan/NodeVisitor/UnreachableStatementNodeVisitor.php new file mode 100644 index 00000000000..9906b340a2e --- /dev/null +++ b/src/PHPStan/NodeVisitor/UnreachableStatementNodeVisitor.php @@ -0,0 +1,69 @@ +currentFileProvider = $currentFileProvider; + $this->phpStanNodeScopeResolver = $phpStanNodeScopeResolver; + $this->scopeFactory = $scopeFactory; + } + public function enterNode(Node $node) : ?Node + { + if (!$node instanceof StmtsAwareInterface && !$node instanceof ClassLike && !$node instanceof Declare_) { + return null; + } + if ($node->stmts === null) { + return null; + } + $file = $this->currentFileProvider->getFile(); + if (!$file instanceof File) { + return null; + } + $filePath = $file->getFilePath(); + $isPassedUnreachableStmt = \false; + $mutatingScope = $node->getAttribute(AttributeKey::SCOPE); + $mutatingScope = $mutatingScope instanceof MutatingScope ? $mutatingScope : $this->scopeFactory->createFromFile($filePath); + foreach ($node->stmts as $stmt) { + if ($stmt->getAttribute(AttributeKey::IS_UNREACHABLE) === \true) { + $isPassedUnreachableStmt = \true; + continue; + } + if ($isPassedUnreachableStmt) { + $stmt->setAttribute(AttributeKey::IS_UNREACHABLE, \true); + $stmt->setAttribute(AttributeKey::SCOPE, $mutatingScope); + $this->phpStanNodeScopeResolver->processNodes([$stmt], $filePath, $mutatingScope); + } + } + return null; + } +} diff --git a/src/PhpParser/NodeTraverser/FileWithoutNamespaceNodeTraverser.php b/src/PhpParser/NodeTraverser/FileWithoutNamespaceNodeTraverser.php index 00b630e70c3..e97d756fe9f 100644 --- a/src/PhpParser/NodeTraverser/FileWithoutNamespaceNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/FileWithoutNamespaceNodeTraverser.php @@ -7,7 +7,6 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Namespace_; use PhpParser\NodeTraverser; use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace; -use Rector\NodeTypeResolver\Node\AttributeKey; final class FileWithoutNamespaceNodeTraverser extends NodeTraverser { /** @@ -23,9 +22,6 @@ final class FileWithoutNamespaceNodeTraverser extends NodeTraverser } } $fileWithoutNamespace = new FileWithoutNamespace($nodes); - foreach ($nodes as $node) { - $node->setAttribute(AttributeKey::PARENT_NODE, $fileWithoutNamespace); - } return [$fileWithoutNamespace]; } } diff --git a/vendor/autoload.php b/vendor/autoload.php index fdc97ee92ea..d8233c445f0 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInitdf89f8040db24f1a031309888e2e2205::getLoader(); +return ComposerAutoloaderInitd7f678f0c3ee659fc396d7770917fe96::getLoader(); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index f4d40839ea5..613b4edd4f1 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -1606,6 +1606,7 @@ return array( 'Rector\\Core\\NodeManipulator\\StmtsManipulator' => $baseDir . '/src/NodeManipulator/StmtsManipulator.php', 'Rector\\Core\\NonPhpFile\\NonPhpFileProcessor' => $baseDir . '/src/NonPhpFile/NonPhpFileProcessor.php', 'Rector\\Core\\NonPhpFile\\Rector\\RenameClassNonPhpRector' => $baseDir . '/src/NonPhpFile/Rector/RenameClassNonPhpRector.php', + 'Rector\\Core\\PHPStan\\NodeVisitor\\UnreachableStatementNodeVisitor' => $baseDir . '/src/PHPStan/NodeVisitor/UnreachableStatementNodeVisitor.php', 'Rector\\Core\\PHPStan\\NodeVisitor\\WrappedNodeRestoringNodeVisitor' => $baseDir . '/src/PHPStan/NodeVisitor/WrappedNodeRestoringNodeVisitor.php', 'Rector\\Core\\PHPStan\\Reflection\\TypeToCallReflectionResolver\\ClosureTypeToCallReflectionResolver' => $baseDir . '/src/PHPStan/Reflection/TypeToCallReflectionResolver/ClosureTypeToCallReflectionResolver.php', 'Rector\\Core\\PHPStan\\Reflection\\TypeToCallReflectionResolver\\ConstantArrayTypeToCallReflectionResolver' => $baseDir . '/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantArrayTypeToCallReflectionResolver.php', diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index f394d135b27..65303ea9649 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInitdf89f8040db24f1a031309888e2e2205 +class ComposerAutoloaderInitd7f678f0c3ee659fc396d7770917fe96 { private static $loader; @@ -22,17 +22,17 @@ class ComposerAutoloaderInitdf89f8040db24f1a031309888e2e2205 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInitdf89f8040db24f1a031309888e2e2205', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitd7f678f0c3ee659fc396d7770917fe96', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInitdf89f8040db24f1a031309888e2e2205', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitd7f678f0c3ee659fc396d7770917fe96', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInitdf89f8040db24f1a031309888e2e2205::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInitd7f678f0c3ee659fc396d7770917fe96::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - $filesToLoad = \Composer\Autoload\ComposerStaticInitdf89f8040db24f1a031309888e2e2205::$files; + $filesToLoad = \Composer\Autoload\ComposerStaticInitd7f678f0c3ee659fc396d7770917fe96::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 3ec7edd2a56..2203a642122 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInitdf89f8040db24f1a031309888e2e2205 +class ComposerStaticInitd7f678f0c3ee659fc396d7770917fe96 { public static $files = array ( 'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php', @@ -1858,6 +1858,7 @@ class ComposerStaticInitdf89f8040db24f1a031309888e2e2205 'Rector\\Core\\NodeManipulator\\StmtsManipulator' => __DIR__ . '/../..' . '/src/NodeManipulator/StmtsManipulator.php', 'Rector\\Core\\NonPhpFile\\NonPhpFileProcessor' => __DIR__ . '/../..' . '/src/NonPhpFile/NonPhpFileProcessor.php', 'Rector\\Core\\NonPhpFile\\Rector\\RenameClassNonPhpRector' => __DIR__ . '/../..' . '/src/NonPhpFile/Rector/RenameClassNonPhpRector.php', + 'Rector\\Core\\PHPStan\\NodeVisitor\\UnreachableStatementNodeVisitor' => __DIR__ . '/../..' . '/src/PHPStan/NodeVisitor/UnreachableStatementNodeVisitor.php', 'Rector\\Core\\PHPStan\\NodeVisitor\\WrappedNodeRestoringNodeVisitor' => __DIR__ . '/../..' . '/src/PHPStan/NodeVisitor/WrappedNodeRestoringNodeVisitor.php', 'Rector\\Core\\PHPStan\\Reflection\\TypeToCallReflectionResolver\\ClosureTypeToCallReflectionResolver' => __DIR__ . '/../..' . '/src/PHPStan/Reflection/TypeToCallReflectionResolver/ClosureTypeToCallReflectionResolver.php', 'Rector\\Core\\PHPStan\\Reflection\\TypeToCallReflectionResolver\\ConstantArrayTypeToCallReflectionResolver' => __DIR__ . '/../..' . '/src/PHPStan/Reflection/TypeToCallReflectionResolver/ConstantArrayTypeToCallReflectionResolver.php', @@ -3100,9 +3101,9 @@ class ComposerStaticInitdf89f8040db24f1a031309888e2e2205 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInitdf89f8040db24f1a031309888e2e2205::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInitdf89f8040db24f1a031309888e2e2205::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInitdf89f8040db24f1a031309888e2e2205::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInitd7f678f0c3ee659fc396d7770917fe96::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitd7f678f0c3ee659fc396d7770917fe96::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitd7f678f0c3ee659fc396d7770917fe96::$classMap; }, null, ClassLoader::class); }