1
0
Fork 0

Support find-all-references for namespaces (#221)

namespace-refs-for-qualified-names
Felix Becker 2016-12-17 03:46:08 +01:00 committed by GitHub
parent 83618fee2e
commit 2005518dfe
7 changed files with 51 additions and 14 deletions

View File

@ -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 . '()';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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