diff --git a/packages/node-collector/src/NodeCollector/ParsedPropertyFetchNodeCollector.php b/packages/node-collector/src/NodeCollector/ParsedPropertyFetchNodeCollector.php index b65afd63478e..a0dcf37aa2f5 100644 --- a/packages/node-collector/src/NodeCollector/ParsedPropertyFetchNodeCollector.php +++ b/packages/node-collector/src/NodeCollector/ParsedPropertyFetchNodeCollector.php @@ -5,7 +5,9 @@ namespace Rector\NodeCollector\NodeCollector; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticCall; use PHPStan\Type\ObjectType; use PHPStan\Type\TypeWithClassName; use PHPStan\Type\UnionType; @@ -53,6 +55,12 @@ public function collect(Node $node): void } $propertyType = $this->nodeTypeResolver->getStaticType($node->var); + + // make sure name is valid + if ($node->name instanceof StaticCall || $node->name instanceof MethodCall) { + return; + } + $propertyName = $this->nodeNameResolver->getName($node->name); if ($propertyType instanceof TypeWithClassName) { diff --git a/packages/node-name-resolver/src/NodeNameResolver.php b/packages/node-name-resolver/src/NodeNameResolver.php index 2e16f82c3386..a7dc04171ad5 100644 --- a/packages/node-name-resolver/src/NodeNameResolver.php +++ b/packages/node-name-resolver/src/NodeNameResolver.php @@ -14,8 +14,11 @@ use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Trait_; use Rector\Core\Exception\ShouldNotHappenException; +use Rector\Core\PhpParser\Printer\BetterStandardPrinter; use Rector\NodeNameResolver\Contract\NodeNameResolverInterface; use Rector\NodeNameResolver\Regex\RegexPatternDetector; +use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider; +use Symplify\SmartFileSystem\SmartFileInfo; final class NodeNameResolver { @@ -29,13 +32,29 @@ final class NodeNameResolver */ private $regexPatternDetector; + /** + * @var CurrentFileInfoProvider + */ + private $currentFileInfoProvider; + + /** + * @var BetterStandardPrinter + */ + private $betterStandardPrinter; + /** * @param NodeNameResolverInterface[] $nodeNameResolvers */ - public function __construct(RegexPatternDetector $regexPatternDetector, array $nodeNameResolvers = []) - { + public function __construct( + RegexPatternDetector $regexPatternDetector, + BetterStandardPrinter $betterStandardPrinter, + CurrentFileInfoProvider $currentFileInfoProvider, + array $nodeNameResolvers = [] + ) { $this->regexPatternDetector = $regexPatternDetector; $this->nodeNameResolvers = $nodeNameResolvers; + $this->currentFileInfoProvider = $currentFileInfoProvider; + $this->betterStandardPrinter = $betterStandardPrinter; } /** @@ -93,7 +112,7 @@ public function getName(Node $node): ?string return null; } - throw new ShouldNotHappenException(sprintf('Pick more specific node than "%s"', get_class($node))); + $this->reportInvalidNodeForName($node); } foreach ($this->nodeNameResolvers as $nodeNameResolver) { @@ -175,4 +194,34 @@ private function resolveNamespacedNameAwareNode(ClassLike $classLike): ?string return $this->getName($classLike->name); } + + /** + * @param MethodCall|StaticCall $node + */ + private function reportInvalidNodeForName(Node $node): void + { + $message = sprintf('Pick more specific node than "%s", e.g. "$node->name"', get_class($node)); + + $fileInfo = $this->currentFileInfoProvider->getSmartFileInfo(); + if ($fileInfo instanceof SmartFileInfo) { + $message .= PHP_EOL . PHP_EOL; + $message .= sprintf( + 'Caused in "%s" file on line %d on code "%s"', + $fileInfo->getRelativeFilePathFromCwd(), + $node->getStartLine(), + $this->betterStandardPrinter->print($node) + ); + } + + $backtrace = debug_backtrace(); + if (isset($backtrace[1])) { + $fileInfo = new SmartFileInfo($backtrace[1]['file']); + $fileAndLine = $fileInfo->getRelativeFilePathFromCwd() . ':' . $backtrace[1]['line']; + + $message .= PHP_EOL . PHP_EOL; + $message .= sprintf('Look at %s', $fileAndLine); + } + + throw new ShouldNotHappenException($message); + } } diff --git a/packages/node-type-resolver/tests/NodeVisitor/FunctionMethodAndClassNodeVisitor/FunctionMethodAndClassNodeVisitorTest.php b/packages/node-type-resolver/tests/NodeVisitor/FunctionMethodAndClassNodeVisitor/FunctionMethodAndClassNodeVisitorTest.php index 38eead1547a6..94596aa6d474 100644 --- a/packages/node-type-resolver/tests/NodeVisitor/FunctionMethodAndClassNodeVisitor/FunctionMethodAndClassNodeVisitorTest.php +++ b/packages/node-type-resolver/tests/NodeVisitor/FunctionMethodAndClassNodeVisitor/FunctionMethodAndClassNodeVisitorTest.php @@ -8,15 +8,26 @@ use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor\NameResolver; -use Rector\CodingStyle\Naming\ClassNaming; +use Rector\Core\HttpKernel\RectorKernel; use Rector\Core\Testing\PHPUnit\AbstractNodeVisitorTestCase; -use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeNameResolver\Regex\RegexPatternDetector; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeVisitor\FunctionMethodAndClassNodeVisitor; final class FunctionMethodAndClassNodeVisitorTest extends AbstractNodeVisitorTestCase { + /** + * @var FunctionMethodAndClassNodeVisitor + */ + private $functionMethodAndClassNodeVisitor; + + protected function setUp(): void + { + parent::setUp(); + + $this->bootKernel(RectorKernel::class); + $this->functionMethodAndClassNodeVisitor = self::$container->get(FunctionMethodAndClassNodeVisitor::class); + } + /** * @dataProvider provideData(); */ @@ -37,9 +48,7 @@ protected function visitNodes(array $nodes): void { $nodeTraverser = new NodeTraverser(); $nodeTraverser->addVisitor(new NameResolver()); - $nodeTraverser->addVisitor( - new FunctionMethodAndClassNodeVisitor(new ClassNaming(new NodeNameResolver(new RegexPatternDetector()))) - ); + $nodeTraverser->addVisitor($this->functionMethodAndClassNodeVisitor); $nodeTraverser->traverse($nodes); }