Support find-all-references for namespaces (#221)
parent
83618fee2e
commit
2005518dfe
|
@ -102,17 +102,18 @@ class DefinitionResolver
|
||||||
*/
|
*/
|
||||||
public function createDefinitionFromNode(Node $node, string $fqn = null): Definition
|
public function createDefinitionFromNode(Node $node, string $fqn = null): Definition
|
||||||
{
|
{
|
||||||
|
$parent = $node->getAttribute('parentNode');
|
||||||
$def = new Definition;
|
$def = new Definition;
|
||||||
$def->canBeInstantiated = $node instanceof Node\Stmt\Class_;
|
$def->canBeInstantiated = $node instanceof Node\Stmt\Class_;
|
||||||
$def->isGlobal = (
|
$def->isGlobal = (
|
||||||
$node instanceof Node\Stmt\ClassLike
|
$node instanceof Node\Stmt\ClassLike
|
||||||
|| $node instanceof Node\Stmt\Namespace_
|
|| ($node instanceof Node\Name && $parent instanceof Node\Stmt\Namespace_)
|
||||||
|| $node instanceof Node\Stmt\Function_
|
|| $node instanceof Node\Stmt\Function_
|
||||||
|| $node->getAttribute('parentNode') instanceof Node\Stmt\Const_
|
|| $parent instanceof Node\Stmt\Const_
|
||||||
);
|
);
|
||||||
$def->isStatic = (
|
$def->isStatic = (
|
||||||
($node instanceof Node\Stmt\ClassMethod && $node->isStatic())
|
($node instanceof Node\Stmt\ClassMethod && $node->isStatic())
|
||||||
|| ($node instanceof Node\Stmt\PropertyProperty && $node->getAttribute('parentNode')->isStatic())
|
|| ($node instanceof Node\Stmt\PropertyProperty && $parent->isStatic())
|
||||||
);
|
);
|
||||||
$def->fqn = $fqn;
|
$def->fqn = $fqn;
|
||||||
if ($node instanceof Node\Stmt\Class_) {
|
if ($node instanceof Node\Stmt\Class_) {
|
||||||
|
@ -203,9 +204,9 @@ class DefinitionResolver
|
||||||
if (
|
if (
|
||||||
$node instanceof Node\Name && (
|
$node instanceof Node\Name && (
|
||||||
$parent instanceof Node\Stmt\ClassLike
|
$parent instanceof Node\Stmt\ClassLike
|
||||||
|| $parent instanceof Node\Stmt\Namespace_
|
|
||||||
|| $parent instanceof Node\Param
|
|| $parent instanceof Node\Param
|
||||||
|| $parent instanceof Node\FunctionLike
|
|| $parent instanceof Node\FunctionLike
|
||||||
|
|| $parent instanceof Node\Stmt\GroupUse
|
||||||
|| $parent instanceof Node\Expr\New_
|
|| $parent instanceof Node\Expr\New_
|
||||||
|| $parent instanceof Node\Expr\StaticCall
|
|| $parent instanceof Node\Expr\StaticCall
|
||||||
|| $parent instanceof Node\Expr\ClassConstFetch
|
|| $parent instanceof Node\Expr\ClassConstFetch
|
||||||
|
@ -804,12 +805,13 @@ class DefinitionResolver
|
||||||
*/
|
*/
|
||||||
public static function getDefinedFqn(Node $node)
|
public static function getDefinedFqn(Node $node)
|
||||||
{
|
{
|
||||||
|
$parent = $node->getAttribute('parentNode');
|
||||||
// Anonymous classes don't count as a definition
|
// Anonymous classes don't count as a definition
|
||||||
if ($node instanceof Node\Stmt\ClassLike && isset($node->name)) {
|
if ($node instanceof Node\Stmt\ClassLike && isset($node->name)) {
|
||||||
// Class, interface or trait declaration
|
// Class, interface or trait declaration
|
||||||
return (string)$node->namespacedName;
|
return (string)$node->namespacedName;
|
||||||
} else if ($node instanceof Node\Stmt\Namespace_) {
|
} else if ($node instanceof Node\Name && $parent instanceof Node\Stmt\Namespace_) {
|
||||||
return (string)$node->name;
|
return (string)$node;
|
||||||
} else if ($node instanceof Node\Stmt\Function_) {
|
} else if ($node instanceof Node\Stmt\Function_) {
|
||||||
// Function: use functionName() as the name
|
// Function: use functionName() as the name
|
||||||
return (string)$node->namespacedName . '()';
|
return (string)$node->namespacedName . '()';
|
||||||
|
|
|
@ -37,11 +37,24 @@ class ReferencesCollector extends NodeVisitorAbstract
|
||||||
// Check if the node references any global symbol
|
// Check if the node references any global symbol
|
||||||
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
||||||
if ($fqn) {
|
if ($fqn) {
|
||||||
|
$parent = $node->getAttribute('parentNode');
|
||||||
|
$grandParent = $parent ? $parent->getAttribute('parentNode') : null;
|
||||||
$this->addReference($fqn, $node);
|
$this->addReference($fqn, $node);
|
||||||
|
if (
|
||||||
|
$node instanceof Node\Name
|
||||||
|
&& $node->isQualified()
|
||||||
|
&& !($parent instanceof Node\Stmt\Namespace_ && $parent->name === $node)
|
||||||
|
) {
|
||||||
|
// 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
|
// Namespaced constant access and function calls also need to register a reference
|
||||||
// to the global version because PHP falls back to global at runtime
|
// to the global version because PHP falls back to global at runtime
|
||||||
// http://php.net/manual/en/language.namespaces.fallback.php
|
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||||
$parent = $node->getAttribute('parentNode');
|
|
||||||
if ($parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall) {
|
if ($parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall) {
|
||||||
$parts = explode('\\', $fqn);
|
$parts = explode('\\', $fqn);
|
||||||
if (count($parts) > 1) {
|
if (count($parts) > 1) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ class SymbolInformation
|
||||||
*/
|
*/
|
||||||
public static function fromNode(Node $node, string $fqn = null)
|
public static function fromNode(Node $node, string $fqn = null)
|
||||||
{
|
{
|
||||||
|
$parent = $node->getAttribute('parentNode');
|
||||||
$symbol = new self;
|
$symbol = new self;
|
||||||
if ($node instanceof Node\Stmt\Class_) {
|
if ($node instanceof Node\Stmt\Class_) {
|
||||||
$symbol->kind = SymbolKind::CLASS_;
|
$symbol->kind = SymbolKind::CLASS_;
|
||||||
|
@ -55,7 +56,7 @@ class SymbolInformation
|
||||||
$symbol->kind = SymbolKind::CLASS_;
|
$symbol->kind = SymbolKind::CLASS_;
|
||||||
} else if ($node instanceof Node\Stmt\Interface_) {
|
} else if ($node instanceof Node\Stmt\Interface_) {
|
||||||
$symbol->kind = SymbolKind::INTERFACE;
|
$symbol->kind = SymbolKind::INTERFACE;
|
||||||
} else if ($node instanceof Node\Stmt\Namespace_) {
|
} else if ($node instanceof Node\Name && $parent instanceof Node\Stmt\Namespace_) {
|
||||||
$symbol->kind = SymbolKind::NAMESPACE;
|
$symbol->kind = SymbolKind::NAMESPACE;
|
||||||
} else if ($node instanceof Node\Stmt\Function_) {
|
} else if ($node instanceof Node\Stmt\Function_) {
|
||||||
$symbol->kind = SymbolKind::FUNCTION;
|
$symbol->kind = SymbolKind::FUNCTION;
|
||||||
|
@ -77,7 +78,9 @@ class SymbolInformation
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) {
|
if ($node instanceof Node\Name) {
|
||||||
|
$symbol->name = (string)$node;
|
||||||
|
} else if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) {
|
||||||
$symbol->name = $node->var->name;
|
$symbol->name = $node->var->name;
|
||||||
} else if ($node instanceof Node\Expr\ClosureUse) {
|
} else if ($node instanceof Node\Expr\ClosureUse) {
|
||||||
$symbol->name = $node->var;
|
$symbol->name = $node->var;
|
||||||
|
|
|
@ -87,7 +87,8 @@ class DefinitionCollectorTest extends TestCase
|
||||||
$defNodes = $definitionCollector->nodes;
|
$defNodes = $definitionCollector->nodes;
|
||||||
|
|
||||||
$this->assertEquals(['TestNamespace', 'TestNamespace\\whatever()'], array_keys($defNodes));
|
$this->assertEquals(['TestNamespace', 'TestNamespace\\whatever()'], array_keys($defNodes));
|
||||||
$this->assertInstanceOf(Node\Stmt\Namespace_::class, $defNodes['TestNamespace']);
|
$this->assertInstanceOf(Node\Name::class, $defNodes['TestNamespace']);
|
||||||
|
$this->assertInstanceOf(Node\Stmt\Namespace_::class, $defNodes['TestNamespace']->getAttribute('parentNode'));
|
||||||
$this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\whatever()']);
|
$this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\whatever()']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,8 @@ abstract class ServerTestCase extends TestCase
|
||||||
'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))),
|
'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))),
|
||||||
|
|
||||||
// Namespaced
|
// Namespaced
|
||||||
'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 0), new Position( 2, 24))),
|
'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))),
|
||||||
'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 0), new Position( 2, 30))),
|
'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 10), new Position( 2, 29))),
|
||||||
'TestNamespace\\TEST_CONST' => new Location($symbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))),
|
'TestNamespace\\TEST_CONST' => new Location($symbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))),
|
||||||
'TestNamespace\\TestClass' => new Location($symbolsUri, new Range(new Position(20, 0), new Position(61, 1))),
|
'TestNamespace\\TestClass' => new Location($symbolsUri, new Range(new Position(20, 0), new Position(61, 1))),
|
||||||
'TestNamespace\\ChildClass' => new Location($symbolsUri, new Range(new Position(99, 0), new Position(99, 37))),
|
'TestNamespace\\ChildClass' => new Location($symbolsUri, new Range(new Position(99, 0), new Position(99, 37))),
|
||||||
|
@ -102,6 +102,11 @@ abstract class ServerTestCase extends TestCase
|
||||||
$this->referenceLocations = [
|
$this->referenceLocations = [
|
||||||
|
|
||||||
// Namespaced
|
// Namespaced
|
||||||
|
'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};
|
||||||
|
],
|
||||||
'TestNamespace\\TEST_CONST' => [
|
'TestNamespace\\TEST_CONST' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
||||||
],
|
],
|
||||||
|
|
|
@ -3,7 +3,7 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer\Tests\Server\TextDocument\References;
|
namespace LanguageServer\Tests\Server\TextDocument\References;
|
||||||
|
|
||||||
use LanguageServer\Protocol\Location;
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position, ReferenceContext, Location, Range};
|
||||||
use function LanguageServer\pathToUri;
|
use function LanguageServer\pathToUri;
|
||||||
|
|
||||||
class NamespacedTest extends GlobalTest
|
class NamespacedTest extends GlobalTest
|
||||||
|
@ -17,4 +17,17 @@ class NamespacedTest extends GlobalTest
|
||||||
{
|
{
|
||||||
return parent::getDefinitionLocation('TestNamespace\\' . $fqn);
|
return parent::getDefinitionLocation('TestNamespace\\' . $fqn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testReferencesForNamespaces()
|
||||||
|
{
|
||||||
|
// namespace TestNamespace;
|
||||||
|
// Get references for TestNamespace
|
||||||
|
$definition = parent::getDefinitionLocation('TestNamespace');
|
||||||
|
$result = $this->textDocument->references(
|
||||||
|
new ReferenceContext,
|
||||||
|
new TextDocumentIdentifier($definition->uri),
|
||||||
|
$definition->range->end
|
||||||
|
)->wait();
|
||||||
|
$this->assertEquals(parent::getReferenceLocations('TestNamespace'), $result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class SymbolTest extends ServerTestCase
|
||||||
$referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
|
$referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
|
||||||
// @codingStandardsIgnoreStart
|
// @codingStandardsIgnoreStart
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 0), new Position(2, 24))), ''),
|
new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 10), new Position(2, 23))), ''),
|
||||||
// Namespaced
|
// Namespaced
|
||||||
new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'),
|
new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'),
|
||||||
new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'),
|
new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'),
|
||||||
|
|
Loading…
Reference in New Issue