Remove "Tolerant" alias
- Now unnecessary - Makes type annotations more readablepull/357/head
parent
836cdb77c0
commit
3dfe505727
|
@ -8,7 +8,7 @@ use LanguageServer\Index\Index;
|
||||||
use LanguageServer\ParserKind;
|
use LanguageServer\ParserKind;
|
||||||
use LanguageServer\PhpDocument;
|
use LanguageServer\PhpDocument;
|
||||||
use LanguageServer\DefinitionResolver;
|
use LanguageServer\DefinitionResolver;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use RecursiveDirectoryIterator;
|
use RecursiveDirectoryIterator;
|
||||||
use RecursiveIteratorIterator;
|
use RecursiveIteratorIterator;
|
||||||
|
@ -58,7 +58,7 @@ foreach($frameworks as $framework) {
|
||||||
$definitions = [];
|
$definitions = [];
|
||||||
|
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
$parser = new Tolerant\Parser();
|
$parser = new PhpParser\Parser();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$document = new PhpDocument($testCaseFile, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver);
|
$document = new PhpDocument($testCaseFile, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver);
|
||||||
|
|
|
@ -13,7 +13,8 @@ use LanguageServer\Protocol\{
|
||||||
CompletionItemKind
|
CompletionItemKind
|
||||||
};
|
};
|
||||||
use function LanguageServer\{strStartsWith};
|
use function LanguageServer\{strStartsWith};
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
class CompletionProvider
|
class CompletionProvider
|
||||||
{
|
{
|
||||||
|
@ -127,7 +128,7 @@ class CompletionProvider
|
||||||
|
|
||||||
$offset = $node === null ? -1 : $pos->toOffset($node->getFileContents());
|
$offset = $node === null ? -1 : $pos->toOffset($node->getFileContents());
|
||||||
if ($node !== null && $offset > $node->getEndPosition() &&
|
if ($node !== null && $offset > $node->getEndPosition() &&
|
||||||
$node->parent->getLastChild() instanceof Tolerant\MissingToken
|
$node->parent->getLastChild() instanceof PhpParser\MissingToken
|
||||||
) {
|
) {
|
||||||
$node = $node->parent;
|
$node = $node->parent;
|
||||||
}
|
}
|
||||||
|
@ -135,14 +136,14 @@ class CompletionProvider
|
||||||
$list = new CompletionList;
|
$list = new CompletionList;
|
||||||
$list->isIncomplete = true;
|
$list->isIncomplete = true;
|
||||||
|
|
||||||
if ($node instanceof Tolerant\Node\Expression\Variable &&
|
if ($node instanceof Node\Expression\Variable &&
|
||||||
$node->parent instanceof Tolerant\Node\Expression\ObjectCreationExpression &&
|
$node->parent instanceof Node\Expression\ObjectCreationExpression &&
|
||||||
$node->name instanceof Tolerant\MissingToken
|
$node->name instanceof PhpParser\MissingToken
|
||||||
) {
|
) {
|
||||||
$node = $node->parent;
|
$node = $node->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node === null || $node instanceof Tolerant\Node\Statement\InlineHtml || $pos == new Position(0, 0)) {
|
if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) {
|
||||||
$item = new CompletionItem('<?php', CompletionItemKind::KEYWORD);
|
$item = new CompletionItem('<?php', CompletionItemKind::KEYWORD);
|
||||||
$item->textEdit = new TextEdit(
|
$item->textEdit = new TextEdit(
|
||||||
new Range($pos, $pos),
|
new Range($pos, $pos),
|
||||||
|
@ -152,9 +153,9 @@ class CompletionProvider
|
||||||
}
|
}
|
||||||
// VARIABLES
|
// VARIABLES
|
||||||
elseif (
|
elseif (
|
||||||
$node instanceof Tolerant\Node\Expression\Variable &&
|
$node instanceof Node\Expression\Variable &&
|
||||||
!(
|
!(
|
||||||
$node->parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression &&
|
$node->parent instanceof Node\Expression\ScopedPropertyAccessExpression &&
|
||||||
$node->parent->memberName === $node)
|
$node->parent->memberName === $node)
|
||||||
) {
|
) {
|
||||||
// Find variables, parameters and use statements in the scope
|
// Find variables, parameters and use statements in the scope
|
||||||
|
@ -176,7 +177,7 @@ class CompletionProvider
|
||||||
// MEMBER ACCESS EXPRESSIONS
|
// MEMBER ACCESS EXPRESSIONS
|
||||||
// $a->c#
|
// $a->c#
|
||||||
// $a->#
|
// $a->#
|
||||||
elseif ($node instanceof Tolerant\Node\Expression\MemberAccessExpression) {
|
elseif ($node instanceof Node\Expression\MemberAccessExpression) {
|
||||||
$prefixes = FqnUtilities::getFqnsFromType(
|
$prefixes = FqnUtilities::getFqnsFromType(
|
||||||
$this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression)
|
$this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression)
|
||||||
);
|
);
|
||||||
|
@ -204,8 +205,8 @@ class CompletionProvider
|
||||||
// A\B\C::foo#
|
// A\B\C::foo#
|
||||||
// TODO: $a::#
|
// TODO: $a::#
|
||||||
elseif (
|
elseif (
|
||||||
($scoped = $node->parent) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
($scoped = $node->parent) instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
($scoped = $node) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
|
($scoped = $node) instanceof Node\Expression\ScopedPropertyAccessExpression
|
||||||
) {
|
) {
|
||||||
$prefixes = FqnUtilities::getFqnsFromType(
|
$prefixes = FqnUtilities::getFqnsFromType(
|
||||||
$classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier)
|
$classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier)
|
||||||
|
@ -227,13 +228,13 @@ class CompletionProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (ParserHelpers::isConstantFetch($node) ||
|
} elseif (ParserHelpers::isConstantFetch($node) ||
|
||||||
($creation = $node->parent) instanceof Tolerant\Node\Expression\ObjectCreationExpression ||
|
($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression ||
|
||||||
(($creation = $node) instanceof Tolerant\Node\Expression\ObjectCreationExpression)) {
|
(($creation = $node) instanceof Node\Expression\ObjectCreationExpression)) {
|
||||||
|
|
||||||
$class = isset($creation) ? $creation->classTypeDesignator : $node;
|
$class = isset($creation) ? $creation->classTypeDesignator : $node;
|
||||||
|
|
||||||
$prefix = $class instanceof Tolerant\Node\QualifiedName
|
$prefix = $class instanceof Node\QualifiedName
|
||||||
? (string)Tolerant\ResolvedName::buildName($class->nameParts, $class->getFileContents())
|
? (string)PhpParser\ResolvedName::buildName($class->nameParts, $class->getFileContents())
|
||||||
: $class->getText($node->getFileContents());
|
: $class->getText($node->getFileContents());
|
||||||
|
|
||||||
$namespaceDefinition = $node->getNamespaceDefinition();
|
$namespaceDefinition = $node->getNamespaceDefinition();
|
||||||
|
@ -248,11 +249,11 @@ class CompletionProvider
|
||||||
$fqnContainsPrefix = empty($prefix) || strpos($fqn, $prefix) !== false;
|
$fqnContainsPrefix = empty($prefix) || strpos($fqn, $prefix) !== false;
|
||||||
if (($def->canBeInstantiated || ($def->isGlobal && !isset($creation))) && $fqnContainsPrefix) {
|
if (($def->canBeInstantiated || ($def->isGlobal && !isset($creation))) && $fqnContainsPrefix) {
|
||||||
if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) {
|
if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) {
|
||||||
$namespacePrefix = (string)Tolerant\ResolvedName::buildName($namespaceDefinition->name->nameParts, $node->getFileContents());
|
$namespacePrefix = (string)PhpParser\ResolvedName::buildName($namespaceDefinition->name->nameParts, $node->getFileContents());
|
||||||
|
|
||||||
$isAliased = false;
|
$isAliased = false;
|
||||||
|
|
||||||
$isNotFullyQualified = !($class instanceof Tolerant\Node\QualifiedName) || !$class->isFullyQualifiedName();
|
$isNotFullyQualified = !($class instanceof Node\QualifiedName) || !$class->isFullyQualifiedName();
|
||||||
if ($isNotFullyQualified) {
|
if ($isNotFullyQualified) {
|
||||||
foreach ($namespaceImportTable as $alias => $name) {
|
foreach ($namespaceImportTable as $alias => $name) {
|
||||||
if (strStartsWith($fqn, $name)) {
|
if (strStartsWith($fqn, $name)) {
|
||||||
|
@ -297,7 +298,7 @@ class CompletionProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (ParserHelpers::isConstantFetch($node)) {
|
} elseif (ParserHelpers::isConstantFetch($node)) {
|
||||||
$prefix = (string) ($node->getResolvedName() ?? Tolerant\ResolvedName::buildName($node->nameParts, $node->getFileContents()));
|
$prefix = (string) ($node->getResolvedName() ?? PhpParser\ResolvedName::buildName($node->nameParts, $node->getFileContents()));
|
||||||
foreach (self::KEYWORDS as $keyword) {
|
foreach (self::KEYWORDS as $keyword) {
|
||||||
$item = new CompletionItem($keyword, CompletionItemKind::KEYWORD);
|
$item = new CompletionItem($keyword, CompletionItemKind::KEYWORD);
|
||||||
$item->insertText = $keyword . ' ';
|
$item->insertText = $keyword . ' ';
|
||||||
|
@ -333,11 +334,11 @@ class CompletionProvider
|
||||||
* and at each level walk all previous siblings and their children to search for definitions
|
* and at each level walk all previous siblings and their children to search for definitions
|
||||||
* of that variable
|
* of that variable
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @param string $namePrefix Prefix to filter
|
* @param string $namePrefix Prefix to filter
|
||||||
* @return array <Tolerant\Node\Expr\Variable|Tolerant\Node\Param|Tolerant\Node\Expr\ClosureUse>
|
* @return array <Node\Expr\Variable|Node\Param|Node\Expr\ClosureUse>
|
||||||
*/
|
*/
|
||||||
private function suggestVariablesAtNode(Tolerant\Node $node, string $namePrefix = ''): array
|
private function suggestVariablesAtNode(Node $node, string $namePrefix = ''): array
|
||||||
{
|
{
|
||||||
$vars = [];
|
$vars = [];
|
||||||
|
|
||||||
|
@ -375,7 +376,7 @@ class CompletionProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($level instanceof Tolerant\Node\Expression\AnonymousFunctionCreationExpression && $level->anonymousFunctionUseClause !== null) {
|
if ($level instanceof Node\Expression\AnonymousFunctionCreationExpression && $level->anonymousFunctionUseClause !== null) {
|
||||||
foreach ($level->anonymousFunctionUseClause->useVariableNameList->getValues() as $use) {
|
foreach ($level->anonymousFunctionUseClause->useVariableNameList->getValues() as $use) {
|
||||||
$useName = $use->getName();
|
$useName = $use->getName();
|
||||||
if (empty($namePrefix) || strpos($useName, $namePrefix) !== false) {
|
if (empty($namePrefix) || strpos($useName, $namePrefix) !== false) {
|
||||||
|
@ -393,24 +394,24 @@ class CompletionProvider
|
||||||
*
|
*
|
||||||
* @param Node $node
|
* @param Node $node
|
||||||
* @param string $namePrefix Prefix to filter
|
* @param string $namePrefix Prefix to filter
|
||||||
* @return Tolerant\Node\Expression\Variable[]
|
* @return Node\Expression\Variable[]
|
||||||
*/
|
*/
|
||||||
private function findVariableDefinitionsInNode(Tolerant\Node $node, string $namePrefix = ''): array
|
private function findVariableDefinitionsInNode(Node $node, string $namePrefix = ''): array
|
||||||
{
|
{
|
||||||
$vars = [];
|
$vars = [];
|
||||||
// If the child node is a variable assignment, save it
|
// If the child node is a variable assignment, save it
|
||||||
|
|
||||||
$isAssignmentToVariable = function ($node) use ($namePrefix) {
|
$isAssignmentToVariable = function ($node) use ($namePrefix) {
|
||||||
return $node instanceof Tolerant\Node\Expression\AssignmentExpression
|
return $node instanceof Node\Expression\AssignmentExpression
|
||||||
&& $node->leftOperand instanceof Tolerant\Node\Expression\Variable
|
&& $node->leftOperand instanceof Node\Expression\Variable
|
||||||
&& (empty($namePrefix) || strpos($node->leftOperand->getName(), $namePrefix) !== false);
|
&& (empty($namePrefix) || strpos($node->leftOperand->getName(), $namePrefix) !== false);
|
||||||
};
|
};
|
||||||
$isNotFunctionLike = function($node) {
|
$isNotFunctionLike = function($node) {
|
||||||
return !(
|
return !(
|
||||||
ParserHelpers::isFunctionLike($node) ||
|
ParserHelpers::isFunctionLike($node) ||
|
||||||
$node instanceof Tolerant\Node\Statement\ClassDeclaration ||
|
$node instanceof Node\Statement\ClassDeclaration ||
|
||||||
$node instanceof Tolerant\Node\Statement\InterfaceDeclaration ||
|
$node instanceof Node\Statement\InterfaceDeclaration ||
|
||||||
$node instanceof Tolerant\Node\Statement\TraitDeclaration
|
$node instanceof Node\Statement\TraitDeclaration
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use Webmozart\PathUtil\Path;
|
use Webmozart\PathUtil\Path;
|
||||||
use Sabre\Uri;
|
use Sabre\Uri;
|
||||||
use function Sabre\Event\coroutine;
|
use function Sabre\Event\coroutine;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
|
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
|
||||||
if (file_exists($file)) {
|
if (file_exists($file)) {
|
||||||
|
@ -30,7 +30,7 @@ class ComposerScripts
|
||||||
$finder = new FileSystemFilesFinder;
|
$finder = new FileSystemFilesFinder;
|
||||||
$contentRetriever = new FileSystemContentRetriever;
|
$contentRetriever = new FileSystemContentRetriever;
|
||||||
$docBlockFactory = DocBlockFactory::createInstance();
|
$docBlockFactory = DocBlockFactory::createInstance();
|
||||||
$parser = new Tolerant\Parser();
|
$parser = new PhpParser\Parser();
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
|
|
||||||
$stubsLocation = null;
|
$stubsLocation = null;
|
||||||
|
|
|
@ -5,7 +5,8 @@ namespace LanguageServer;
|
||||||
|
|
||||||
use LanguageServer\Index\ReadableIndex;
|
use LanguageServer\Index\ReadableIndex;
|
||||||
use LanguageServer\Protocol\SymbolInformation;
|
use LanguageServer\Protocol\SymbolInformation;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use phpDocumentor\Reflection\{
|
use phpDocumentor\Reflection\{
|
||||||
DocBlock, DocBlockFactory, Fqsen, Type, TypeResolver, Types
|
DocBlock, DocBlockFactory, Fqsen, Type, TypeResolver, Types
|
||||||
};
|
};
|
||||||
|
@ -46,7 +47,7 @@ class DefinitionResolver
|
||||||
/**
|
/**
|
||||||
* Builds the declaration line for a given node. Declarations with multiple lines are trimmed.
|
* Builds the declaration line for a given node. Declarations with multiple lines are trimmed.
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getDeclarationLineFromNode($node): string
|
public function getDeclarationLineFromNode($node): string
|
||||||
|
@ -82,13 +83,13 @@ class DefinitionResolver
|
||||||
/**
|
/**
|
||||||
* Gets the documentation string for a node, if it has one
|
* Gets the documentation string for a node, if it has one
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getDocumentationFromNode($node)
|
public function getDocumentationFromNode($node)
|
||||||
{
|
{
|
||||||
// Any NamespaceDefinition comments likely apply to the file, not the declaration itself.
|
// Any NamespaceDefinition comments likely apply to the file, not the declaration itself.
|
||||||
if ($node instanceof Tolerant\Node\Statement\NamespaceDefinition) {
|
if ($node instanceof Node\Statement\NamespaceDefinition) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +101,7 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// For parameters, parse the function-like declaration to get documentation for a parameter
|
// For parameters, parse the function-like declaration to get documentation for a parameter
|
||||||
if ($node instanceof Tolerant\Node\Parameter) {
|
if ($node instanceof Node\Parameter) {
|
||||||
$variableName = $node->getName();
|
$variableName = $node->getName();
|
||||||
|
|
||||||
$functionLikeDeclaration = ParserHelpers::getFunctionLikeDeclarationFromParameter($node);
|
$functionLikeDeclaration = ParserHelpers::getFunctionLikeDeclarationFromParameter($node);
|
||||||
|
@ -129,10 +130,10 @@ class DefinitionResolver
|
||||||
/**
|
/**
|
||||||
* Gets Doc Block with resolved names for a Node
|
* Gets Doc Block with resolved names for a Node
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return DocBlock | null
|
* @return DocBlock | null
|
||||||
*/
|
*/
|
||||||
private function getDocBlock(Tolerant\Node $node)
|
private function getDocBlock(Node $node)
|
||||||
{
|
{
|
||||||
// TODO make more efficient (caching, ensure import table is in right format to begin with)
|
// TODO make more efficient (caching, ensure import table is in right format to begin with)
|
||||||
$docCommentText = $node->getDocCommentText();
|
$docCommentText = $node->getDocCommentText();
|
||||||
|
@ -161,7 +162,7 @@ class DefinitionResolver
|
||||||
/**
|
/**
|
||||||
* Create a Definition for a definition node
|
* Create a Definition for a definition node
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @param string $fqn
|
* @param string $fqn
|
||||||
* @return Definition
|
* @return Definition
|
||||||
*/
|
*/
|
||||||
|
@ -171,30 +172,30 @@ class DefinitionResolver
|
||||||
$def->fqn = $fqn;
|
$def->fqn = $fqn;
|
||||||
|
|
||||||
// Determines whether the suggestion will show after "new"
|
// Determines whether the suggestion will show after "new"
|
||||||
$def->canBeInstantiated = $node instanceof Tolerant\Node\Statement\ClassDeclaration;
|
$def->canBeInstantiated = $node instanceof Node\Statement\ClassDeclaration;
|
||||||
|
|
||||||
// Interfaces, classes, traits, namespaces, functions, and global const elements
|
// Interfaces, classes, traits, namespaces, functions, and global const elements
|
||||||
$def->isGlobal = (
|
$def->isGlobal = (
|
||||||
$node instanceof Tolerant\Node\Statement\InterfaceDeclaration ||
|
$node instanceof Node\Statement\InterfaceDeclaration ||
|
||||||
$node instanceof Tolerant\Node\Statement\ClassDeclaration ||
|
$node instanceof Node\Statement\ClassDeclaration ||
|
||||||
$node instanceof Tolerant\Node\Statement\TraitDeclaration ||
|
$node instanceof Node\Statement\TraitDeclaration ||
|
||||||
|
|
||||||
($node instanceof Tolerant\Node\Statement\NamespaceDefinition && $node->name !== null) ||
|
($node instanceof Node\Statement\NamespaceDefinition && $node->name !== null) ||
|
||||||
|
|
||||||
$node instanceof Tolerant\Node\Statement\FunctionDeclaration ||
|
$node instanceof Node\Statement\FunctionDeclaration ||
|
||||||
|
|
||||||
($node instanceof Tolerant\Node\ConstElement && $node->parent->parent instanceof Tolerant\Node\Statement\ConstDeclaration)
|
($node instanceof Node\ConstElement && $node->parent->parent instanceof Node\Statement\ConstDeclaration)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Static methods and static property declarations
|
// Static methods and static property declarations
|
||||||
$def->isStatic = (
|
$def->isStatic = (
|
||||||
($node instanceof Tolerant\Node\MethodDeclaration && $node->isStatic()) ||
|
($node instanceof Node\MethodDeclaration && $node->isStatic()) ||
|
||||||
|
|
||||||
(($propertyDeclaration = ParserHelpers::tryGetPropertyDeclaration($node)) !== null
|
(($propertyDeclaration = ParserHelpers::tryGetPropertyDeclaration($node)) !== null
|
||||||
&& $propertyDeclaration->isStatic())
|
&& $propertyDeclaration->isStatic())
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($node instanceof Tolerant\Node\Statement\ClassDeclaration &&
|
if ($node instanceof Node\Statement\ClassDeclaration &&
|
||||||
// TODO - this should be bette rrpreented in the parser API
|
// TODO - this should be bette rrpreented in the parser API
|
||||||
$node->classBaseClause !== null && $node->classBaseClause->baseClass !== null)
|
$node->classBaseClause !== null && $node->classBaseClause->baseClass !== null)
|
||||||
{
|
{
|
||||||
|
@ -202,7 +203,7 @@ class DefinitionResolver
|
||||||
// TODO - why is this represented as an array?
|
// TODO - why is this represented as an array?
|
||||||
// TODO interface implementations.
|
// TODO interface implementations.
|
||||||
} elseif (
|
} elseif (
|
||||||
$node instanceof Tolerant\Node\Statement\InterfaceDeclaration &&
|
$node instanceof Node\Statement\InterfaceDeclaration &&
|
||||||
// TODO - this should be better represented in the parser API
|
// TODO - this should be better represented in the parser API
|
||||||
$node->interfaceBaseClause !== null && $node->interfaceBaseClause->interfaceNameList !== null
|
$node->interfaceBaseClause !== null && $node->interfaceBaseClause->interfaceNameList !== null
|
||||||
) {
|
) {
|
||||||
|
@ -226,7 +227,7 @@ class DefinitionResolver
|
||||||
/**
|
/**
|
||||||
* Given any node, returns the Definition object of the symbol that is referenced
|
* Given any node, returns the Definition object of the symbol that is referenced
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node Any reference node
|
* @param Node $node Any reference node
|
||||||
* @return Definition|null
|
* @return Definition|null
|
||||||
*/
|
*/
|
||||||
public function resolveReferenceNodeToDefinition($node)
|
public function resolveReferenceNodeToDefinition($node)
|
||||||
|
@ -235,8 +236,8 @@ class DefinitionResolver
|
||||||
// Variables are not indexed globally, as they stay in the file scope anyway.
|
// Variables are not indexed globally, as they stay in the file scope anyway.
|
||||||
// Ignore variable nodes that are part of ScopedPropertyAccessExpression,
|
// Ignore variable nodes that are part of ScopedPropertyAccessExpression,
|
||||||
// as the scoped property access expression node is handled separately.
|
// as the scoped property access expression node is handled separately.
|
||||||
if ($node instanceof Tolerant\Node\Expression\Variable &&
|
if ($node instanceof Node\Expression\Variable &&
|
||||||
!($parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression))
|
!($parent instanceof Node\Expression\ScopedPropertyAccessExpression))
|
||||||
{
|
{
|
||||||
// Resolve $this to the containing class definition.
|
// Resolve $this to the containing class definition.
|
||||||
if ($node->getName() === 'this' && $fqn = $this->getContainingClassFqn($node)) {
|
if ($node->getName() === 'this' && $fqn = $this->getContainingClassFqn($node)) {
|
||||||
|
@ -259,7 +260,7 @@ class DefinitionResolver
|
||||||
// If the node is a function or constant, it could be namespaced, but PHP falls back to global
|
// 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
|
// http://php.net/manual/en/language.namespaces.fallback.php
|
||||||
// TODO - verify that this is not a method
|
// TODO - verify that this is not a method
|
||||||
$globalFallback = ParserHelpers::isConstantFetch($node) || $parent instanceof Tolerant\Node\Expression\CallExpression;
|
$globalFallback = ParserHelpers::isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression;
|
||||||
// Return the Definition object from the index index
|
// Return the Definition object from the index index
|
||||||
return $this->index->getDefinition($fqn, $globalFallback);
|
return $this->index->getDefinition($fqn, $globalFallback);
|
||||||
}
|
}
|
||||||
|
@ -273,11 +274,11 @@ class DefinitionResolver
|
||||||
*/
|
*/
|
||||||
public function resolveReferenceNodeToFqn($node) {
|
public function resolveReferenceNodeToFqn($node) {
|
||||||
// TODO all name tokens should be a part of a node
|
// TODO all name tokens should be a part of a node
|
||||||
if ($node instanceof Tolerant\Node\QualifiedName) {
|
if ($node instanceof Node\QualifiedName) {
|
||||||
return $this->resolveQualifiedNameNodeToFqn($node);
|
return $this->resolveQualifiedNameNodeToFqn($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ($node instanceof Tolerant\Node\Expression\MemberAccessExpression) {
|
else if ($node instanceof Node\Expression\MemberAccessExpression) {
|
||||||
return $this->resolveMemberAccessExpressionNodeToFqn($node);
|
return $this->resolveMemberAccessExpressionNodeToFqn($node);
|
||||||
}
|
}
|
||||||
else if (ParserHelpers::isConstantFetch($node)) {
|
else if (ParserHelpers::isConstantFetch($node)) {
|
||||||
|
@ -285,13 +286,13 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
// A\B::C - constant access expression
|
// A\B::C - constant access expression
|
||||||
$node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
|
$node instanceof Node\Expression\ScopedPropertyAccessExpression
|
||||||
&& !($node->memberName instanceof Tolerant\Node\Expression\Variable)
|
&& !($node->memberName instanceof Node\Expression\Variable)
|
||||||
) {
|
) {
|
||||||
return $this->resolveScopedPropertyAccessExpressionNodeToFqn($node);
|
return $this->resolveScopedPropertyAccessExpressionNodeToFqn($node);
|
||||||
} else if (
|
} else if (
|
||||||
// A\B::$c - static property access expression
|
// A\B::$c - static property access expression
|
||||||
$node->parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression
|
$node->parent instanceof Node\Expression\ScopedPropertyAccessExpression
|
||||||
) {
|
) {
|
||||||
return $this->resolveScopedPropertyAccessExpressionNodeToFqn($node->parent);
|
return $this->resolveScopedPropertyAccessExpressionNodeToFqn($node->parent);
|
||||||
}
|
}
|
||||||
|
@ -299,36 +300,36 @@ class DefinitionResolver
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resolveQualifiedNameNodeToFqn(Tolerant\Node\QualifiedName $node) {
|
private function resolveQualifiedNameNodeToFqn(Node\QualifiedName $node) {
|
||||||
$parent = $node->parent;
|
$parent = $node->parent;
|
||||||
|
|
||||||
if ($parent instanceof Tolerant\Node\TraitSelectOrAliasClause) {
|
if ($parent instanceof Node\TraitSelectOrAliasClause) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Add use clause references
|
// Add use clause references
|
||||||
if (($useClause = $parent) instanceof Tolerant\Node\NamespaceUseGroupClause
|
if (($useClause = $parent) instanceof Node\NamespaceUseGroupClause
|
||||||
|| $useClause instanceof Tolerant\Node\NamespaceUseClause
|
|| $useClause instanceof Node\NamespaceUseClause
|
||||||
) {
|
) {
|
||||||
$contents = $node->getFileContents();
|
$contents = $node->getFileContents();
|
||||||
if ($useClause instanceof Tolerant\Node\NamespaceUseGroupClause) {
|
if ($useClause instanceof Node\NamespaceUseGroupClause) {
|
||||||
$prefix = $useClause->parent->parent->namespaceName;
|
$prefix = $useClause->parent->parent->namespaceName;
|
||||||
if ($prefix === null) {
|
if ($prefix === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$name = Tolerant\ResolvedName::buildName($prefix->nameParts, $contents);
|
$name = PhpParser\ResolvedName::buildName($prefix->nameParts, $contents);
|
||||||
$name->addNameParts($node->nameParts, $contents);
|
$name->addNameParts($node->nameParts, $contents);
|
||||||
$name = (string)$name;
|
$name = (string)$name;
|
||||||
|
|
||||||
if ($useClause->functionOrConst === null) {
|
if ($useClause->functionOrConst === null) {
|
||||||
$useClause = $node->getFirstAncestor(Tolerant\Node\Statement\NamespaceUseDeclaration::class);
|
$useClause = $node->getFirstAncestor(Node\Statement\NamespaceUseDeclaration::class);
|
||||||
if ($useClause->functionOrConst !== null && $useClause->functionOrConst->kind === Tolerant\TokenKind::FunctionKeyword) {
|
if ($useClause->functionOrConst !== null && $useClause->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword) {
|
||||||
$name .= '()';
|
$name .= '()';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $name;
|
return $name;
|
||||||
} else {
|
} else {
|
||||||
$name = (string) Tolerant\ResolvedName::buildName($node->nameParts, $contents);
|
$name = (string) PhpParser\ResolvedName::buildName($node->nameParts, $contents);
|
||||||
if ($useClause->groupClauses === null && $useClause->parent->parent->functionOrConst !== null && $useClause->parent->parent->functionOrConst->kind === Tolerant\TokenKind::FunctionKeyword) {
|
if ($useClause->groupClauses === null && $useClause->parent->parent->functionOrConst !== null && $useClause->parent->parent->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword) {
|
||||||
$name .= '()';
|
$name .= '()';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,14 +340,14 @@ class DefinitionResolver
|
||||||
// For extends, implements, type hints and classes of classes of static calls use the name directly
|
// For extends, implements, type hints and classes of classes of static calls use the name directly
|
||||||
$name = (string) ($node->getResolvedName() ?? $node->getNamespacedName());
|
$name = (string) ($node->getResolvedName() ?? $node->getNamespacedName());
|
||||||
|
|
||||||
if ($node->parent instanceof Tolerant\Node\Expression\CallExpression) {
|
if ($node->parent instanceof Node\Expression\CallExpression) {
|
||||||
$name .= '()';
|
$name .= '()';
|
||||||
}
|
}
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resolveMemberAccessExpressionNodeToFqn(Tolerant\Node\Expression\MemberAccessExpression $access) {
|
private function resolveMemberAccessExpressionNodeToFqn(Node\Expression\MemberAccessExpression $access) {
|
||||||
if ($access->memberName instanceof Tolerant\Node\Expression) {
|
if ($access->memberName instanceof Node\Expression) {
|
||||||
// Cannot get definition if right-hand side is expression
|
// Cannot get definition if right-hand side is expression
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -382,7 +383,7 @@ class DefinitionResolver
|
||||||
$classFqn = substr((string)$varType->getFqsen(), 1);
|
$classFqn = substr((string)$varType->getFqsen(), 1);
|
||||||
}
|
}
|
||||||
$memberSuffix = '->' . (string)($access->memberName->getText() ?? $access->memberName->getText($access->getFileContents()));
|
$memberSuffix = '->' . (string)($access->memberName->getText() ?? $access->memberName->getText($access->getFileContents()));
|
||||||
if ($access->parent instanceof Tolerant\Node\Expression\CallExpression) {
|
if ($access->parent instanceof Node\Expression\CallExpression) {
|
||||||
$memberSuffix .= '()';
|
$memberSuffix .= '()';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,14 +413,14 @@ class DefinitionResolver
|
||||||
return $classFqn . $memberSuffix;
|
return $classFqn . $memberSuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resolveScopedPropertyAccessExpressionNodeToFqn(Tolerant\Node\Expression\ScopedPropertyAccessExpression $scoped) {
|
private function resolveScopedPropertyAccessExpressionNodeToFqn(Node\Expression\ScopedPropertyAccessExpression $scoped) {
|
||||||
if ($scoped->scopeResolutionQualifier instanceof Tolerant\Node\Expression\Variable) {
|
if ($scoped->scopeResolutionQualifier instanceof Node\Expression\Variable) {
|
||||||
$varType = $this->getTypeFromNode($scoped->scopeResolutionQualifier);
|
$varType = $this->getTypeFromNode($scoped->scopeResolutionQualifier);
|
||||||
if ($varType === null) {
|
if ($varType === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$className = substr((string)$varType->getFqsen(), 1);
|
$className = substr((string)$varType->getFqsen(), 1);
|
||||||
} elseif ($scoped->scopeResolutionQualifier instanceof Tolerant\Node\QualifiedName) {
|
} elseif ($scoped->scopeResolutionQualifier instanceof Node\QualifiedName) {
|
||||||
$className = (string)$scoped->scopeResolutionQualifier->getResolvedName();
|
$className = (string)$scoped->scopeResolutionQualifier->getResolvedName();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -427,7 +428,7 @@ class DefinitionResolver
|
||||||
|
|
||||||
if ($className === 'self' || $className === 'static' || $className === 'parent') {
|
if ($className === 'self' || $className === 'static' || $className === 'parent') {
|
||||||
// self and static are resolved to the containing class
|
// self and static are resolved to the containing class
|
||||||
$classNode = $scoped->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
|
$classNode = $scoped->getFirstAncestor(Node\Statement\ClassDeclaration::class);
|
||||||
if ($classNode === null) {
|
if ($classNode === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -440,11 +441,11 @@ class DefinitionResolver
|
||||||
} else {
|
} else {
|
||||||
$className = (string)$classNode->getNamespacedName();
|
$className = (string)$classNode->getNamespacedName();
|
||||||
}
|
}
|
||||||
} elseif ($scoped->scopeResolutionQualifier instanceof Tolerant\Node\QualifiedName) {
|
} elseif ($scoped->scopeResolutionQualifier instanceof Node\QualifiedName) {
|
||||||
$className = $scoped->scopeResolutionQualifier->getResolvedName();
|
$className = $scoped->scopeResolutionQualifier->getResolvedName();
|
||||||
}
|
}
|
||||||
if ($scoped->memberName instanceof Tolerant\Node\Expression\Variable) {
|
if ($scoped->memberName instanceof Node\Expression\Variable) {
|
||||||
if ($scoped->parent instanceof Tolerant\Node\Expression\CallExpression) {
|
if ($scoped->parent instanceof Node\Expression\CallExpression) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$memberName = $scoped->memberName->getName();
|
$memberName = $scoped->memberName->getName();
|
||||||
|
@ -455,7 +456,7 @@ class DefinitionResolver
|
||||||
} else {
|
} else {
|
||||||
$name = (string)$className . '::' . $scoped->memberName->getText($scoped->getFileContents());
|
$name = (string)$className . '::' . $scoped->memberName->getText($scoped->getFileContents());
|
||||||
}
|
}
|
||||||
if ($scoped->parent instanceof Tolerant\Node\Expression\CallExpression) {
|
if ($scoped->parent instanceof Node\Expression\CallExpression) {
|
||||||
$name .= '()';
|
$name .= '()';
|
||||||
}
|
}
|
||||||
return $name;
|
return $name;
|
||||||
|
@ -465,12 +466,12 @@ class DefinitionResolver
|
||||||
* Returns FQN of the class a node is contained in
|
* Returns FQN of the class a node is contained in
|
||||||
* Returns null if the class is anonymous or the node is not contained in a class
|
* Returns null if the class is anonymous or the node is not contained in a class
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
private static function getContainingClassFqn(Tolerant\Node $node)
|
private static function getContainingClassFqn(Node $node)
|
||||||
{
|
{
|
||||||
$classNode = $node->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
|
$classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class);
|
||||||
if ($classNode === null) {
|
if ($classNode === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -480,18 +481,18 @@ class DefinitionResolver
|
||||||
/**
|
/**
|
||||||
* Returns the assignment or parameter node where a variable was defined
|
* Returns the assignment or parameter node where a variable was defined
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node\Expression\Variable | Tolerant\Node\Expression\ClosureUse $var The variable access
|
* @param Node\Expression\Variable | Node\Expression\ClosureUse $var The variable access
|
||||||
* @return Tolerant\Node\Expression\Assign | Tolerant\Node\Expression\AssignOp|Node\Param | Tolerant\Node\Expression\ClosureUse|null
|
* @return Node\Expression\Assign | Node\Expression\AssignOp|Node\Param | Node\Expression\ClosureUse|null
|
||||||
*/
|
*/
|
||||||
public function resolveVariableToNode($var)
|
public function resolveVariableToNode($var)
|
||||||
{
|
{
|
||||||
$n = $var;
|
$n = $var;
|
||||||
// When a use is passed, start outside the closure to not return immediately
|
// When a use is passed, start outside the closure to not return immediately
|
||||||
// Use variable vs variable parsing?
|
// Use variable vs variable parsing?
|
||||||
if ($var instanceof Tolerant\Node\UseVariableName) {
|
if ($var instanceof Node\UseVariableName) {
|
||||||
$n = $var->getFirstAncestor(Tolerant\Node\Expression\AnonymousFunctionCreationExpression::class)->parent;
|
$n = $var->getFirstAncestor(Node\Expression\AnonymousFunctionCreationExpression::class)->parent;
|
||||||
$name = $var->getName();
|
$name = $var->getName();
|
||||||
} else if ($var instanceof Tolerant\Node\Expression\Variable || $var instanceof Tolerant\Node\Parameter) {
|
} else if ($var instanceof Node\Expression\Variable || $var instanceof Node\Parameter) {
|
||||||
$name = $var->getName();
|
$name = $var->getName();
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException('$var must be Variable, Param or ClosureUse, not ' . get_class($var));
|
throw new \InvalidArgumentException('$var must be Variable, Param or ClosureUse, not ' . get_class($var));
|
||||||
|
@ -509,7 +510,7 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If it is a closure, also check use statements
|
// If it is a closure, also check use statements
|
||||||
if ($n instanceof Tolerant\Node\Expression\AnonymousFunctionCreationExpression &&
|
if ($n instanceof Node\Expression\AnonymousFunctionCreationExpression &&
|
||||||
$n->anonymousFunctionUseClause !== null &&
|
$n->anonymousFunctionUseClause !== null &&
|
||||||
$n->anonymousFunctionUseClause->useVariableNameList !== null) {
|
$n->anonymousFunctionUseClause->useVariableNameList !== null) {
|
||||||
foreach ($n->anonymousFunctionUseClause->useVariableNameList->getElements() as $use
|
foreach ($n->anonymousFunctionUseClause->useVariableNameList->getElements() as $use
|
||||||
|
@ -523,13 +524,13 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
// Check each previous sibling node for a variable assignment to that variable
|
// Check each previous sibling node for a variable assignment to that variable
|
||||||
while (($prevSibling = $n->getPreviousSibling()) !== null && $n = $prevSibling) {
|
while (($prevSibling = $n->getPreviousSibling()) !== null && $n = $prevSibling) {
|
||||||
if ($n instanceof Tolerant\Node\Statement\ExpressionStatement) {
|
if ($n instanceof Node\Statement\ExpressionStatement) {
|
||||||
$n = $n->expression;
|
$n = $n->expression;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
// TODO - clean this up
|
// TODO - clean this up
|
||||||
($n instanceof Tolerant\Node\Expression\AssignmentExpression && $n->operator->kind === Tolerant\TokenKind::EqualsToken)
|
($n instanceof Node\Expression\AssignmentExpression && $n->operator->kind === PhpParser\TokenKind::EqualsToken)
|
||||||
&& $n->leftOperand instanceof Tolerant\Node\Expression\Variable && $n->leftOperand->getName() === $name
|
&& $n->leftOperand instanceof Node\Expression\Variable && $n->leftOperand->getName() === $name
|
||||||
) {
|
) {
|
||||||
return $n;
|
return $n;
|
||||||
}
|
}
|
||||||
|
@ -543,12 +544,12 @@ class DefinitionResolver
|
||||||
* Given an expression node, resolves that expression recursively to a type.
|
* Given an expression node, resolves that expression recursively to a type.
|
||||||
* If the type could not be resolved, returns Types\Mixed.
|
* If the type could not be resolved, returns Types\Mixed.
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node\Expression $expr
|
* @param Node\Expression $expr
|
||||||
* @return \phpDocumentor\Reflection\Type|null
|
* @return \phpDocumentor\Reflection\Type|null
|
||||||
*/
|
*/
|
||||||
public function resolveExpressionNodeToType($expr)
|
public function resolveExpressionNodeToType($expr)
|
||||||
{
|
{
|
||||||
if ($expr == null || $expr instanceof Tolerant\MissingToken || $expr instanceof Tolerant\SkippedToken) {
|
if ($expr == null || $expr instanceof PhpParser\MissingToken || $expr instanceof PhpParser\SkippedToken) {
|
||||||
// TODO some members are null or Missing/SkippedToken
|
// TODO some members are null or Missing/SkippedToken
|
||||||
// How do we handle this more generally?
|
// How do we handle this more generally?
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
|
@ -556,42 +557,42 @@ class DefinitionResolver
|
||||||
|
|
||||||
// PARENTHESIZED EXPRESSION
|
// PARENTHESIZED EXPRESSION
|
||||||
// Retrieve inner expression from parenthesized expression
|
// Retrieve inner expression from parenthesized expression
|
||||||
while ($expr instanceof Tolerant\Node\Expression\ParenthesizedExpression) {
|
while ($expr instanceof Node\Expression\ParenthesizedExpression) {
|
||||||
$expr = $expr->expression;
|
$expr = $expr->expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VARIABLE
|
// VARIABLE
|
||||||
// $this -> Type\this
|
// $this -> Type\this
|
||||||
// $myVariable -> type of corresponding assignment expression
|
// $myVariable -> type of corresponding assignment expression
|
||||||
if ($expr instanceof Tolerant\Node\Expression\Variable || $expr instanceof Tolerant\Node\UseVariableName) {
|
if ($expr instanceof Node\Expression\Variable || $expr instanceof Node\UseVariableName) {
|
||||||
if ($expr->getName() === 'this') {
|
if ($expr->getName() === 'this') {
|
||||||
return new Types\This;
|
return new Types\This;
|
||||||
}
|
}
|
||||||
// Find variable definition (parameter or assignment expression)
|
// Find variable definition (parameter or assignment expression)
|
||||||
$defNode = $this->resolveVariableToNode($expr);
|
$defNode = $this->resolveVariableToNode($expr);
|
||||||
if ($defNode instanceof Tolerant\Node\Expression\AssignmentExpression || $defNode instanceof Tolerant\Node\UseVariableName) {
|
if ($defNode instanceof Node\Expression\AssignmentExpression || $defNode instanceof Node\UseVariableName) {
|
||||||
return $this->resolveExpressionNodeToType($defNode);
|
return $this->resolveExpressionNodeToType($defNode);
|
||||||
}
|
}
|
||||||
if ($defNode instanceof Tolerant\Node\Parameter) {
|
if ($defNode instanceof Node\Parameter) {
|
||||||
return $this->getTypeFromNode($defNode);
|
return $this->getTypeFromNode($defNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FUNCTION CALL
|
// FUNCTION CALL
|
||||||
// Function calls are resolved to type corresponding to their FQN
|
// Function calls are resolved to type corresponding to their FQN
|
||||||
if ($expr instanceof Tolerant\Node\Expression\CallExpression &&
|
if ($expr instanceof Node\Expression\CallExpression &&
|
||||||
!(
|
!(
|
||||||
$expr->callableExpression instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
$expr->callableExpression instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
$expr->callableExpression instanceof Tolerant\Node\Expression\MemberAccessExpression)
|
$expr->callableExpression instanceof Node\Expression\MemberAccessExpression)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Find the function definition
|
// Find the function definition
|
||||||
if ($expr->callableExpression instanceof Tolerant\Node\Expression) {
|
if ($expr->callableExpression instanceof Node\Expression) {
|
||||||
// Cannot get type for dynamic function call
|
// Cannot get type for dynamic function call
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr->callableExpression instanceof Tolerant\Node\QualifiedName) {
|
if ($expr->callableExpression instanceof Node\QualifiedName) {
|
||||||
$fqn = $expr->callableExpression->getResolvedName() ?? $expr->callableExpression->getNamespacedName();
|
$fqn = $expr->callableExpression->getResolvedName() ?? $expr->callableExpression->getNamespacedName();
|
||||||
$fqn .= '()';
|
$fqn .= '()';
|
||||||
$def = $this->index->getDefinition($fqn, true);
|
$def = $this->index->getDefinition($fqn, true);
|
||||||
|
@ -603,13 +604,13 @@ class DefinitionResolver
|
||||||
|
|
||||||
// TRUE / FALSE / NULL
|
// TRUE / FALSE / NULL
|
||||||
// Resolve true and false reserved words to Types\Boolean
|
// Resolve true and false reserved words to Types\Boolean
|
||||||
if ($expr instanceof Tolerant\Node\ReservedWord) {
|
if ($expr instanceof Node\ReservedWord) {
|
||||||
$token = $expr->children->kind;
|
$token = $expr->children->kind;
|
||||||
if ($token === Tolerant\TokenKind::TrueReservedWord || $token === Tolerant\TokenKind::FalseReservedWord) {
|
if ($token === PhpParser\TokenKind::TrueReservedWord || $token === PhpParser\TokenKind::FalseReservedWord) {
|
||||||
return new Types\Boolean;
|
return new Types\Boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($token === Tolerant\TokenKind::NullReservedWord) {
|
if ($token === PhpParser\TokenKind::NullReservedWord) {
|
||||||
return new Types\Null_;
|
return new Types\Null_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -625,8 +626,8 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// MEMBER ACCESS EXPRESSION
|
// MEMBER ACCESS EXPRESSION
|
||||||
if ($expr instanceof Tolerant\Node\Expression\MemberAccessExpression) {
|
if ($expr instanceof Node\Expression\MemberAccessExpression) {
|
||||||
if ($expr->memberName instanceof Tolerant\Node\Expression) {
|
if ($expr->memberName instanceof Node\Expression) {
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
$var = $expr->dereferencableExpression;
|
$var = $expr->dereferencableExpression;
|
||||||
|
@ -648,7 +649,7 @@ class DefinitionResolver
|
||||||
$classFqn = substr((string)$t->getFqsen(), 1);
|
$classFqn = substr((string)$t->getFqsen(), 1);
|
||||||
}
|
}
|
||||||
$fqn = $classFqn . '->' . $expr->memberName->getText($expr->getFileContents());
|
$fqn = $classFqn . '->' . $expr->memberName->getText($expr->getFileContents());
|
||||||
if ($expr->parent instanceof Tolerant\Node\Expression\CallExpression) {
|
if ($expr->parent instanceof Node\Expression\CallExpression) {
|
||||||
$fqn .= '()';
|
$fqn .= '()';
|
||||||
}
|
}
|
||||||
$def = $this->index->getDefinition($fqn);
|
$def = $this->index->getDefinition($fqn);
|
||||||
|
@ -659,7 +660,7 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCOPED PROPERTY ACCESS EXPRESSION
|
// SCOPED PROPERTY ACCESS EXPRESSION
|
||||||
if ($expr instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression) {
|
if ($expr instanceof Node\Expression\ScopedPropertyAccessExpression) {
|
||||||
$classType = $this->resolveClassNameToType($expr->scopeResolutionQualifier);
|
$classType = $this->resolveClassNameToType($expr->scopeResolutionQualifier);
|
||||||
if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null) {
|
if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null) {
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
|
@ -668,7 +669,7 @@ class DefinitionResolver
|
||||||
|
|
||||||
// TODO is there a cleaner way to do this?
|
// TODO is there a cleaner way to do this?
|
||||||
$fqn .= $expr->memberName->getText() ?? $expr->memberName->getText($expr->getFileContents());
|
$fqn .= $expr->memberName->getText() ?? $expr->memberName->getText($expr->getFileContents());
|
||||||
if ($expr->parent instanceof Tolerant\Node\Expression\CallExpression) {
|
if ($expr->parent instanceof Node\Expression\CallExpression) {
|
||||||
$fqn .= '()';
|
$fqn .= '()';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,26 +683,26 @@ class DefinitionResolver
|
||||||
// OBJECT CREATION EXPRESSION
|
// OBJECT CREATION EXPRESSION
|
||||||
// new A() => resolves to the type of the class type designator (A)
|
// new A() => resolves to the type of the class type designator (A)
|
||||||
// TODO: new $this->a => resolves to the string represented by "a"
|
// TODO: new $this->a => resolves to the string represented by "a"
|
||||||
if ($expr instanceof Tolerant\Node\Expression\ObjectCreationExpression) {
|
if ($expr instanceof Node\Expression\ObjectCreationExpression) {
|
||||||
return $this->resolveClassNameToType($expr->classTypeDesignator);
|
return $this->resolveClassNameToType($expr->classTypeDesignator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLONE EXPRESSION
|
// CLONE EXPRESSION
|
||||||
// clone($a) => resolves to the type of $a
|
// clone($a) => resolves to the type of $a
|
||||||
if ($expr instanceof Tolerant\Node\Expression\CloneExpression) {
|
if ($expr instanceof Node\Expression\CloneExpression) {
|
||||||
return $this->resolveExpressionNodeToType($expr->expression);
|
return $this->resolveExpressionNodeToType($expr->expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ASSIGNMENT EXPRESSION
|
// ASSIGNMENT EXPRESSION
|
||||||
// $a = $myExpression => resolves to the type of the right-hand operand
|
// $a = $myExpression => resolves to the type of the right-hand operand
|
||||||
if ($expr instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
if ($expr instanceof Node\Expression\AssignmentExpression) {
|
||||||
return $this->resolveExpressionNodeToType($expr->rightOperand);
|
return $this->resolveExpressionNodeToType($expr->rightOperand);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TERNARY EXPRESSION
|
// TERNARY EXPRESSION
|
||||||
// $condition ? $ifExpression : $elseExpression => reslves to type of $ifCondition or $elseExpression
|
// $condition ? $ifExpression : $elseExpression => reslves to type of $ifCondition or $elseExpression
|
||||||
// $condition ?: $elseExpression => resolves to type of $condition or $elseExpression
|
// $condition ?: $elseExpression => resolves to type of $condition or $elseExpression
|
||||||
if ($expr instanceof Tolerant\Node\Expression\TernaryExpression) {
|
if ($expr instanceof Node\Expression\TernaryExpression) {
|
||||||
// ?:
|
// ?:
|
||||||
if ($expr->ifExpression === null) {
|
if ($expr->ifExpression === null) {
|
||||||
return new Types\Compound([
|
return new Types\Compound([
|
||||||
|
@ -718,7 +719,7 @@ class DefinitionResolver
|
||||||
|
|
||||||
// NULL COALLESCE
|
// NULL COALLESCE
|
||||||
// $rightOperand ?? $leftOperand => resolves to type of $rightOperand or $leftOperand
|
// $rightOperand ?? $leftOperand => resolves to type of $rightOperand or $leftOperand
|
||||||
if ($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::QuestionQuestionToken) {
|
if ($expr instanceof Node\Expression\BinaryExpression && $expr->operator->kind === PhpParser\TokenKind::QuestionQuestionToken) {
|
||||||
// ?? operator
|
// ?? operator
|
||||||
return new Types\Compound([
|
return new Types\Compound([
|
||||||
$this->resolveExpressionNodeToType($expr->leftOperand),
|
$this->resolveExpressionNodeToType($expr->leftOperand),
|
||||||
|
@ -735,10 +736,10 @@ class DefinitionResolver
|
||||||
if (
|
if (
|
||||||
ParserHelpers::isBooleanExpression($expr)
|
ParserHelpers::isBooleanExpression($expr)
|
||||||
|
|
||||||
|| ($expr instanceof Tolerant\Node\Expression\CastExpression && $expr->castType->kind === Tolerant\TokenKind::BoolCastToken)
|
|| ($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::BoolCastToken)
|
||||||
|| ($expr instanceof Tolerant\Node\Expression\UnaryOpExpression && $expr->operator->kind === Tolerant\TokenKind::ExclamationToken)
|
|| ($expr instanceof Node\Expression\UnaryOpExpression && $expr->operator->kind === PhpParser\TokenKind::ExclamationToken)
|
||||||
|| $expr instanceof Tolerant\Node\Expression\EmptyIntrinsicExpression
|
|| $expr instanceof Node\Expression\EmptyIntrinsicExpression
|
||||||
|| $expr instanceof Tolerant\Node\Expression\IssetIntrinsicExpression
|
|| $expr instanceof Node\Expression\IssetIntrinsicExpression
|
||||||
) {
|
) {
|
||||||
return new Types\Boolean;
|
return new Types\Boolean;
|
||||||
}
|
}
|
||||||
|
@ -750,10 +751,10 @@ class DefinitionResolver
|
||||||
//
|
//
|
||||||
// TODO: Magic constants (__CLASS__, __DIR__, __FUNCTION__, __METHOD__, __NAMESPACE__, __TRAIT__, __FILE__)
|
// TODO: Magic constants (__CLASS__, __DIR__, __FUNCTION__, __METHOD__, __NAMESPACE__, __TRAIT__, __FILE__)
|
||||||
if (
|
if (
|
||||||
($expr instanceof Tolerant\Node\Expression\BinaryExpression &&
|
($expr instanceof Node\Expression\BinaryExpression &&
|
||||||
($expr->operator->kind === Tolerant\TokenKind::DotToken || $expr->operator->kind === Tolerant\TokenKind::DotEqualsToken)) ||
|
($expr->operator->kind === PhpParser\TokenKind::DotToken || $expr->operator->kind === PhpParser\TokenKind::DotEqualsToken)) ||
|
||||||
$expr instanceof Tolerant\Node\StringLiteral ||
|
$expr instanceof Node\StringLiteral ||
|
||||||
($expr instanceof Tolerant\Node\Expression\CastExpression && $expr->castType->kind === Tolerant\TokenKind::StringCastToken)
|
($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::StringCastToken)
|
||||||
) {
|
) {
|
||||||
return new Types\String_;
|
return new Types\String_;
|
||||||
}
|
}
|
||||||
|
@ -765,18 +766,18 @@ class DefinitionResolver
|
||||||
// Resolve to Types\Float
|
// Resolve to Types\Float
|
||||||
// [assignment] /=
|
// [assignment] /=
|
||||||
if (
|
if (
|
||||||
$expr instanceof Tolerant\Node\Expression\BinaryExpression &&
|
$expr instanceof Node\Expression\BinaryExpression &&
|
||||||
($operator = $expr->operator->kind)
|
($operator = $expr->operator->kind)
|
||||||
&& ($operator === Tolerant\TokenKind::PlusToken ||
|
&& ($operator === PhpParser\TokenKind::PlusToken ||
|
||||||
$operator === Tolerant\TokenKind::AsteriskAsteriskToken ||
|
$operator === PhpParser\TokenKind::AsteriskAsteriskToken ||
|
||||||
$operator === Tolerant\TokenKind::AsteriskToken ||
|
$operator === PhpParser\TokenKind::AsteriskToken ||
|
||||||
$operator === Tolerant\TokenKind::MinusToken ||
|
$operator === PhpParser\TokenKind::MinusToken ||
|
||||||
|
|
||||||
// Assignment expressions (TODO: consider making this a type of AssignmentExpression rather than kind of BinaryExpression)
|
// Assignment expressions (TODO: consider making this a type of AssignmentExpression rather than kind of BinaryExpression)
|
||||||
$operator === Tolerant\TokenKind::AsteriskEqualsToken||
|
$operator === PhpParser\TokenKind::AsteriskEqualsToken||
|
||||||
$operator === Tolerant\TokenKind::AsteriskAsteriskEqualsToken ||
|
$operator === PhpParser\TokenKind::AsteriskAsteriskEqualsToken ||
|
||||||
$operator === Tolerant\TokenKind::MinusEqualsToken ||
|
$operator === PhpParser\TokenKind::MinusEqualsToken ||
|
||||||
$operator === Tolerant\TokenKind::PlusEqualsToken
|
$operator === PhpParser\TokenKind::PlusEqualsToken
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
|
@ -787,8 +788,8 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
return new Types\Float_;
|
return new Types\Float_;
|
||||||
} else if (
|
} else if (
|
||||||
$expr instanceof Tolerant\Node\Expression\BinaryExpression &&
|
$expr instanceof Node\Expression\BinaryExpression &&
|
||||||
$expr->operator->kind === Tolerant\TokenKind::SlashEqualsToken
|
$expr->operator->kind === PhpParser\TokenKind::SlashEqualsToken
|
||||||
) {
|
) {
|
||||||
return new Types\Float_;
|
return new Types\Float_;
|
||||||
}
|
}
|
||||||
|
@ -799,13 +800,13 @@ class DefinitionResolver
|
||||||
// TODO: Magic constants (__LINE__)
|
// TODO: Magic constants (__LINE__)
|
||||||
if (
|
if (
|
||||||
// TODO: consider different Node types of float/int, also better property name (not "children")
|
// TODO: consider different Node types of float/int, also better property name (not "children")
|
||||||
($expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::IntegerLiteralToken) ||
|
($expr instanceof Node\NumericLiteral && $expr->children->kind === PhpParser\TokenKind::IntegerLiteralToken) ||
|
||||||
$expr instanceof Tolerant\Node\Expression\BinaryExpression && (
|
$expr instanceof Node\Expression\BinaryExpression && (
|
||||||
($operator = $expr->operator->kind)
|
($operator = $expr->operator->kind)
|
||||||
&& ($operator === Tolerant\TokenKind::LessThanEqualsGreaterThanToken ||
|
&& ($operator === PhpParser\TokenKind::LessThanEqualsGreaterThanToken ||
|
||||||
$operator === Tolerant\TokenKind::AmpersandToken ||
|
$operator === PhpParser\TokenKind::AmpersandToken ||
|
||||||
$operator === Tolerant\TokenKind::CaretToken ||
|
$operator === PhpParser\TokenKind::CaretToken ||
|
||||||
$operator === Tolerant\TokenKind::BarToken)
|
$operator === PhpParser\TokenKind::BarToken)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return new Types\Integer;
|
return new Types\Integer;
|
||||||
|
@ -816,9 +817,9 @@ class DefinitionResolver
|
||||||
// [operator] /
|
// [operator] /
|
||||||
// [cast] (double)
|
// [cast] (double)
|
||||||
if (
|
if (
|
||||||
$expr instanceof Tolerant\Node\NumericLiteral && $expr->children->kind === Tolerant\TokenKind::FloatingLiteralToken ||
|
$expr instanceof Node\NumericLiteral && $expr->children->kind === PhpParser\TokenKind::FloatingLiteralToken ||
|
||||||
($expr instanceof Tolerant\Node\Expression\CastExpression && $expr->castType->kind === Tolerant\TokenKind::DoubleCastToken) ||
|
($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::DoubleCastToken) ||
|
||||||
($expr instanceof Tolerant\Node\Expression\BinaryExpression && $expr->operator->kind === Tolerant\TokenKind::SlashToken)
|
($expr instanceof Node\Expression\BinaryExpression && $expr->operator->kind === PhpParser\TokenKind::SlashToken)
|
||||||
) {
|
) {
|
||||||
return new Types\Float_;
|
return new Types\Float_;
|
||||||
}
|
}
|
||||||
|
@ -827,7 +828,7 @@ class DefinitionResolver
|
||||||
// Resolve to Types\Array (Types\Compound of value and key types)
|
// Resolve to Types\Array (Types\Compound of value and key types)
|
||||||
// [a, b, c]
|
// [a, b, c]
|
||||||
// [1=>"hello", "hi"=>1, 4=>[]]s
|
// [1=>"hello", "hi"=>1, 4=>[]]s
|
||||||
if ($expr instanceof Tolerant\Node\Expression\ArrayCreationExpression) {
|
if ($expr instanceof Node\Expression\ArrayCreationExpression) {
|
||||||
$valueTypes = [];
|
$valueTypes = [];
|
||||||
$keyTypes = [];
|
$keyTypes = [];
|
||||||
if ($expr->arrayElements !== null) {
|
if ($expr->arrayElements !== null) {
|
||||||
|
@ -858,7 +859,7 @@ class DefinitionResolver
|
||||||
// SUBSCRIPT EXPRESSION
|
// SUBSCRIPT EXPRESSION
|
||||||
// $myArray[3]
|
// $myArray[3]
|
||||||
// $myArray{"hello"}
|
// $myArray{"hello"}
|
||||||
if ($expr instanceof Tolerant\Node\Expression\SubscriptExpression) {
|
if ($expr instanceof Node\Expression\SubscriptExpression) {
|
||||||
$varType = $this->resolveExpressionNodeToType($expr->postfixExpression);
|
$varType = $this->resolveExpressionNodeToType($expr->postfixExpression);
|
||||||
if (!($varType instanceof Types\Array_)) {
|
if (!($varType instanceof Types\Array_)) {
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
|
@ -868,12 +869,12 @@ class DefinitionResolver
|
||||||
|
|
||||||
// SCRIPT INCLUSION EXPRESSION
|
// SCRIPT INCLUSION EXPRESSION
|
||||||
// include, require, include_once, require_once
|
// include, require, include_once, require_once
|
||||||
if ($expr instanceof Tolerant\Node\Expression\ScriptInclusionExpression) {
|
if ($expr instanceof Node\Expression\ScriptInclusionExpression) {
|
||||||
// TODO: resolve path to PhpDocument and find return statement
|
// TODO: resolve path to PhpDocument and find return statement
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($expr instanceof Tolerant\Node\QualifiedName) {
|
if ($expr instanceof Node\QualifiedName) {
|
||||||
return $this->resolveClassNameToType($expr);
|
return $this->resolveClassNameToType($expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -885,15 +886,15 @@ class DefinitionResolver
|
||||||
* Takes any class name node (from a static method call, or new node) and returns a Type object
|
* Takes any class name node (from a static method call, or new node) and returns a Type object
|
||||||
* Resolves keywords like self, static and parent
|
* Resolves keywords like self, static and parent
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node || Tolerant\Token $class
|
* @param Node || PhpParser\Token $class
|
||||||
* @return Type
|
* @return Type
|
||||||
*/
|
*/
|
||||||
public function resolveClassNameToType($class): Type
|
public function resolveClassNameToType($class): Type
|
||||||
{
|
{
|
||||||
if ($class instanceof Tolerant\Node\Expression) {
|
if ($class instanceof Node\Expression) {
|
||||||
return new Types\Mixed;
|
return new Types\Mixed;
|
||||||
}
|
}
|
||||||
if ($class instanceof Tolerant\Token && $class->kind === Tolerant\TokenKind::ClassKeyword) {
|
if ($class instanceof PhpParser\Token && $class->kind === PhpParser\TokenKind::ClassKeyword) {
|
||||||
// Anonymous class
|
// Anonymous class
|
||||||
return new Types\Object_;
|
return new Types\Object_;
|
||||||
}
|
}
|
||||||
|
@ -903,7 +904,7 @@ class DefinitionResolver
|
||||||
return new Types\Static_;
|
return new Types\Static_;
|
||||||
}
|
}
|
||||||
if ($className === 'self' || $className === 'parent') {
|
if ($className === 'self' || $className === 'parent') {
|
||||||
$classNode = $class->getFirstAncestor(Tolerant\Node\Statement\ClassDeclaration::class);
|
$classNode = $class->getFirstAncestor(Node\Statement\ClassDeclaration::class);
|
||||||
if ($className === 'parent') {
|
if ($className === 'parent') {
|
||||||
if ($classNode === null || $classNode->classBaseClause === null) {
|
if ($classNode === null || $classNode->classBaseClause === null) {
|
||||||
return new Types\Object_;
|
return new Types\Object_;
|
||||||
|
@ -933,7 +934,7 @@ class DefinitionResolver
|
||||||
* If it is unknown, will be Types\Mixed.
|
* If it is unknown, will be Types\Mixed.
|
||||||
* Returns null if the node does not have a type.
|
* Returns null if the node does not have a type.
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return \phpDocumentor\Reflection\Type|null
|
* @return \phpDocumentor\Reflection\Type|null
|
||||||
*/
|
*/
|
||||||
public function getTypeFromNode($node)
|
public function getTypeFromNode($node)
|
||||||
|
@ -948,7 +949,7 @@ class DefinitionResolver
|
||||||
// Get the type of the parameter:
|
// Get the type of the parameter:
|
||||||
// 1. Doc block
|
// 1. Doc block
|
||||||
// 2. Parameter type and default
|
// 2. Parameter type and default
|
||||||
if ($node instanceof Tolerant\Node\Parameter) {
|
if ($node instanceof Node\Parameter) {
|
||||||
// Parameters
|
// Parameters
|
||||||
// Get the doc block for the the function call
|
// Get the doc block for the the function call
|
||||||
// /**
|
// /**
|
||||||
|
@ -968,7 +969,7 @@ class DefinitionResolver
|
||||||
// function foo(MyClass $a)
|
// function foo(MyClass $a)
|
||||||
if ($node->typeDeclaration !== null) {
|
if ($node->typeDeclaration !== null) {
|
||||||
// Use PHP7 return type hint
|
// Use PHP7 return type hint
|
||||||
if ($node->typeDeclaration instanceof Tolerant\Token) {
|
if ($node->typeDeclaration instanceof PhpParser\Token) {
|
||||||
// Resolve a string like "bool" to a type object
|
// Resolve a string like "bool" to a type object
|
||||||
$type = $this->typeResolver->resolve($node->typeDeclaration->getText($node->getFileContents()));
|
$type = $this->typeResolver->resolve($node->typeDeclaration->getText($node->getFileContents()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1003,9 +1004,9 @@ class DefinitionResolver
|
||||||
// Use @return tag
|
// Use @return tag
|
||||||
return $returnTags[0]->getType();
|
return $returnTags[0]->getType();
|
||||||
}
|
}
|
||||||
if ($node->returnType !== null && !($node->returnType instanceof Tolerant\MissingToken)) {
|
if ($node->returnType !== null && !($node->returnType instanceof PhpParser\MissingToken)) {
|
||||||
// Use PHP7 return type hint
|
// Use PHP7 return type hint
|
||||||
if ($node->returnType instanceof Tolerant\Token) {
|
if ($node->returnType instanceof PhpParser\Token) {
|
||||||
// Resolve a string like "bool" to a type object
|
// Resolve a string like "bool" to a type object
|
||||||
return $this->typeResolver->resolve($node->returnType->getText($node->getFileContents()));
|
return $this->typeResolver->resolve($node->returnType->getText($node->getFileContents()));
|
||||||
}
|
}
|
||||||
|
@ -1022,7 +1023,7 @@ class DefinitionResolver
|
||||||
ParserHelpers::tryGetPropertyDeclaration($node) ??
|
ParserHelpers::tryGetPropertyDeclaration($node) ??
|
||||||
ParserHelpers::tryGetConstOrClassConstDeclaration($node)
|
ParserHelpers::tryGetConstOrClassConstDeclaration($node)
|
||||||
) !== null ||
|
) !== null ||
|
||||||
($node = $node->parent) instanceof Tolerant\Node\Expression\AssignmentExpression)
|
($node = $node->parent) instanceof Node\Expression\AssignmentExpression)
|
||||||
{
|
{
|
||||||
$declarationNode = $declarationNode ?? $node;
|
$declarationNode = $declarationNode ?? $node;
|
||||||
|
|
||||||
|
@ -1037,14 +1038,14 @@ class DefinitionResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the expression
|
// Resolve the expression
|
||||||
if ($declarationNode instanceof Tolerant\Node\PropertyDeclaration) {
|
if ($declarationNode instanceof Node\PropertyDeclaration) {
|
||||||
// TODO should have default
|
// TODO should have default
|
||||||
if (isset($node->parent->rightOperand)) {
|
if (isset($node->parent->rightOperand)) {
|
||||||
return $this->resolveExpressionNodeToType($node->parent->rightOperand);
|
return $this->resolveExpressionNodeToType($node->parent->rightOperand);
|
||||||
}
|
}
|
||||||
} else if ($node instanceof Tolerant\Node\ConstElement) {
|
} else if ($node instanceof Node\ConstElement) {
|
||||||
return $this->resolveExpressionNodeToType($node->assignment);
|
return $this->resolveExpressionNodeToType($node->assignment);
|
||||||
} else if ($node instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
} else if ($node instanceof Node\Expression\AssignmentExpression) {
|
||||||
return $this->resolveExpressionNodeToType($node->rightOperand);
|
return $this->resolveExpressionNodeToType($node->rightOperand);
|
||||||
}
|
}
|
||||||
// TODO: read @property tags of class
|
// TODO: read @property tags of class
|
||||||
|
@ -1061,7 +1062,7 @@ class DefinitionResolver
|
||||||
* Returns the fully qualified name (FQN) that is defined by a node
|
* Returns the fully qualified name (FQN) that is defined by a node
|
||||||
* Returns null if the node does not declare any symbol that can be referenced by an FQN
|
* Returns null if the node does not declare any symbol that can be referenced by an FQN
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public static function getDefinedFqn($node)
|
public static function getDefinedFqn($node)
|
||||||
|
@ -1074,23 +1075,23 @@ class DefinitionResolver
|
||||||
// interface C { } A\B\C
|
// interface C { } A\B\C
|
||||||
// trait C { } A\B\C
|
// trait C { } A\B\C
|
||||||
if (
|
if (
|
||||||
$node instanceof Tolerant\Node\Statement\ClassDeclaration ||
|
$node instanceof Node\Statement\ClassDeclaration ||
|
||||||
$node instanceof Tolerant\Node\Statement\InterfaceDeclaration ||
|
$node instanceof Node\Statement\InterfaceDeclaration ||
|
||||||
$node instanceof Tolerant\Node\Statement\TraitDeclaration
|
$node instanceof Node\Statement\TraitDeclaration
|
||||||
) {
|
) {
|
||||||
return (string) $node->getNamespacedName();
|
return (string) $node->getNamespacedName();
|
||||||
}
|
}
|
||||||
|
|
||||||
// INPUT OUTPUT:
|
// INPUT OUTPUT:
|
||||||
// namespace A\B; A\B
|
// namespace A\B; A\B
|
||||||
else if ($node instanceof Tolerant\Node\Statement\NamespaceDefinition && $node->name instanceof Tolerant\Node\QualifiedName) {
|
else if ($node instanceof Node\Statement\NamespaceDefinition && $node->name instanceof Node\QualifiedName) {
|
||||||
$name = (string) Tolerant\ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
|
$name = (string) PhpParser\ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
|
||||||
return \count($name) > 0 ? $name : null;
|
return \count($name) > 0 ? $name : null;
|
||||||
}
|
}
|
||||||
// INPUT OUTPUT:
|
// INPUT OUTPUT:
|
||||||
// namespace A\B;
|
// namespace A\B;
|
||||||
// function a(); A\B\a();
|
// function a(); A\B\a();
|
||||||
else if ($node instanceof Tolerant\Node\Statement\FunctionDeclaration) {
|
else if ($node instanceof Node\Statement\FunctionDeclaration) {
|
||||||
// Function: use functionName() as the name
|
// Function: use functionName() as the name
|
||||||
$name = (string)$node->getNamespacedName();
|
$name = (string)$node->getNamespacedName();
|
||||||
return \count($name) > 0 ? $name . '()' : null;
|
return \count($name) > 0 ? $name . '()' : null;
|
||||||
|
@ -1101,13 +1102,13 @@ class DefinitionResolver
|
||||||
// function a () {} A\B\C->a()
|
// function a () {} A\B\C->a()
|
||||||
// static function b() {} A\B\C::b()
|
// static function b() {} A\B\C::b()
|
||||||
// }
|
// }
|
||||||
else if ($node instanceof Tolerant\Node\MethodDeclaration) {
|
else if ($node instanceof Node\MethodDeclaration) {
|
||||||
// Class method: use ClassName->methodName() as name
|
// Class method: use ClassName->methodName() as name
|
||||||
$class = $node->getFirstAncestor(
|
$class = $node->getFirstAncestor(
|
||||||
Tolerant\Node\Expression\ObjectCreationExpression::class,
|
Node\Expression\ObjectCreationExpression::class,
|
||||||
Tolerant\Node\Statement\ClassDeclaration::class,
|
Node\Statement\ClassDeclaration::class,
|
||||||
Tolerant\Node\Statement\InterfaceDeclaration::class,
|
Node\Statement\InterfaceDeclaration::class,
|
||||||
Tolerant\Node\Statement\TraitDeclaration::class
|
Node\Statement\TraitDeclaration::class
|
||||||
);
|
);
|
||||||
if (!isset($class->name)) {
|
if (!isset($class->name)) {
|
||||||
// Ignore anonymous classes
|
// Ignore anonymous classes
|
||||||
|
@ -1130,10 +1131,10 @@ class DefinitionResolver
|
||||||
($propertyDeclaration = ParserHelpers::tryGetPropertyDeclaration($node)) !== null &&
|
($propertyDeclaration = ParserHelpers::tryGetPropertyDeclaration($node)) !== null &&
|
||||||
($classDeclaration =
|
($classDeclaration =
|
||||||
$node->getFirstAncestor(
|
$node->getFirstAncestor(
|
||||||
Tolerant\Node\Expression\ObjectCreationExpression::class,
|
Node\Expression\ObjectCreationExpression::class,
|
||||||
Tolerant\Node\Statement\ClassDeclaration::class,
|
Node\Statement\ClassDeclaration::class,
|
||||||
Tolerant\Node\Statement\InterfaceDeclaration::class,
|
Node\Statement\InterfaceDeclaration::class,
|
||||||
Tolerant\Node\Statement\TraitDeclaration::class
|
Node\Statement\TraitDeclaration::class
|
||||||
)
|
)
|
||||||
) !== null && isset($classDeclaration->name))
|
) !== null && isset($classDeclaration->name))
|
||||||
{
|
{
|
||||||
|
@ -1154,17 +1155,17 @@ class DefinitionResolver
|
||||||
// const $a, $b = 4 A\B\C::$a(), A\B\C::$b
|
// const $a, $b = 4 A\B\C::$a(), A\B\C::$b
|
||||||
// }
|
// }
|
||||||
else if (($constDeclaration = ParserHelpers::tryGetConstOrClassConstDeclaration($node)) !== null) {
|
else if (($constDeclaration = ParserHelpers::tryGetConstOrClassConstDeclaration($node)) !== null) {
|
||||||
if ($constDeclaration instanceof Tolerant\Node\Statement\ConstDeclaration) {
|
if ($constDeclaration instanceof Node\Statement\ConstDeclaration) {
|
||||||
// Basic constant: use CONSTANT_NAME as name
|
// Basic constant: use CONSTANT_NAME as name
|
||||||
return (string)$node->getNamespacedName();
|
return (string)$node->getNamespacedName();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class constant: use ClassName::CONSTANT_NAME as name
|
// Class constant: use ClassName::CONSTANT_NAME as name
|
||||||
$classDeclaration = $constDeclaration->getFirstAncestor(
|
$classDeclaration = $constDeclaration->getFirstAncestor(
|
||||||
Tolerant\Node\Expression\ObjectCreationExpression::class,
|
Node\Expression\ObjectCreationExpression::class,
|
||||||
Tolerant\Node\Statement\ClassDeclaration::class,
|
Node\Statement\ClassDeclaration::class,
|
||||||
Tolerant\Node\Statement\InterfaceDeclaration::class,
|
Node\Statement\InterfaceDeclaration::class,
|
||||||
Tolerant\Node\Statement\TraitDeclaration::class
|
Node\Statement\TraitDeclaration::class
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isset($classDeclaration->name)) {
|
if (!isset($classDeclaration->name)) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace LanguageServer;
|
namespace LanguageServer;
|
||||||
|
|
||||||
use phpDocumentor\Reflection\{Type, Types};
|
use phpDocumentor\Reflection\{Type, Types};
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
class FqnUtilities
|
class FqnUtilities
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,68 +3,69 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace LanguageServer;
|
namespace LanguageServer;
|
||||||
|
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
class ParserHelpers {
|
class ParserHelpers {
|
||||||
public static function isConstantFetch(Tolerant\Node $node) : bool {
|
public static function isConstantFetch(Node $node) : bool {
|
||||||
$parent = $node->parent;
|
$parent = $node->parent;
|
||||||
return
|
return
|
||||||
(
|
(
|
||||||
$node instanceof Tolerant\Node\QualifiedName &&
|
$node instanceof Node\QualifiedName &&
|
||||||
(
|
(
|
||||||
// $node->parent instanceof Tolerant\Node\Statement\ExpressionStatement ||
|
// $node->parent instanceof Node\Statement\ExpressionStatement ||
|
||||||
$parent instanceof Tolerant\Node\Expression ||
|
$parent instanceof Node\Expression ||
|
||||||
$parent instanceof Tolerant\Node\DelimitedList\ExpressionList ||
|
$parent instanceof Node\DelimitedList\ExpressionList ||
|
||||||
$parent instanceof Tolerant\Node\ArrayElement ||
|
$parent instanceof Node\ArrayElement ||
|
||||||
($parent instanceof Tolerant\Node\Parameter && $node->parent->default === $node) ||
|
($parent instanceof Node\Parameter && $node->parent->default === $node) ||
|
||||||
$parent instanceof Tolerant\Node\StatementNode ||
|
$parent instanceof Node\StatementNode ||
|
||||||
$parent instanceof Tolerant\Node\CaseStatementNode
|
$parent instanceof Node\CaseStatementNode
|
||||||
) &&
|
) &&
|
||||||
!(
|
!(
|
||||||
$parent instanceof Tolerant\Node\Expression\MemberAccessExpression ||
|
$parent instanceof Node\Expression\MemberAccessExpression ||
|
||||||
$parent instanceof Tolerant\Node\Expression\CallExpression ||
|
$parent instanceof Node\Expression\CallExpression ||
|
||||||
$parent instanceof Tolerant\Node\Expression\ObjectCreationExpression ||
|
$parent instanceof Node\Expression\ObjectCreationExpression ||
|
||||||
$parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
$parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
self::isFunctionLike($parent) ||
|
self::isFunctionLike($parent) ||
|
||||||
(
|
(
|
||||||
$parent instanceof Tolerant\Node\Expression\BinaryExpression &&
|
$parent instanceof Node\Expression\BinaryExpression &&
|
||||||
$parent->operator->kind === Tolerant\TokenKind::InstanceOfKeyword
|
$parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getFunctionLikeDeclarationFromParameter(Tolerant\Node\Parameter $node) {
|
public static function getFunctionLikeDeclarationFromParameter(Node\Parameter $node) {
|
||||||
return $node->parent->parent;
|
return $node->parent->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isFunctionLike(Tolerant\Node $node) {
|
public static function isFunctionLike(Node $node) {
|
||||||
return
|
return
|
||||||
$node instanceof Tolerant\Node\Statement\FunctionDeclaration ||
|
$node instanceof Node\Statement\FunctionDeclaration ||
|
||||||
$node instanceof Tolerant\Node\MethodDeclaration ||
|
$node instanceof Node\MethodDeclaration ||
|
||||||
$node instanceof Tolerant\Node\Expression\AnonymousFunctionCreationExpression;
|
$node instanceof Node\Expression\AnonymousFunctionCreationExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isBooleanExpression($expression) : bool {
|
public static function isBooleanExpression($expression) : bool {
|
||||||
if (!($expression instanceof Tolerant\Node\Expression\BinaryExpression)) {
|
if (!($expression instanceof Node\Expression\BinaryExpression)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
switch ($expression->operator->kind) {
|
switch ($expression->operator->kind) {
|
||||||
case Tolerant\TokenKind::InstanceOfKeyword:
|
case PhpParser\TokenKind::InstanceOfKeyword:
|
||||||
case Tolerant\TokenKind::GreaterThanToken:
|
case PhpParser\TokenKind::GreaterThanToken:
|
||||||
case Tolerant\TokenKind::GreaterThanEqualsToken:
|
case PhpParser\TokenKind::GreaterThanEqualsToken:
|
||||||
case Tolerant\TokenKind::LessThanToken:
|
case PhpParser\TokenKind::LessThanToken:
|
||||||
case Tolerant\TokenKind::LessThanEqualsToken:
|
case PhpParser\TokenKind::LessThanEqualsToken:
|
||||||
case Tolerant\TokenKind::AndKeyword:
|
case PhpParser\TokenKind::AndKeyword:
|
||||||
case Tolerant\TokenKind::AmpersandAmpersandToken:
|
case PhpParser\TokenKind::AmpersandAmpersandToken:
|
||||||
case Tolerant\TokenKind::LessThanEqualsGreaterThanToken:
|
case PhpParser\TokenKind::LessThanEqualsGreaterThanToken:
|
||||||
case Tolerant\TokenKind::OrKeyword:
|
case PhpParser\TokenKind::OrKeyword:
|
||||||
case Tolerant\TokenKind::BarBarToken:
|
case PhpParser\TokenKind::BarBarToken:
|
||||||
case Tolerant\TokenKind::XorKeyword:
|
case PhpParser\TokenKind::XorKeyword:
|
||||||
case Tolerant\TokenKind::ExclamationEqualsEqualsToken:
|
case PhpParser\TokenKind::ExclamationEqualsEqualsToken:
|
||||||
case Tolerant\TokenKind::ExclamationEqualsToken:
|
case PhpParser\TokenKind::ExclamationEqualsToken:
|
||||||
case Tolerant\TokenKind::CaretToken:
|
case PhpParser\TokenKind::CaretToken:
|
||||||
case Tolerant\TokenKind::EqualsEqualsEqualsToken:
|
case PhpParser\TokenKind::EqualsEqualsEqualsToken:
|
||||||
case Tolerant\TokenKind::EqualsToken:
|
case PhpParser\TokenKind::EqualsToken:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -73,13 +74,13 @@ class ParserHelpers {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to get the parent property declaration given a Node
|
* Tries to get the parent property declaration given a Node
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return Tolerant\Node\PropertyDeclaration | null $node
|
* @return Node\PropertyDeclaration | null $node
|
||||||
*/
|
*/
|
||||||
public static function tryGetPropertyDeclaration(Tolerant\Node $node) {
|
public static function tryGetPropertyDeclaration(Node $node) {
|
||||||
if ($node instanceof Tolerant\Node\Expression\Variable &&
|
if ($node instanceof Node\Expression\Variable &&
|
||||||
(($propertyDeclaration = $node->parent->parent) instanceof Tolerant\Node\PropertyDeclaration ||
|
(($propertyDeclaration = $node->parent->parent) instanceof Node\PropertyDeclaration ||
|
||||||
($propertyDeclaration = $propertyDeclaration->parent) instanceof Tolerant\Node\PropertyDeclaration)
|
($propertyDeclaration = $propertyDeclaration->parent) instanceof Node\PropertyDeclaration)
|
||||||
) {
|
) {
|
||||||
return $propertyDeclaration;
|
return $propertyDeclaration;
|
||||||
}
|
}
|
||||||
|
@ -88,14 +89,14 @@ class ParserHelpers {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to get the parent ConstDeclaration or ClassConstDeclaration given a Node
|
* Tries to get the parent ConstDeclaration or ClassConstDeclaration given a Node
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return Tolerant\Node\Statement\ConstDeclaration | Tolerant\Node\ClassConstDeclaration | null $node
|
* @return Node\Statement\ConstDeclaration | Node\ClassConstDeclaration | null $node
|
||||||
*/
|
*/
|
||||||
public static function tryGetConstOrClassConstDeclaration(Tolerant\Node $node) {
|
public static function tryGetConstOrClassConstDeclaration(Node $node) {
|
||||||
if (
|
if (
|
||||||
$node instanceof Tolerant\Node\ConstElement && (
|
$node instanceof Node\ConstElement && (
|
||||||
($constDeclaration = $node->parent->parent) instanceof Tolerant\Node\ClassConstDeclaration ||
|
($constDeclaration = $node->parent->parent) instanceof Node\ClassConstDeclaration ||
|
||||||
$constDeclaration instanceof Tolerant\Node\Statement\ConstDeclaration )
|
$constDeclaration instanceof Node\Statement\ConstDeclaration )
|
||||||
) {
|
) {
|
||||||
return $constDeclaration;
|
return $constDeclaration;
|
||||||
}
|
}
|
||||||
|
@ -105,15 +106,15 @@ class ParserHelpers {
|
||||||
/**
|
/**
|
||||||
* Returns true if the node is a usage of `define`.
|
* Returns true if the node is a usage of `define`.
|
||||||
* e.g. define('TEST_DEFINE_CONSTANT', false);
|
* e.g. define('TEST_DEFINE_CONSTANT', false);
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function isConstDefineExpression(Tolerant\Node $node): bool {
|
public static function isConstDefineExpression(Node $node): bool {
|
||||||
return $node instanceof Tolerant\Node\Expression\CallExpression
|
return $node instanceof Node\Expression\CallExpression
|
||||||
&& $node->callableExpression instanceof Tolerant\Node\QualifiedName
|
&& $node->callableExpression instanceof Node\QualifiedName
|
||||||
&& strtolower($node->callableExpression->getText()) === 'define'
|
&& strtolower($node->callableExpression->getText()) === 'define'
|
||||||
&& isset($node->argumentExpressionList->children[0])
|
&& isset($node->argumentExpressionList->children[0])
|
||||||
&& $node->argumentExpressionList->children[0]->expression instanceof Tolerant\Node\StringLiteral
|
&& $node->argumentExpressionList->children[0]->expression instanceof Node\StringLiteral
|
||||||
&& isset($node->argumentExpressionList->children[2]);
|
&& isset($node->argumentExpressionList->children[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,8 @@ use LanguageServer\Index\Index;
|
||||||
use LanguageServer\Protocol\{
|
use LanguageServer\Protocol\{
|
||||||
Diagnostic, Position, Range
|
Diagnostic, Position, Range
|
||||||
};
|
};
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
|
|
||||||
class PhpDocument
|
class PhpDocument
|
||||||
|
@ -15,7 +16,7 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* The PHPParser instance
|
* The PHPParser instance
|
||||||
*
|
*
|
||||||
* @var Tolerant\Parser
|
* @var PhpParser\Parser
|
||||||
*/
|
*/
|
||||||
private $parser;
|
private $parser;
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* The AST of the document
|
* The AST of the document
|
||||||
*
|
*
|
||||||
* @var Tolerant\Node
|
* @var Node
|
||||||
*/
|
*/
|
||||||
private $stmts;
|
private $stmts;
|
||||||
|
|
||||||
|
@ -69,14 +70,14 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* Map from fully qualified name (FQN) to Node
|
* Map from fully qualified name (FQN) to Node
|
||||||
*
|
*
|
||||||
* @var Tolerant\Node
|
* @var Node
|
||||||
*/
|
*/
|
||||||
private $definitionNodes;
|
private $definitionNodes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map from fully qualified name (FQN) to array of nodes that reference the symbol
|
* Map from fully qualified name (FQN) to array of nodes that reference the symbol
|
||||||
*
|
*
|
||||||
* @var Tolerant\Node[][]
|
* @var Node[][]
|
||||||
*/
|
*/
|
||||||
private $referenceNodes;
|
private $referenceNodes;
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ class PhpDocument
|
||||||
* @param string $uri The URI of the document
|
* @param string $uri The URI of the document
|
||||||
* @param string $content The content of the document
|
* @param string $content The content of the document
|
||||||
* @param Index $index The Index to register definitions and references to
|
* @param Index $index The Index to register definitions and references to
|
||||||
* @param Tolerant\Parser $parser The PhpParser instance
|
* @param PhpParser\Parser $parser The PhpParser instance
|
||||||
* @param DocBlockFactory $docBlockFactory The DocBlockFactory instance to parse docblocks
|
* @param DocBlockFactory $docBlockFactory The DocBlockFactory instance to parse docblocks
|
||||||
* @param DefinitionResolver $definitionResolver The DefinitionResolver to resolve definitions to symbols in the workspace
|
* @param DefinitionResolver $definitionResolver The DefinitionResolver to resolve definitions to symbols in the workspace
|
||||||
*/
|
*/
|
||||||
|
@ -115,7 +116,7 @@ class PhpDocument
|
||||||
* Get all references of a fully qualified name
|
* Get all references of a fully qualified name
|
||||||
*
|
*
|
||||||
* @param string $fqn The fully qualified name of the symbol
|
* @param string $fqn The fully qualified name of the symbol
|
||||||
* @return Tolerant\Node[]
|
* @return Node[]
|
||||||
*/
|
*/
|
||||||
public function getReferenceNodesByFqn(string $fqn)
|
public function getReferenceNodesByFqn(string $fqn)
|
||||||
{
|
{
|
||||||
|
@ -220,7 +221,7 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* Returns the AST of the document
|
* Returns the AST of the document
|
||||||
*
|
*
|
||||||
* @return Tolerant\Node | null
|
* @return Node | null
|
||||||
*/
|
*/
|
||||||
public function getStmts()
|
public function getStmts()
|
||||||
{
|
{
|
||||||
|
@ -231,7 +232,7 @@ class PhpDocument
|
||||||
* Returns the node at a specified position
|
* Returns the node at a specified position
|
||||||
*
|
*
|
||||||
* @param Position $position
|
* @param Position $position
|
||||||
* @return Tolerant\Node|null
|
* @return Node|null
|
||||||
*/
|
*/
|
||||||
public function getNodeAtPosition(Position $position)
|
public function getNodeAtPosition(Position $position)
|
||||||
{
|
{
|
||||||
|
@ -267,7 +268,7 @@ class PhpDocument
|
||||||
* Returns the definition node for a fully qualified name
|
* Returns the definition node for a fully qualified name
|
||||||
*
|
*
|
||||||
* @param string $fqn
|
* @param string $fqn
|
||||||
* @return Tolerant\Node|null
|
* @return Node|null
|
||||||
*/
|
*/
|
||||||
public function getDefinitionNodeByFqn(string $fqn)
|
public function getDefinitionNodeByFqn(string $fqn)
|
||||||
{
|
{
|
||||||
|
@ -277,7 +278,7 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* Returns a map from fully qualified name (FQN) to Nodes defined in this document
|
* Returns a map from fully qualified name (FQN) to Nodes defined in this document
|
||||||
*
|
*
|
||||||
* @return Tolerant\Node[]
|
* @return Node[]
|
||||||
*/
|
*/
|
||||||
public function getDefinitionNodes()
|
public function getDefinitionNodes()
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ use LanguageServer\Index\ProjectIndex;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use Sabre\Event\Promise;
|
use Sabre\Event\Promise;
|
||||||
use function Sabre\Event\coroutine;
|
use function Sabre\Event\coroutine;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes care of loading documents and managing "open" documents
|
* Takes care of loading documents and managing "open" documents
|
||||||
|
@ -38,7 +38,7 @@ class PhpDocumentLoader
|
||||||
private $parser;
|
private $parser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Tolerant\Parser
|
* @var PhpParser\Parser
|
||||||
*/
|
*/
|
||||||
private $tolerantParser;
|
private $tolerantParser;
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class PhpDocumentLoader
|
||||||
$this->contentRetriever = $contentRetriever;
|
$this->contentRetriever = $contentRetriever;
|
||||||
$this->projectIndex = $projectIndex;
|
$this->projectIndex = $projectIndex;
|
||||||
$this->definitionResolver = $definitionResolver;
|
$this->definitionResolver = $definitionResolver;
|
||||||
$this->parser = new Tolerant\Parser();
|
$this->parser = new PhpParser\Parser();
|
||||||
$this->docBlockFactory = DocBlockFactory::createInstance();
|
$this->docBlockFactory = DocBlockFactory::createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace LanguageServer\Protocol;
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a location inside a resource, such as a line inside a text file.
|
* Represents a location inside a resource, such as a line inside a text file.
|
||||||
|
@ -22,12 +23,12 @@ class Location
|
||||||
/**
|
/**
|
||||||
* Returns the location of the node
|
* Returns the location of the node
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function fromNode($node)
|
public static function fromNode($node)
|
||||||
{
|
{
|
||||||
$range = Tolerant\PositionUtilities::getRangeFromPosition($node->getStart(), $node->getWidth(), $node->getFileContents());
|
$range = PhpParser\PositionUtilities::getRangeFromPosition($node->getStart(), $node->getWidth(), $node->getFileContents());
|
||||||
return new self($node->getUri(), new Range(
|
return new self($node->getUri(), new Range(
|
||||||
new Position($range->start->line, $range->start->character),
|
new Position($range->start->line, $range->start->character),
|
||||||
new Position($range->end->line, $range->end->character)
|
new Position($range->end->line, $range->end->character)
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace LanguageServer\Protocol;
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A range in a text document expressed as (zero-based) start and end positions.
|
* A range in a text document expressed as (zero-based) start and end positions.
|
||||||
|
@ -26,12 +27,12 @@ class Range
|
||||||
/**
|
/**
|
||||||
* Returns the range the node spans
|
* Returns the range the node spans
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function fromNode($node)
|
public static function fromNode($node)
|
||||||
{
|
{
|
||||||
$range = Tolerant\PositionUtilities::getRangeFromPosition($node->getStart(), $node->getWidth(), $node->getFileContents());
|
$range = PhpParser\PositionUtilities::getRangeFromPosition($node->getStart(), $node->getWidth(), $node->getFileContents());
|
||||||
|
|
||||||
return new self(
|
return new self(
|
||||||
new Position($range->start->line, $range->start->character),
|
new Position($range->start->line, $range->start->character),
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace LanguageServer\Protocol;
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,16 +43,16 @@ class SymbolInformation
|
||||||
/**
|
/**
|
||||||
* Converts a Node to a SymbolInformation
|
* Converts a Node to a SymbolInformation
|
||||||
*
|
*
|
||||||
* @param Tolerant\Node $node
|
* @param Node $node
|
||||||
* @param string $fqn If given, $containerName will be extracted from it
|
* @param string $fqn If given, $containerName will be extracted from it
|
||||||
* @return SymbolInformation|null
|
* @return SymbolInformation|null
|
||||||
*/
|
*/
|
||||||
public static function fromNode($node, string $fqn = null)
|
public static function fromNode($node, string $fqn = null)
|
||||||
{
|
{
|
||||||
$symbol = new self;
|
$symbol = new self;
|
||||||
if ($node instanceof Tolerant\Node\Statement\ClassDeclaration) {
|
if ($node instanceof Node\Statement\ClassDeclaration) {
|
||||||
$symbol->kind = SymbolKind::CLASS_;
|
$symbol->kind = SymbolKind::CLASS_;
|
||||||
} else if ($node instanceof Tolerant\Node\Statement\TraitDeclaration) {
|
} else if ($node instanceof Node\Statement\TraitDeclaration) {
|
||||||
$symbol->kind = SymbolKind::CLASS_;
|
$symbol->kind = SymbolKind::CLASS_;
|
||||||
}
|
}
|
||||||
else if (\LanguageServer\ParserHelpers::isConstDefineExpression($node)) {
|
else if (\LanguageServer\ParserHelpers::isConstDefineExpression($node)) {
|
||||||
|
@ -59,49 +60,49 @@ class SymbolInformation
|
||||||
// define('TEST_DEFINE_CONSTANT', false);
|
// define('TEST_DEFINE_CONSTANT', false);
|
||||||
$symbol->kind = SymbolKind::CONSTANT;
|
$symbol->kind = SymbolKind::CONSTANT;
|
||||||
$symbol->name = $node->argumentExpressionList->children[0]->expression->getStringContentsText();
|
$symbol->name = $node->argumentExpressionList->children[0]->expression->getStringContentsText();
|
||||||
} else if ($node instanceof Tolerant\Node\Statement\InterfaceDeclaration) {
|
} else if ($node instanceof Node\Statement\InterfaceDeclaration) {
|
||||||
$symbol->kind = SymbolKind::INTERFACE;
|
$symbol->kind = SymbolKind::INTERFACE;
|
||||||
} else if ($node instanceof Tolerant\Node\Statement\NamespaceDefinition) {
|
} else if ($node instanceof Node\Statement\NamespaceDefinition) {
|
||||||
$symbol->kind = SymbolKind::NAMESPACE;
|
$symbol->kind = SymbolKind::NAMESPACE;
|
||||||
} else if ($node instanceof Tolerant\Node\Statement\FunctionDeclaration) {
|
} else if ($node instanceof Node\Statement\FunctionDeclaration) {
|
||||||
$symbol->kind = SymbolKind::FUNCTION;
|
$symbol->kind = SymbolKind::FUNCTION;
|
||||||
} else if ($node instanceof Tolerant\Node\MethodDeclaration) {
|
} else if ($node instanceof Node\MethodDeclaration) {
|
||||||
$nameText = $node->getName();
|
$nameText = $node->getName();
|
||||||
if ($nameText === '__construct' || $nameText === '__destruct') {
|
if ($nameText === '__construct' || $nameText === '__destruct') {
|
||||||
$symbol->kind = SymbolKind::CONSTRUCTOR;
|
$symbol->kind = SymbolKind::CONSTRUCTOR;
|
||||||
} else {
|
} else {
|
||||||
$symbol->kind = SymbolKind::METHOD;
|
$symbol->kind = SymbolKind::METHOD;
|
||||||
}
|
}
|
||||||
} else if ($node instanceof Tolerant\Node\Expression\Variable && $node->getFirstAncestor(Tolerant\Node\PropertyDeclaration::class) !== null) {
|
} else if ($node instanceof Node\Expression\Variable && $node->getFirstAncestor(Node\PropertyDeclaration::class) !== null) {
|
||||||
$symbol->kind = SymbolKind::PROPERTY;
|
$symbol->kind = SymbolKind::PROPERTY;
|
||||||
} else if ($node instanceof Tolerant\Node\ConstElement) {
|
} else if ($node instanceof Node\ConstElement) {
|
||||||
$symbol->kind = SymbolKind::CONSTANT;
|
$symbol->kind = SymbolKind::CONSTANT;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (
|
else if (
|
||||||
(
|
(
|
||||||
($node instanceof Tolerant\Node\Expression\AssignmentExpression)
|
($node instanceof Node\Expression\AssignmentExpression)
|
||||||
&& $node->leftOperand instanceof Tolerant\Node\Expression\Variable
|
&& $node->leftOperand instanceof Node\Expression\Variable
|
||||||
)
|
)
|
||||||
|| $node instanceof Tolerant\Node\UseVariableName
|
|| $node instanceof Node\UseVariableName
|
||||||
|| $node instanceof Tolerant\Node\Parameter
|
|| $node instanceof Node\Parameter
|
||||||
) {
|
) {
|
||||||
$symbol->kind = SymbolKind::VARIABLE;
|
$symbol->kind = SymbolKind::VARIABLE;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node instanceof Tolerant\Node\Expression\AssignmentExpression) {
|
if ($node instanceof Node\Expression\AssignmentExpression) {
|
||||||
if ($node->leftOperand instanceof Tolerant\Node\Expression\Variable) {
|
if ($node->leftOperand instanceof Node\Expression\Variable) {
|
||||||
$symbol->name = $node->leftOperand->getName();
|
$symbol->name = $node->leftOperand->getName();
|
||||||
} elseif ($node->leftOperand instanceof Tolerant\Token) {
|
} elseif ($node->leftOperand instanceof PhpParser\Token) {
|
||||||
$symbol->name = trim($node->leftOperand->getText($node->getFileContents()), "$");
|
$symbol->name = trim($node->leftOperand->getText($node->getFileContents()), "$");
|
||||||
}
|
}
|
||||||
} else if ($node instanceof Tolerant\Node\UseVariableName) {
|
} else if ($node instanceof Node\UseVariableName) {
|
||||||
$symbol->name = $node->getName();
|
$symbol->name = $node->getName();
|
||||||
} else if (isset($node->name)) {
|
} else if (isset($node->name)) {
|
||||||
if ($node->name instanceof Tolerant\Node\QualifiedName) {
|
if ($node->name instanceof Node\QualifiedName) {
|
||||||
$symbol->name = (string)Tolerant\ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
|
$symbol->name = (string)PhpParser\ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
|
||||||
} else {
|
} else {
|
||||||
$symbol->name = ltrim((string)$node->name->getText($node->getFileContents()), "$");
|
$symbol->name = ltrim((string)$node->name->getText($node->getFileContents()), "$");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ use LanguageServer\Index\ReadableIndex;
|
||||||
use LanguageServer\Protocol\{
|
use LanguageServer\Protocol\{
|
||||||
FormattingOptions, Hover, Location, MarkedString, Position, Range, ReferenceContext, SymbolDescriptor, SymbolLocationInformation, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier
|
FormattingOptions, Hover, Location, MarkedString, Position, Range, ReferenceContext, SymbolDescriptor, SymbolLocationInformation, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier
|
||||||
};
|
};
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use Sabre\Event\Promise;
|
use Sabre\Event\Promise;
|
||||||
use Sabre\Uri;
|
use Sabre\Uri;
|
||||||
use function LanguageServer\{
|
use function LanguageServer\{
|
||||||
|
@ -183,24 +184,24 @@ class TextDocument
|
||||||
// by traversing the AST
|
// by traversing the AST
|
||||||
if (
|
if (
|
||||||
|
|
||||||
($node instanceof Tolerant\Node\Expression\Variable && !($node->getParent()->getParent() instanceof Tolerant\Node\PropertyDeclaration))
|
($node instanceof Node\Expression\Variable && !($node->getParent()->getParent() instanceof Node\PropertyDeclaration))
|
||||||
|| $node instanceof Tolerant\Node\Parameter
|
|| $node instanceof Node\Parameter
|
||||||
|| $node instanceof Tolerant\Node\UseVariableName
|
|| $node instanceof Node\UseVariableName
|
||||||
) {
|
) {
|
||||||
if (isset($node->name) && $node->name instanceof Tolerant\Node\Expression) {
|
if (isset($node->name) && $node->name instanceof Node\Expression) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Find function/method/closure scope
|
// Find function/method/closure scope
|
||||||
$n = $node;
|
$n = $node;
|
||||||
|
|
||||||
$n = $n->getFirstAncestor(Tolerant\Node\Statement\FunctionDeclaration::class, Tolerant\Node\MethodDeclaration::class, Tolerant\Node\Expression\AnonymousFunctionCreationExpression::class, Tolerant\Node\SourceFileNode::class);
|
$n = $n->getFirstAncestor(Node\Statement\FunctionDeclaration::class, Node\MethodDeclaration::class, Node\Expression\AnonymousFunctionCreationExpression::class, Node\SourceFileNode::class);
|
||||||
|
|
||||||
if ($n === null) {
|
if ($n === null) {
|
||||||
$n = $node->getFirstAncestor(Tolerant\Node\Statement\ExpressionStatement::class)->getParent();
|
$n = $node->getFirstAncestor(Node\Statement\ExpressionStatement::class)->getParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($n->getDescendantNodes() as $descendantNode) {
|
foreach ($n->getDescendantNodes() as $descendantNode) {
|
||||||
if ($descendantNode instanceof Tolerant\Node\Expression\Variable &&
|
if ($descendantNode instanceof Node\Expression\Variable &&
|
||||||
$descendantNode->getName() === $node->getName()
|
$descendantNode->getName() === $node->getName()
|
||||||
) {
|
) {
|
||||||
$locations[] = Location::fromNode($descendantNode);
|
$locations[] = Location::fromNode($descendantNode);
|
||||||
|
|
|
@ -7,12 +7,13 @@ use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, Te
|
||||||
use LanguageServer\Index\Index;
|
use LanguageServer\Index\Index;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use Sabre\Uri;
|
use Sabre\Uri;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
class TreeAnalyzer {
|
class TreeAnalyzer {
|
||||||
private $parser;
|
private $parser;
|
||||||
|
|
||||||
/** @var Tolerant\Node */
|
/** @var Node */
|
||||||
private $stmts;
|
private $stmts;
|
||||||
|
|
||||||
private $diagnostics;
|
private $diagnostics;
|
||||||
|
@ -21,7 +22,7 @@ class TreeAnalyzer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TreeAnalyzer constructor.
|
* TreeAnalyzer constructor.
|
||||||
* @param Tolerant\Parser $parser
|
* @param PhpParser\Parser $parser
|
||||||
* @param $content
|
* @param $content
|
||||||
* @param $docBlockFactory
|
* @param $docBlockFactory
|
||||||
* @param DefinitionResolver $definitionResolver
|
* @param DefinitionResolver $definitionResolver
|
||||||
|
@ -40,7 +41,7 @@ class TreeAnalyzer {
|
||||||
$this->collectDefinitionsAndReferences($this->stmts);
|
$this->collectDefinitionsAndReferences($this->stmts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function collectDefinitionsAndReferences(Tolerant\Node $stmts) {
|
public function collectDefinitionsAndReferences(Node $stmts) {
|
||||||
foreach ($stmts::CHILD_NAMES as $name) {
|
foreach ($stmts::CHILD_NAMES as $name) {
|
||||||
$node = $stmts->$name;
|
$node = $stmts->$name;
|
||||||
|
|
||||||
|
@ -50,19 +51,19 @@ class TreeAnalyzer {
|
||||||
|
|
||||||
if (\is_array($node)) {
|
if (\is_array($node)) {
|
||||||
foreach ($node as $child) {
|
foreach ($node as $child) {
|
||||||
if ($child instanceof Tolerant\Node) {
|
if ($child instanceof Node) {
|
||||||
$this->update($child);
|
$this->update($child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node instanceof Tolerant\Node) {
|
if ($node instanceof Node) {
|
||||||
$this->update($node);
|
$this->update($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($_error = Tolerant\DiagnosticsProvider::checkDiagnostics($node)) !== null) {
|
if (($_error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) {
|
||||||
$range = Tolerant\PositionUtilities::getRangeFromPosition($_error->start, $_error->length, $this->content);
|
$range = PhpParser\PositionUtilities::getRangeFromPosition($_error->start, $_error->length, $this->content);
|
||||||
|
|
||||||
$this->diagnostics[] = new Diagnostic(
|
$this->diagnostics[] = new Diagnostic(
|
||||||
$_error->message,
|
$_error->message,
|
||||||
|
@ -88,14 +89,14 @@ class TreeAnalyzer {
|
||||||
$parent = $node->parent;
|
$parent = $node->parent;
|
||||||
if (!(
|
if (!(
|
||||||
(
|
(
|
||||||
// $node->parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
// $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
($node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
($node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
$node instanceof Tolerant\Node\Expression\MemberAccessExpression)
|
$node instanceof Node\Expression\MemberAccessExpression)
|
||||||
&& !(
|
&& !(
|
||||||
$node->parent instanceof Tolerant\Node\Expression\CallExpression ||
|
$node->parent instanceof Node\Expression\CallExpression ||
|
||||||
$node->memberName instanceof Tolerant\Token
|
$node->memberName instanceof PhpParser\Token
|
||||||
))
|
))
|
||||||
|| ($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart()))
|
|| ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart()))
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
||||||
|
@ -103,9 +104,9 @@ class TreeAnalyzer {
|
||||||
$this->addReference($fqn, $node);
|
$this->addReference($fqn, $node);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$node instanceof Tolerant\Node\QualifiedName
|
$node instanceof Node\QualifiedName
|
||||||
&& ($node->isQualifiedName() || $node->parent instanceof Tolerant\Node\NamespaceUseClause)
|
&& ($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause)
|
||||||
&& !($parent instanceof Tolerant\Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
|
&& !($parent instanceof Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// Add references for each referenced namespace
|
// Add references for each referenced namespace
|
||||||
|
@ -120,10 +121,10 @@ class TreeAnalyzer {
|
||||||
// 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
|
||||||
if (ParserHelpers::isConstantFetch($node) ||
|
if (ParserHelpers::isConstantFetch($node) ||
|
||||||
($parent instanceof Tolerant\Node\Expression\CallExpression
|
($parent instanceof Node\Expression\CallExpression
|
||||||
&& !(
|
&& !(
|
||||||
$node instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ||
|
$node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
$node instanceof Tolerant\Node\Expression\MemberAccessExpression
|
$node instanceof Node\Expression\MemberAccessExpression
|
||||||
))) {
|
))) {
|
||||||
$parts = explode('\\', $fqn);
|
$parts = explode('\\', $fqn);
|
||||||
if (count($parts) > 1) {
|
if (count($parts) > 1) {
|
||||||
|
@ -141,7 +142,7 @@ class TreeAnalyzer {
|
||||||
return $this->diagnostics ?? [];
|
return $this->diagnostics ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addReference(string $fqn, Tolerant\Node $node)
|
private function addReference(string $fqn, Node $node)
|
||||||
{
|
{
|
||||||
if (!isset($this->referenceNodes[$fqn])) {
|
if (!isset($this->referenceNodes[$fqn])) {
|
||||||
$this->referenceNodes[$fqn] = [];
|
$this->referenceNodes[$fqn] = [];
|
||||||
|
|
|
@ -6,13 +6,13 @@ namespace LanguageServer\Tests;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Index\Index;
|
use LanguageServer\Index\Index;
|
||||||
use LanguageServer\DefinitionResolver;
|
use LanguageServer\DefinitionResolver;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
class DefinitionResolverTest extends TestCase
|
class DefinitionResolverTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testCreateDefinitionFromNode()
|
public function testCreateDefinitionFromNode()
|
||||||
{
|
{
|
||||||
$parser = new Tolerant\Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$doc = new MockPhpDocument;
|
$doc = new MockPhpDocument;
|
||||||
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class DefinitionResolverTest extends TestCase
|
||||||
|
|
||||||
public function testGetTypeFromNode()
|
public function testGetTypeFromNode()
|
||||||
{
|
{
|
||||||
$parser = new Tolerant\Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$doc = new MockPhpDocument;
|
$doc = new MockPhpDocument;
|
||||||
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class DefinitionResolverTest extends TestCase
|
||||||
public function testGetDefinedFqnForIncompleteDefine()
|
public function testGetDefinedFqnForIncompleteDefine()
|
||||||
{
|
{
|
||||||
// define('XXX') (only one argument) must not introduce a new symbol
|
// define('XXX') (only one argument) must not introduce a new symbol
|
||||||
$parser = new Tolerant\Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$doc = new MockPhpDocument;
|
$doc = new MockPhpDocument;
|
||||||
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE');", $doc->getUri());
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE');", $doc->getUri());
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class DefinitionResolverTest extends TestCase
|
||||||
|
|
||||||
public function testGetDefinedFqnForDefine()
|
public function testGetDefinedFqnForDefine()
|
||||||
{
|
{
|
||||||
$parser = new Tolerant\Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$doc = new MockPhpDocument;
|
$doc = new MockPhpDocument;
|
||||||
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ use LanguageServer\{
|
||||||
};
|
};
|
||||||
use LanguageServer\Index\{Index};
|
use LanguageServer\Index\{Index};
|
||||||
use function LanguageServer\pathToUri;
|
use function LanguageServer\pathToUri;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
class DefinitionCollectorTest extends TestCase
|
class DefinitionCollectorTest extends TestCase
|
||||||
{
|
{
|
||||||
|
@ -37,21 +38,21 @@ class DefinitionCollectorTest extends TestCase
|
||||||
'TestNamespace\\Example->__destruct()'
|
'TestNamespace\\Example->__destruct()'
|
||||||
], array_keys($defNodes));
|
], array_keys($defNodes));
|
||||||
|
|
||||||
$this->assertInstanceOf(Tolerant\Node\ConstElement::class, $defNodes['TestNamespace\\TEST_CONST']);
|
$this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TEST_CONST']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\TestClass']);
|
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\TestClass']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\ConstElement::class, $defNodes['TestNamespace\\TestClass::TEST_CLASS_CONST']);
|
$this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TestClass::TEST_CLASS_CONST']);
|
||||||
// TODO - should we parse properties more strictly?
|
// TODO - should we parse properties more strictly?
|
||||||
$this->assertInstanceOf(Tolerant\Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass::$staticTestProperty']);
|
$this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass::$staticTestProperty']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass->testProperty']);
|
$this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass->testProperty']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass::staticTestMethod()']);
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass::staticTestMethod()']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass->testMethod()']);
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass->testMethod()']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\TraitDeclaration::class, $defNodes['TestNamespace\\TestTrait']);
|
$this->assertInstanceOf(Node\Statement\TraitDeclaration::class, $defNodes['TestNamespace\\TestTrait']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\InterfaceDeclaration::class, $defNodes['TestNamespace\\TestInterface']);
|
$this->assertInstanceOf(Node\Statement\InterfaceDeclaration::class, $defNodes['TestNamespace\\TestInterface']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\FunctionDeclaration::class, $defNodes['TestNamespace\\test_function()']);
|
$this->assertInstanceOf(Node\Statement\FunctionDeclaration::class, $defNodes['TestNamespace\\test_function()']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\ChildClass']);
|
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\ChildClass']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\Example']);
|
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\Example']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__construct()']);
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__construct()']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__destruct()']);
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__destruct()']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDoesNotCollectReferences()
|
public function testDoesNotCollectReferences()
|
||||||
|
@ -60,8 +61,8 @@ class DefinitionCollectorTest extends TestCase
|
||||||
$defNodes = $this->collectDefinitions($path);
|
$defNodes = $this->collectDefinitions($path);
|
||||||
|
|
||||||
$this->assertEquals(['TestNamespace', 'TestNamespace\\whatever()'], array_keys($defNodes));
|
$this->assertEquals(['TestNamespace', 'TestNamespace\\whatever()'], array_keys($defNodes));
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\NamespaceDefinition::class, $defNodes['TestNamespace']);
|
$this->assertInstanceOf(Node\Statement\NamespaceDefinition::class, $defNodes['TestNamespace']);
|
||||||
$this->assertInstanceOf(Tolerant\Node\Statement\FunctionDeclaration::class, $defNodes['TestNamespace\\whatever()']);
|
$this->assertInstanceOf(Node\Statement\FunctionDeclaration::class, $defNodes['TestNamespace\\whatever()']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +71,7 @@ class DefinitionCollectorTest extends TestCase
|
||||||
private function collectDefinitions($path): array
|
private function collectDefinitions($path): array
|
||||||
{
|
{
|
||||||
$uri = pathToUri($path);
|
$uri = pathToUri($path);
|
||||||
$parser = new Tolerant\Parser();
|
$parser = new PhpParser\Parser();
|
||||||
|
|
||||||
$docBlockFactory = DocBlockFactory::createInstance();
|
$docBlockFactory = DocBlockFactory::createInstance();
|
||||||
$index = new Index;
|
$index = new Index;
|
||||||
|
|
|
@ -12,7 +12,8 @@ use LanguageServer\Index\{
|
||||||
use LanguageServer\Protocol\{
|
use LanguageServer\Protocol\{
|
||||||
Position
|
Position
|
||||||
};
|
};
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use function LanguageServer\isVendored;
|
use function LanguageServer\isVendored;
|
||||||
|
@ -21,7 +22,7 @@ class PhpDocumentTest extends TestCase
|
||||||
{
|
{
|
||||||
public function createDocument(string $uri, string $content)
|
public function createDocument(string $uri, string $content)
|
||||||
{
|
{
|
||||||
$parser = new Tolerant\Parser();
|
$parser = new PhpParser\Parser();
|
||||||
$docBlockFactory = DocBlockFactory::createInstance();
|
$docBlockFactory = DocBlockFactory::createInstance();
|
||||||
$index = new Index;
|
$index = new Index;
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
|
@ -44,7 +45,7 @@ class PhpDocumentTest extends TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
private function assertQualifiedName($node) {
|
private function assertQualifiedName($node) {
|
||||||
$this->assertInstanceOf(Tolerant\Node\QualifiedName::class, $node);
|
$this->assertInstanceOf(Node\QualifiedName::class, $node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsVendored()
|
public function testIsVendored()
|
||||||
|
|
|
@ -19,7 +19,7 @@ use AdvancedJsonRpc;
|
||||||
use RecursiveDirectoryIterator;
|
use RecursiveDirectoryIterator;
|
||||||
use RecursiveIteratorIterator;
|
use RecursiveIteratorIterator;
|
||||||
use Sabre\Event\Loop;
|
use Sabre\Event\Loop;
|
||||||
use Microsoft\PhpParser as Tolerant;
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
$frameworksDir = realpath(__DIR__ . '/../../validation/frameworks');
|
$frameworksDir = realpath(__DIR__ . '/../../validation/frameworks');
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ class ValidationTest extends TestCase
|
||||||
global $frameworksDir;
|
global $frameworksDir;
|
||||||
|
|
||||||
$index = new Index();
|
$index = new Index();
|
||||||
$parser = new Tolerant\Parser();
|
$parser = new PhpParser\Parser();
|
||||||
$docBlockFactory = DocBlockFactory::createInstance();
|
$docBlockFactory = DocBlockFactory::createInstance();
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue