Find all references support
parent
167b6ac6f2
commit
4324530389
|
@ -132,6 +132,7 @@ class DefinitionResolver implements DefinitionResolverInterface
|
|||
$def->type = $this->getTypeFromNode($node);
|
||||
$def->declarationLine = $this->getDeclarationLineFromNode($node);
|
||||
$def->documentation = $this->getDocumentationFromNode($node);
|
||||
// var_dump($def);
|
||||
return $def;
|
||||
}
|
||||
|
||||
|
@ -440,6 +441,8 @@ class DefinitionResolver implements DefinitionResolverInterface
|
|||
}
|
||||
// Resolve object
|
||||
$objType = $this->resolveExpressionNodeToType($expr->var);
|
||||
// var_dump((string)$expr->var->name);
|
||||
// var_dump($objType);
|
||||
if (!($objType instanceof Types\Compound)) {
|
||||
$objType = new Types\Compound([$objType]);
|
||||
}
|
||||
|
@ -453,6 +456,7 @@ class DefinitionResolver implements DefinitionResolverInterface
|
|||
return new Types\Mixed;
|
||||
} else {
|
||||
$classFqn = substr((string)$t->getFqsen(), 1);
|
||||
// var_dump($classFqn);
|
||||
}
|
||||
$fqn = $classFqn . '->' . $expr->name;
|
||||
if ($expr instanceof Node\Expr\MethodCall) {
|
||||
|
@ -771,6 +775,9 @@ class DefinitionResolver implements DefinitionResolverInterface
|
|||
&& !empty($varTags = $docBlock->getTagsByName('var'))
|
||||
&& ($type = $varTags[0]->getType())
|
||||
) {
|
||||
if ((string)$type === "\\TestNamespace\\TestClass") {
|
||||
var_dump($type);
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
// Resolve the expression
|
||||
|
|
|
@ -257,7 +257,7 @@ class PhpDocument
|
|||
} else {
|
||||
$offset = $position->toOffset($this->stmts->getFileContents());
|
||||
$node = $this->stmts->getDescendantNodeAtPosition($offset);
|
||||
if ($node->getStart() > $offset) {
|
||||
if ($node !== null && $node->getStart() > $offset) {
|
||||
return null;
|
||||
}
|
||||
return $node;
|
||||
|
|
|
@ -3,9 +3,11 @@ declare(strict_types = 1);
|
|||
|
||||
namespace LanguageServer\Server;
|
||||
|
||||
use Microsoft\PhpParser as Tolerant;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
||||
use PhpParser\{Node, NodeTraverser};
|
||||
use LanguageServer\{
|
||||
DefinitionResolverInterface, FqnUtilities, LanguageClient, PhpDocumentLoader, PhpDocument, DefinitionResolver, CompletionProvider
|
||||
DefinitionResolverInterface, FqnUtilities, LanguageClient, PhpDocumentLoader, PhpDocument, DefinitionResolver, CompletionProvider, TolerantDefinitionResolver, TolerantTreeAnalyzer
|
||||
};
|
||||
use LanguageServer\NodeVisitor\VariableReferencesCollector;
|
||||
use LanguageServer\Protocol\{
|
||||
|
@ -197,31 +199,35 @@ class TextDocument
|
|||
// Variables always stay in the boundary of the file and need to be searched inside their function scope
|
||||
// by traversing the AST
|
||||
if (
|
||||
$node instanceof Node\Expr\Variable
|
||||
|| $node instanceof Node\Param
|
||||
|| $node instanceof Node\Expr\ClosureUse
|
||||
|
||||
($node instanceof Tolerant\Node\Expression\Variable && !($node->getParent()->getParent() instanceof Tolerant\Node\PropertyDeclaration))
|
||||
|| $node instanceof Tolerant\Node\Parameter
|
||||
|| $node instanceof Tolerant\Node\UseVariableName
|
||||
) {
|
||||
if ($node->name instanceof Node\Expr) {
|
||||
if (isset($node->name) && $node->name instanceof Tolerant\Node\Expression) {
|
||||
return null;
|
||||
}
|
||||
// Find function/method/closure scope
|
||||
$n = $node;
|
||||
while (isset($n) && !($n instanceof Node\FunctionLike)) {
|
||||
$n = $n->getAttribute('parentNode');
|
||||
|
||||
$n = $n->getFirstAncestor(Tolerant\Node\Statement\FunctionDeclaration::class, Tolerant\Node\MethodDeclaration::class, Tolerant\Node\Expression\AnonymousFunctionCreationExpression::class, Tolerant\Node\SourceFileNode::class);
|
||||
|
||||
if ($n === null) {
|
||||
$n = $node->getFirstAncestor(Tolerant\Node\Statement\ExpressionStatement::class)->getParent();
|
||||
}
|
||||
if (!isset($n)) {
|
||||
$n = $node->getAttribute('ownerDocument');
|
||||
|
||||
foreach ($n->getDescendantNodes() as $descendantNode) {
|
||||
if ($descendantNode instanceof Tolerant\Node\Expression\Variable &&
|
||||
$descendantNode->getName() === $node->getName()
|
||||
) {
|
||||
var_dump($descendantNode->getName());
|
||||
$locations[] = Location::fromNode($descendantNode);
|
||||
}
|
||||
$traverser = new NodeTraverser;
|
||||
$refCollector = new VariableReferencesCollector($node->name);
|
||||
$traverser->addVisitor($refCollector);
|
||||
$traverser->traverse($n->getStmts());
|
||||
foreach ($refCollector->nodes as $ref) {
|
||||
$locations[] = Location::fromNode($ref);
|
||||
}
|
||||
} else {
|
||||
// Definition with a global FQN
|
||||
$fqn = FqnUtilities::getDefinedFqn($node);
|
||||
// var_dump($fqn);
|
||||
// Wait until indexing finished
|
||||
if (!$this->index->isComplete()) {
|
||||
yield waitForEvent($this->index, 'complete');
|
||||
|
@ -240,6 +246,7 @@ class TextDocument
|
|||
$refs = $document->getReferenceNodesByFqn($fqn);
|
||||
if ($refs !== null) {
|
||||
foreach ($refs as $ref) {
|
||||
// var_dump($ref->getNodeKindName());
|
||||
$locations[] = Location::fromNode($ref);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,7 +229,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
if ($node->getName() === 'this' && $fqn = $this->getContainingClassFqn($node)) {
|
||||
return $this->index->getDefinition($fqn, false);
|
||||
}
|
||||
// TODO running throug thid for class constants or properties
|
||||
// TODO running through thid for class constants or properties
|
||||
|
||||
// Resolve the variable to a definition node (assignment, param or closure use)
|
||||
$defNode = $this->resolveVariableToNode($node);
|
||||
|
@ -246,7 +246,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
}
|
||||
// If the node is a function or constant, it could be namespaced, but PHP falls back to global
|
||||
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||
$globalFallback = $this->isConstantFetch($node) || $node->getFirstAncestor(Tolerant\Node\Expression\CallExpression::class) !== null;
|
||||
$globalFallback = $this->isConstantFetch($node) || $node->parent instanceof Tolerant\Node\Expression\CallExpression;
|
||||
// Return the Definition object from the index index
|
||||
return $this->index->getDefinition($fqn, $globalFallback);
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
}
|
||||
}
|
||||
|
||||
if ($useClause->functionOrConst->kind === Tolerant\TokenKind::FunctionKeyword) {
|
||||
if ($useClause->functionOrConst !== null && $useClause->functionOrConst->kind === Tolerant\TokenKind::FunctionKeyword) {
|
||||
$name .= '()';
|
||||
}
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
|
||||
// TODO
|
||||
// $classFqn = $node->getNamespaceDefinition()->name->getNamespacedName() . (string)$varType->getFqsen();
|
||||
var_dump($classFqn);
|
||||
// var_dump($classFqn);
|
||||
}
|
||||
$memberSuffix = '->' . (string)($access->memberName->getText() ?? $access->memberName->getText($node->getFileContents()));
|
||||
if ($node instanceof Tolerant\Node\Expression\CallExpression) {
|
||||
|
@ -453,8 +453,9 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
return $name;
|
||||
}
|
||||
|
||||
private function isConstantFetch(Tolerant\Node $node) : bool {
|
||||
public static function isConstantFetch(Tolerant\Node $node) : bool {
|
||||
return
|
||||
(
|
||||
$node instanceof Tolerant\Node\QualifiedName &&
|
||||
($node->parent instanceof Tolerant\Node\Statement\ExpressionStatement || $node->parent instanceof Tolerant\Node\Expression || $node->parent instanceof Tolerant\Node\DelimitedList\ExpressionList) &&
|
||||
!(
|
||||
|
@ -462,7 +463,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
$node->parent instanceof Tolerant\Node\Expression\ObjectCreationExpression ||
|
||||
$node->parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression || $node->parent instanceof Tolerant\Node\Expression\AnonymousFunctionCreationExpression ||
|
||||
($node->parent instanceof Tolerant\Node\Expression\BinaryExpression && $node->parent->operator->kind === Tolerant\TokenKind::InstanceOfKeyword)
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -547,7 +548,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
);
|
||||
}
|
||||
|
||||
static function isFunctionLike(Tolerant\Node $node) {
|
||||
public static function isFunctionLike(Tolerant\Node $node) {
|
||||
return
|
||||
$node instanceof Tolerant\Node\Statement\FunctionDeclaration ||
|
||||
$node instanceof Tolerant\Node\MethodDeclaration ||
|
||||
|
@ -638,7 +639,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
}
|
||||
$var = $access->dereferencableExpression;
|
||||
|
||||
var_dump("HERE!!!");
|
||||
// var_dump("HERE!!!");
|
||||
// Resolve object
|
||||
$objType = $this->resolveExpressionNodeToType($var);
|
||||
// var_dump($objType);
|
||||
|
@ -655,16 +656,16 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface
|
|||
return new Types\Mixed;
|
||||
} else {
|
||||
$classFqn = substr((string)$t->getFqsen(), 1);
|
||||
var_dump($classFqn);
|
||||
// var_dump($classFqn);
|
||||
}
|
||||
$fqn = $classFqn . '->' . $access->memberName->getText($expr->getFileContents());
|
||||
if ($expr instanceof Tolerant\Node\Expression\CallExpression) {
|
||||
$fqn .= '()';
|
||||
}
|
||||
var_dump($fqn);
|
||||
// var_dump($fqn);
|
||||
// var_dump($fqn);
|
||||
$def = $this->index->getDefinition($fqn);
|
||||
var_dump($def);
|
||||
// var_dump($def);
|
||||
if ($def !== null) {
|
||||
return $def->type;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,54 @@ class TolerantTreeAnalyzer implements TreeAnalyzerInterface {
|
|||
$this->definitionNodes[$fqn] = $node;
|
||||
$this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn);
|
||||
}
|
||||
|
||||
foreach ($this->stmts->getDescendantNodes() as $node) {
|
||||
$parent = $node->parent;
|
||||
if (
|
||||
($node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
|
||||
&& !(
|
||||
$node->parent instanceof Tolerant\Node\Expression\CallExpression ||
|
||||
$node->memberName instanceof Tolerant\Token
|
||||
))
|
||||
|| ($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart())
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$fqn = $definitionResolver->resolveReferenceNodeToFqn($node);
|
||||
if ($fqn === null) {
|
||||
continue;
|
||||
}
|
||||
$this->addReference($fqn, $node);
|
||||
|
||||
if (
|
||||
$node instanceof Tolerant\Node\QualifiedName
|
||||
&& $node->isQualifiedName()
|
||||
&& !($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
|
||||
)
|
||||
) {
|
||||
// Add references for each referenced namespace
|
||||
$ns = $fqn;
|
||||
while (($pos = strrpos($ns, '\\')) !== false) {
|
||||
$ns = substr($ns, 0, $pos);
|
||||
$this->addReference($ns, $node);
|
||||
}
|
||||
}
|
||||
|
||||
// Namespaced constant access and function calls also need to register a reference
|
||||
// to the global version because PHP falls back to global at runtime
|
||||
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||
if ($definitionResolver::isConstantFetch($node) ||
|
||||
($parent instanceof Tolerant\Node\Expression\CallExpression
|
||||
&& !(
|
||||
$node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
|
||||
))) {
|
||||
$parts = explode('\\', $fqn);
|
||||
if (count($parts) > 1) {
|
||||
$globalFqn = end($parts);
|
||||
$this->addReference($globalFqn, $node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDiagnostics() {
|
||||
|
@ -71,6 +119,14 @@ class TolerantTreeAnalyzer implements TreeAnalyzerInterface {
|
|||
return $diagnostics;
|
||||
}
|
||||
|
||||
private function addReference(string $fqn, Tolerant\Node $node)
|
||||
{
|
||||
if (!isset($this->referenceNodes[$fqn])) {
|
||||
$this->referenceNodes[$fqn] = [];
|
||||
}
|
||||
$this->referenceNodes[$fqn][] = $node;
|
||||
}
|
||||
|
||||
public function getDefinitions() {
|
||||
return $this->definitions ?? [];
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ abstract class ServerTestCase extends TestCase
|
|||
'TestNamespace' => [
|
||||
0 => new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))), // use function TestNamespace\test_function;
|
||||
1 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass;
|
||||
2 => new Location($useUri, new Range(new Position( 5, 4), new Position( 5, 17))) // use TestNamespace\{TestTrait, TestInterface};
|
||||
2 => new Location($useUri, new Range(new Position( 5, 4), new Position( 5, 18))) // use TestNamespace\{TestTrait, TestInterface};
|
||||
],
|
||||
'TestNamespace\\TEST_CONST' => [
|
||||
0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
||||
|
@ -145,8 +145,8 @@ abstract class ServerTestCase extends TestCase
|
|||
3 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||
],
|
||||
'TestNamespace\\TestClass::staticTestProperty' => [
|
||||
0 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 35))), // echo TestClass::$staticTestProperty;
|
||||
1 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||
0 => new Location($referencesUri, new Range(new Position( 8, 16), new Position( 8, 35))), // echo TestClass::$staticTestProperty;
|
||||
1 => new Location($referencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||
],
|
||||
'TestNamespace\\TestClass::staticTestMethod()' => [
|
||||
0 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 27)))
|
||||
|
@ -196,7 +196,7 @@ abstract class ServerTestCase extends TestCase
|
|||
],
|
||||
'TestClass::staticTestProperty' => [
|
||||
0 => new Location($globalReferencesUri, new Range(new Position( 8, 16), new Position( 8, 35))), // echo TestClass::$staticTestProperty;
|
||||
1 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||
1 => new Location($globalReferencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||
],
|
||||
'TestClass::staticTestMethod()' => [
|
||||
0 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 27)))
|
||||
|
|
|
@ -28,6 +28,7 @@ class NamespacedTest extends GlobalTest
|
|||
new TextDocumentIdentifier($definition->uri),
|
||||
$definition->range->end
|
||||
)->wait();
|
||||
// var_dump($result);
|
||||
$this->assertEquals(parent::getReferenceLocations('TestNamespace'), $result);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue