2018-08-03 18:41:30 +00:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
|
2018-08-06 12:10:16 +00:00
|
|
|
namespace Rector\NodeTypeResolver\PHPStan\Scope;
|
2018-08-03 18:41:30 +00:00
|
|
|
|
|
|
|
use PhpParser\Node;
|
2018-08-04 20:32:01 +00:00
|
|
|
use PhpParser\Node\Stmt\Class_;
|
2018-08-05 16:12:11 +00:00
|
|
|
use PhpParser\Node\Stmt\Interface_;
|
2018-08-03 18:41:30 +00:00
|
|
|
use PhpParser\NodeVisitor\NameResolver;
|
2018-08-06 12:10:16 +00:00
|
|
|
use PHPStan\Analyser\NodeScopeResolver as PHPStanNodeScopeResolver;
|
2018-08-03 18:41:30 +00:00
|
|
|
use PHPStan\Analyser\Scope;
|
|
|
|
use PHPStan\Broker\Broker;
|
|
|
|
use Rector\Configuration\Option;
|
2018-08-04 20:32:01 +00:00
|
|
|
use Rector\Exception\ShouldNotHappenException;
|
2018-08-03 18:41:30 +00:00
|
|
|
use Rector\FileSystem\FilesFinder;
|
|
|
|
use Rector\NodeTypeResolver\Configuration\CurrentFileProvider;
|
2018-08-10 11:35:13 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\TypeAttribute;
|
2018-08-03 18:41:30 +00:00
|
|
|
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
2018-08-05 15:49:14 +00:00
|
|
|
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
2018-08-03 18:41:30 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @inspired by https://github.com/silverstripe/silverstripe-upgrader/blob/532182b23e854d02e0b27e68ebc394f436de0682/src/UpgradeRule/PHP/Visitor/PHPStanScopeVisitor.php
|
|
|
|
* - https://github.com/silverstripe/silverstripe-upgrader/pull/57/commits/e5c7cfa166ad940d9d4ff69537d9f7608e992359#diff-5e0807bb3dc03d6a8d8b6ad049abd774
|
|
|
|
*/
|
2018-08-06 12:10:16 +00:00
|
|
|
final class NodeScopeResolver
|
2018-08-03 18:41:30 +00:00
|
|
|
{
|
|
|
|
/**
|
2018-08-06 12:10:16 +00:00
|
|
|
* @var PHPStanNodeScopeResolver
|
2018-08-03 18:41:30 +00:00
|
|
|
*/
|
2018-08-06 12:10:16 +00:00
|
|
|
private $phpStanNodeScopeResolver;
|
2018-08-03 18:41:30 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var CurrentFileProvider
|
|
|
|
*/
|
|
|
|
private $currentFileProvider;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ParameterProvider
|
|
|
|
*/
|
|
|
|
private $parameterProvider;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var FilesFinder
|
|
|
|
*/
|
|
|
|
private $filesFinder;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ScopeFactory
|
|
|
|
*/
|
2018-08-06 10:31:17 +00:00
|
|
|
private $scopeFactory;
|
2018-08-03 18:41:30 +00:00
|
|
|
|
2018-08-05 15:11:56 +00:00
|
|
|
/**
|
2018-08-06 10:31:17 +00:00
|
|
|
* @var Broker
|
2018-08-05 15:11:56 +00:00
|
|
|
*/
|
2018-08-06 10:31:17 +00:00
|
|
|
private $broker;
|
2018-08-05 15:11:56 +00:00
|
|
|
|
2018-08-03 18:41:30 +00:00
|
|
|
public function __construct(
|
|
|
|
CurrentFileProvider $currentFileProvider,
|
|
|
|
ParameterProvider $parameterProvider,
|
2018-08-06 10:31:17 +00:00
|
|
|
FilesFinder $filesFinder,
|
|
|
|
ScopeFactory $scopeFactory,
|
2018-08-06 12:10:16 +00:00
|
|
|
PHPStanNodeScopeResolver $phpStanNodeScopeResolver,
|
2018-08-06 10:31:17 +00:00
|
|
|
Broker $broker
|
2018-08-03 18:41:30 +00:00
|
|
|
) {
|
|
|
|
$this->currentFileProvider = $currentFileProvider;
|
|
|
|
$this->parameterProvider = $parameterProvider;
|
|
|
|
$this->filesFinder = $filesFinder;
|
2018-08-06 10:31:17 +00:00
|
|
|
$this->scopeFactory = $scopeFactory;
|
2018-08-06 12:10:16 +00:00
|
|
|
$this->phpStanNodeScopeResolver = $phpStanNodeScopeResolver;
|
2018-08-06 10:31:17 +00:00
|
|
|
$this->broker = $broker;
|
2018-08-03 18:41:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-05 08:58:42 +00:00
|
|
|
* @param Node[] $nodes
|
|
|
|
* @return Node[]
|
2018-08-03 18:41:30 +00:00
|
|
|
*/
|
|
|
|
public function processNodes(array $nodes): array
|
|
|
|
{
|
2018-08-05 12:15:59 +00:00
|
|
|
$this->ensureNameResolverWasRun($nodes);
|
2018-08-04 16:58:52 +00:00
|
|
|
$this->setAnalysedFiles();
|
|
|
|
|
2018-08-06 12:10:16 +00:00
|
|
|
$this->phpStanNodeScopeResolver->processNodes(
|
2018-08-03 18:41:30 +00:00
|
|
|
$nodes,
|
2018-08-06 10:31:17 +00:00
|
|
|
$this->scopeFactory->createFromFileInfo($this->currentFileProvider->getSplFileInfo()),
|
2018-08-03 18:41:30 +00:00
|
|
|
function (Node $node, Scope $scope): void {
|
2018-08-05 15:11:56 +00:00
|
|
|
// the class reflection is resolved AFTER entering to class node
|
|
|
|
// so we need to get it from the first after this one
|
2018-08-05 16:12:11 +00:00
|
|
|
if ($node instanceof Class_ || $node instanceof Interface_) {
|
2018-08-05 15:49:14 +00:00
|
|
|
if (isset($node->namespacedName)) {
|
2018-08-06 10:31:17 +00:00
|
|
|
$scope = $scope->enterClass($this->broker->getClass((string) $node->namespacedName));
|
2018-08-05 15:49:14 +00:00
|
|
|
} else {
|
|
|
|
// possibly anonymous class
|
2018-08-05 16:19:02 +00:00
|
|
|
$anonymousClassReflection = (new PrivatesAccessor())->getPrivateProperty(
|
2018-08-06 12:10:16 +00:00
|
|
|
$this->phpStanNodeScopeResolver,
|
2018-08-05 16:19:02 +00:00
|
|
|
'anonymousClassReflection'
|
2018-08-05 15:49:14 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if ($anonymousClassReflection) {
|
|
|
|
$scope = $scope->enterAnonymousClass($anonymousClassReflection);
|
|
|
|
}
|
|
|
|
}
|
2018-08-05 15:11:56 +00:00
|
|
|
}
|
|
|
|
|
2018-08-10 11:35:13 +00:00
|
|
|
$node->setAttribute(TypeAttribute::SCOPE, $scope);
|
2018-08-03 18:41:30 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return $nodes;
|
|
|
|
}
|
|
|
|
|
2018-08-04 16:58:52 +00:00
|
|
|
private function setAnalysedFiles(): void
|
|
|
|
{
|
|
|
|
$source = $this->parameterProvider->provideParameter(Option::SOURCE);
|
2018-08-05 08:29:33 +00:00
|
|
|
$phpFileInfos = $this->filesFinder->findInDirectoriesAndFiles($source, ['php']);
|
|
|
|
|
|
|
|
$filePaths = [];
|
|
|
|
foreach ($phpFileInfos as $phpFileInfo) {
|
|
|
|
$filePaths[] = $phpFileInfo->getRealPath();
|
|
|
|
}
|
|
|
|
|
2018-08-06 12:10:16 +00:00
|
|
|
$this->phpStanNodeScopeResolver->setAnalysedFiles($filePaths);
|
2018-08-04 16:58:52 +00:00
|
|
|
}
|
2018-08-04 20:32:01 +00:00
|
|
|
|
|
|
|
/**
|
2018-08-05 08:58:42 +00:00
|
|
|
* @param Node[] $nodes
|
2018-08-04 20:32:01 +00:00
|
|
|
*/
|
|
|
|
private function ensureNameResolverWasRun(array $nodes): void
|
|
|
|
{
|
|
|
|
foreach ($nodes as $node) {
|
|
|
|
if ($node instanceof Class_) {
|
2018-08-05 11:51:40 +00:00
|
|
|
if (isset($node->namespacedName)) {
|
|
|
|
return;
|
2018-08-04 20:32:01 +00:00
|
|
|
}
|
2018-08-05 11:51:40 +00:00
|
|
|
|
|
|
|
throw new ShouldNotHappenException(sprintf(
|
|
|
|
'"%s" node needs "namespacedNode" property set via "%s" Node Traverser. Did you forget to run it before calling "%s->processNodes()"?.',
|
|
|
|
get_class($node),
|
|
|
|
NameResolver::class,
|
|
|
|
self::class
|
|
|
|
));
|
2018-08-04 20:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-03 18:41:30 +00:00
|
|
|
}
|