Split up PhpDocument::getDefinitionByNode()
parent
6be53ad658
commit
3a880934e5
|
@ -70,6 +70,13 @@ class PhpDocument
|
||||||
*/
|
*/
|
||||||
private $definitions = [];
|
private $definitions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map from fully qualified name (FQN) to array of nodes that reference the symbol
|
||||||
|
*
|
||||||
|
* @var Node[][]
|
||||||
|
*/
|
||||||
|
private $references;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $uri The URI of the document
|
* @param string $uri The URI of the document
|
||||||
* @param Project $project The Project this document belongs to (to register definitions etc)
|
* @param Project $project The Project this document belongs to (to register definitions etc)
|
||||||
|
@ -284,23 +291,28 @@ class PhpDocument
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the definition node for any node
|
* Returns true if the given FQN is defined in this document
|
||||||
* The definition node MAY be in another document, check the ownerDocument attribute
|
*
|
||||||
|
* @param string $fqn The fully qualified name of the symbol
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDefined(string $fqn): bool
|
||||||
|
{
|
||||||
|
return isset($this->definitions[$fqn]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the FQN that is referenced by a node
|
||||||
*
|
*
|
||||||
* @param Node $node
|
* @param Node $node
|
||||||
* @return Node|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getDefinitionByNode(Node $node)
|
public function getReferencedFqn(Node $node)
|
||||||
{
|
{
|
||||||
if ($node instanceof Node\Name) {
|
if ($node instanceof Node\Name) {
|
||||||
$nameNode = $node;
|
$nameNode = $node;
|
||||||
$node = $node->getAttribute('parentNode');
|
$node = $node->getAttribute('parentNode');
|
||||||
}
|
}
|
||||||
// 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) {
|
|
||||||
return $this->getVariableDefinition($node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
($node instanceof Node\Stmt\ClassLike
|
($node instanceof Node\Stmt\ClassLike
|
||||||
|
@ -310,13 +322,15 @@ class PhpDocument
|
||||||
) {
|
) {
|
||||||
// For extends, implements and type hints use the name directly
|
// For extends, implements and type hints use the name directly
|
||||||
$name = (string)$nameNode;
|
$name = (string)$nameNode;
|
||||||
} else if ($node instanceof Node\Stmt\UseUse) {
|
// Only the name node should be considered a reference, not the UseUse node itself
|
||||||
|
} else if ($node instanceof Node\Stmt\UseUse && isset($nameNode)) {
|
||||||
$name = (string)$node->name;
|
$name = (string)$node->name;
|
||||||
$parent = $node->getAttribute('parentNode');
|
$parent = $node->getAttribute('parentNode');
|
||||||
if ($parent instanceof Node\Stmt\GroupUse) {
|
if ($parent instanceof Node\Stmt\GroupUse) {
|
||||||
$name = $parent->prefix . '\\' . $name;
|
$name = $parent->prefix . '\\' . $name;
|
||||||
}
|
}
|
||||||
} else if ($node instanceof Node\Expr\New_) {
|
// Only the name node should be considered a reference, not the New_ node itself
|
||||||
|
} else if ($node instanceof Node\Expr\New_ && isset($nameNode)) {
|
||||||
if (!($node->class instanceof Node\Name)) {
|
if (!($node->class instanceof Node\Name)) {
|
||||||
// Cannot get definition of dynamic calls
|
// Cannot get definition of dynamic calls
|
||||||
return null;
|
return null;
|
||||||
|
@ -381,25 +395,50 @@ class PhpDocument
|
||||||
if (!isset($name)) {
|
if (!isset($name)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Search for the document where the class, interface, trait, function, method or property is defined
|
// If the node is a function or constant, it could be namespaced, but PHP falls back to global
|
||||||
$document = $this->project->getDefinitionDocument($name);
|
// The NameResolver therefor does not resolve these to namespaced names
|
||||||
if (!$document && $node instanceof Node\Expr\FuncCall) {
|
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||||
|
if ($node instanceof Node\Expr\FuncCall || $node instanceof Node\Expr\ConstFetch) {
|
||||||
// Find and try with namespace
|
// Find and try with namespace
|
||||||
// Namespaces aren't added automatically by NameResolver because PHP falls back to global functions
|
|
||||||
$n = $node;
|
$n = $node;
|
||||||
while (isset($n)) {
|
while (isset($n)) {
|
||||||
$n = $n->getAttribute('parentNode');
|
$n = $n->getAttribute('parentNode');
|
||||||
if ($n instanceof Node\Stmt\Namespace_) {
|
if ($n instanceof Node\Stmt\Namespace_) {
|
||||||
$name = (string)$n->name . '\\' . $name;
|
$namespacedName = (string)$n->name . '\\' . $name;
|
||||||
$document = $this->project->getDefinitionDocument($name);
|
// If the namespaced version is defined, return that
|
||||||
break;
|
// Otherwise fall back to global
|
||||||
|
if ($this->project->isDefined($namespacedName)) {
|
||||||
|
return $namespacedName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the definition node for any node
|
||||||
|
* The definition node MAY be in another document, check the ownerDocument attribute
|
||||||
|
*
|
||||||
|
* @param Node $node
|
||||||
|
* @return Node|null
|
||||||
|
*/
|
||||||
|
public function getDefinitionByNode(Node $node)
|
||||||
|
{
|
||||||
|
// 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) {
|
||||||
|
return $this->getVariableDefinition($node);
|
||||||
|
}
|
||||||
|
$fqn = $this->getReferencedFqn($node);
|
||||||
|
if (!isset($fqn)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$document = $this->project->getDefinitionDocument($fqn);
|
||||||
if (!isset($document)) {
|
if (!isset($document)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return $document->getDefinitionByFqn($name);
|
return $document->getDefinitionByFqn($fqn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,7 +15,7 @@ class Project
|
||||||
*
|
*
|
||||||
* @var PhpDocument[]
|
* @var PhpDocument[]
|
||||||
*/
|
*/
|
||||||
private $documents;
|
private $documents = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An associative array [string => PhpDocument]
|
* An associative array [string => PhpDocument]
|
||||||
|
@ -23,7 +23,7 @@ class Project
|
||||||
*
|
*
|
||||||
* @var PhpDocument[]
|
* @var PhpDocument[]
|
||||||
*/
|
*/
|
||||||
private $definitions;
|
private $definitions = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of the PHP parser
|
* Instance of the PHP parser
|
||||||
|
@ -84,6 +84,17 @@ class Project
|
||||||
return $this->definitions[$fqn] ?? null;
|
return $this->definitions[$fqn] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given FQN is defined in the project
|
||||||
|
*
|
||||||
|
* @param string $fqn The fully qualified name of the symbol
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDefined(string $fqn): bool
|
||||||
|
{
|
||||||
|
return isset($this->definitions[$fqn]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds symbols in all documents, filtered by query parameter.
|
* Finds symbols in all documents, filtered by query parameter.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue