1
0
Fork 0

Find all references support

pull/357/head
Sara Itani 2017-03-09 15:56:41 -08:00
parent 167b6ac6f2
commit 4324530389
7 changed files with 104 additions and 32 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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 ?? [];
}

View File

@ -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)))

View File

@ -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);
}
}