diff --git a/composer.json b/composer.json index a1aedfd..b9c413d 100644 --- a/composer.json +++ b/composer.json @@ -54,7 +54,8 @@ }, "files" : [ "src/utils.php", - "src/FqnUtilities.php" + "src/FqnUtilities.php", + "src/ParserHelpers.php" ] }, "autoload-dev": { diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 5457812..6e083ff 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -228,7 +228,7 @@ class CompletionProvider } } } - } elseif (ParserHelpers::isConstantFetch($node) || + } elseif (ParserHelpers\isConstantFetch($node) || ($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression || (($creation = $node) instanceof Node\Expression\ObjectCreationExpression)) { $class = isset($creation) ? $creation->classTypeDesignator : $node; @@ -296,7 +296,7 @@ class CompletionProvider $list->items[] = $item; } } - } elseif (ParserHelpers::isConstantFetch($node)) { + } elseif (ParserHelpers\isConstantFetch($node)) { $prefix = (string) ($node->getResolvedName() ?? PhpParser\ResolvedName::buildName($node->nameParts, $node->getFileContents())); foreach (self::KEYWORDS as $keyword) { $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); @@ -353,7 +353,7 @@ class CompletionProvider // Walk the AST upwards until a scope boundary is met $level = $node; - while ($level && !ParserHelpers::isFunctionLike($level)) { + while ($level && !ParserHelpers\isFunctionLike($level)) { // Walk siblings before the node $sibling = $level; while ($sibling = $sibling->getPreviousSibling()) { @@ -367,7 +367,7 @@ class CompletionProvider // If the traversal ended because a function was met, // also add its parameters and closure uses to the result list - if ($level && ParserHelpers::isFunctionLike($level) && $level->parameters !== null) { + if ($level && ParserHelpers\isFunctionLike($level) && $level->parameters !== null) { foreach ($level->parameters->getValues() as $param) { $paramName = $param->getName(); if (empty($namePrefix) || strpos($paramName, $namePrefix) !== false) { diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 098220b..23de895 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -56,8 +56,8 @@ class DefinitionResolver // - [PropertyDeclaration] // public $a, [$b = 3], $c; => public $b = 3; // - [ConstDeclaration | ClassConstDeclaration] // "const A = 3, [B = 4];" => "const B = 4;" if ( - ($declaration = ParserHelpers::tryGetPropertyDeclaration($node)) && ($elements = $declaration->propertyElements) || - ($declaration = ParserHelpers::tryGetConstOrClassConstDeclaration($node)) && ($elements = $declaration->constElements) + ($declaration = ParserHelpers\tryGetPropertyDeclaration($node)) && ($elements = $declaration->propertyElements) || + ($declaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) && ($elements = $declaration->constElements) ) { $defLine = $declaration->getText(); $defLineStart = $declaration->getStart(); @@ -95,7 +95,7 @@ class DefinitionResolver // For properties and constants, set the node to the declaration node, rather than the individual property. // This is because they get defined as part of a list. - $constOrPropertyDeclaration = ParserHelpers::tryGetPropertyDeclaration($node) ?? ParserHelpers::tryGetConstOrClassConstDeclaration($node); + $constOrPropertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node) ?? ParserHelpers\tryGetConstOrClassConstDeclaration($node); if ($constOrPropertyDeclaration !== null) { $node = $constOrPropertyDeclaration; } @@ -104,7 +104,7 @@ class DefinitionResolver if ($node instanceof Node\Parameter) { $variableName = $node->getName(); - $functionLikeDeclaration = ParserHelpers::getFunctionLikeDeclarationFromParameter($node); + $functionLikeDeclaration = ParserHelpers\getFunctionLikeDeclarationFromParameter($node); $docBlock = $this->getDocBlock($functionLikeDeclaration); $parameterDocBlockTag = $this->tryGetDocBlockTagForParameter($docBlock, $variableName); @@ -193,7 +193,7 @@ class DefinitionResolver $def->isStatic = ( ($node instanceof Node\MethodDeclaration && $node->isStatic()) || - (($propertyDeclaration = ParserHelpers::tryGetPropertyDeclaration($node)) !== null + (($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null && $propertyDeclaration->isStatic()) ); @@ -258,7 +258,7 @@ class DefinitionResolver // 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 // TODO - verify that this is not a method - $globalFallback = ParserHelpers::isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression; + $globalFallback = ParserHelpers\isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression; // Return the Definition object from the index index return $this->index->getDefinition($fqn, $globalFallback); } @@ -277,7 +277,7 @@ class DefinitionResolver return $this->resolveQualifiedNameNodeToFqn($node); } else if ($node instanceof Node\Expression\MemberAccessExpression) { return $this->resolveMemberAccessExpressionNodeToFqn($node); - } else if (ParserHelpers::isConstantFetch($node)) { + } else if (ParserHelpers\isConstantFetch($node)) { return (string)($node->getNamespacedName()); } else if ( // A\B::C - constant access expression @@ -497,7 +497,7 @@ class DefinitionResolver // Traverse the AST up do { // If a function is met, check the parameters and use statements - if (ParserHelpers::isFunctionLike($n)) { + if (ParserHelpers\isFunctionLike($n)) { if ($n->parameters !== null) { foreach ($n->parameters->getElements() as $param) { if ($param->getName() === $name) { @@ -611,7 +611,7 @@ class DefinitionResolver // CONSTANT FETCH // Resolve constants by retrieving corresponding definition type from FQN - if (ParserHelpers::isConstantFetch($expr)) { + if (ParserHelpers\isConstantFetch($expr)) { $fqn = (string)$expr->getNamespacedName(); $def = $this->index->getDefinition($fqn, true); if ($def !== null) { @@ -728,7 +728,7 @@ class DefinitionResolver // isset($var) // >, >=, <, <=, &&, ||, AND, OR, XOR, ==, ===, !=, !== if ( - ParserHelpers::isBooleanExpression($expr) + ParserHelpers\isBooleanExpression($expr) || ($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::BoolCastToken) || ($expr instanceof Node\Expression\UnaryOpExpression && $expr->operator->kind === PhpParser\TokenKind::ExclamationToken) @@ -933,7 +933,7 @@ class DefinitionResolver */ public function getTypeFromNode($node) { - if (ParserHelpers::isConstDefineExpression($node)) { + if (ParserHelpers\isConstDefineExpression($node)) { // constants with define() like // define('TEST_DEFINE_CONSTANT', false); return $this->resolveExpressionNodeToType($node->argumentExpressionList->children[2]->expression); @@ -950,7 +950,7 @@ class DefinitionResolver // * @param MyClass $myParam // */ // function foo($a) - $functionLikeDeclaration = ParserHelpers::getFunctionLikeDeclarationFromParameter($node); + $functionLikeDeclaration = ParserHelpers\getFunctionLikeDeclarationFromParameter($node); $variableName = $node->getName(); $docBlock = $this->getDocBlock($functionLikeDeclaration); @@ -987,7 +987,7 @@ class DefinitionResolver // 1. doc block // 2. return type hint // 3. TODO: infer from return statements - if (ParserHelpers::isFunctionLike($node)) { + if (ParserHelpers\isFunctionLike($node)) { // Functions/methods $docBlock = $this->getDocBlock($node); if ( @@ -1014,8 +1014,8 @@ class DefinitionResolver // Get the documented type the assignment resolves to. if ( ($declarationNode = - ParserHelpers::tryGetPropertyDeclaration($node) ?? - ParserHelpers::tryGetConstOrClassConstDeclaration($node) + ParserHelpers\tryGetPropertyDeclaration($node) ?? + ParserHelpers\tryGetConstOrClassConstDeclaration($node) ) !== null || ($node = $node->parent) instanceof Node\Expression\AssignmentExpression) { $declarationNode = $declarationNode ?? $node; @@ -1123,7 +1123,7 @@ class DefinitionResolver // $a = 4, $b = 4 A\B\C->$a, A\B\C->$b // TODO verify variable name // } if ( - ($propertyDeclaration = ParserHelpers::tryGetPropertyDeclaration($node)) !== null && + ($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null && ($classDeclaration = $node->getFirstAncestor( Node\Expression\ObjectCreationExpression::class, @@ -1148,7 +1148,7 @@ class DefinitionResolver // class C { // const $a, $b = 4 A\B\C::$a(), A\B\C::$b // } - if (($constDeclaration = ParserHelpers::tryGetConstOrClassConstDeclaration($node)) !== null) { + if (($constDeclaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) !== null) { if ($constDeclaration instanceof Node\Statement\ConstDeclaration) { // Basic constant: use CONSTANT_NAME as name return (string)$node->getNamespacedName(); @@ -1168,7 +1168,7 @@ class DefinitionResolver return (string)$classDeclaration->getNamespacedName() . '::' . $node->getName(); } - if (ParserHelpers::isConstDefineExpression($node)) { + if (ParserHelpers\isConstDefineExpression($node)) { return $node->argumentExpressionList->children[0]->expression->getStringContentsText(); } diff --git a/src/FqnUtilities.php b/src/FqnUtilities.php index 0fc06b2..a3aab05 100644 --- a/src/FqnUtilities.php +++ b/src/FqnUtilities.php @@ -5,8 +5,6 @@ namespace LanguageServer\FqnUtilities; use phpDocumentor\Reflection\{Type, Types}; use Microsoft\PhpParser; -echo "FQN_UTILIIES" . PHP_EOL; - /** * Returns all possible FQNs in a type * diff --git a/src/ParserHelpers.php b/src/ParserHelpers.php index 779c283..d687d49 100644 --- a/src/ParserHelpers.php +++ b/src/ParserHelpers.php @@ -1,127 +1,124 @@ parent; - return + $parent = $node->parent; + return + ( + $node instanceof Node\QualifiedName && + ( + $parent instanceof Node\Expression || + $parent instanceof Node\DelimitedList\ExpressionList || + $parent instanceof Node\ArrayElement || + ($parent instanceof Node\Parameter && $node->parent->default === $node) || + $parent instanceof Node\StatementNode || + $parent instanceof Node\CaseStatementNode + ) && + !( + $parent instanceof Node\Expression\MemberAccessExpression || + $parent instanceof Node\Expression\CallExpression || + $parent instanceof Node\Expression\ObjectCreationExpression || + $parent instanceof Node\Expression\ScopedPropertyAccessExpression || + isFunctionLike($parent) || ( - $node instanceof Node\QualifiedName && - ( - $parent instanceof Node\Expression || - $parent instanceof Node\DelimitedList\ExpressionList || - $parent instanceof Node\ArrayElement || - ($parent instanceof Node\Parameter && $node->parent->default === $node) || - $parent instanceof Node\StatementNode || - $parent instanceof Node\CaseStatementNode - ) && - !( - $parent instanceof Node\Expression\MemberAccessExpression || - $parent instanceof Node\Expression\CallExpression || - $parent instanceof Node\Expression\ObjectCreationExpression || - $parent instanceof Node\Expression\ScopedPropertyAccessExpression || - self::isFunctionLike($parent) || - ( - $parent instanceof Node\Expression\BinaryExpression && - $parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword - ) - )); - } + $parent instanceof Node\Expression\BinaryExpression && + $parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword + ) + )); +} - public static function getFunctionLikeDeclarationFromParameter(Node\Parameter $node) - { - return $node->parent->parent; - } +function getFunctionLikeDeclarationFromParameter(Node\Parameter $node) +{ + return $node->parent->parent; +} - public static function isFunctionLike(Node $node) - { - return - $node instanceof Node\Statement\FunctionDeclaration || - $node instanceof Node\MethodDeclaration || - $node instanceof Node\Expression\AnonymousFunctionCreationExpression; - } +function isFunctionLike(Node $node) +{ + return + $node instanceof Node\Statement\FunctionDeclaration || + $node instanceof Node\MethodDeclaration || + $node instanceof Node\Expression\AnonymousFunctionCreationExpression; +} - public static function isBooleanExpression($expression) : bool - { - if (!($expression instanceof Node\Expression\BinaryExpression)) { - return false; - } - switch ($expression->operator->kind) { - case PhpParser\TokenKind::InstanceOfKeyword: - case PhpParser\TokenKind::GreaterThanToken: - case PhpParser\TokenKind::GreaterThanEqualsToken: - case PhpParser\TokenKind::LessThanToken: - case PhpParser\TokenKind::LessThanEqualsToken: - case PhpParser\TokenKind::AndKeyword: - case PhpParser\TokenKind::AmpersandAmpersandToken: - case PhpParser\TokenKind::LessThanEqualsGreaterThanToken: - case PhpParser\TokenKind::OrKeyword: - case PhpParser\TokenKind::BarBarToken: - case PhpParser\TokenKind::XorKeyword: - case PhpParser\TokenKind::ExclamationEqualsEqualsToken: - case PhpParser\TokenKind::ExclamationEqualsToken: - case PhpParser\TokenKind::CaretToken: - case PhpParser\TokenKind::EqualsEqualsEqualsToken: - case PhpParser\TokenKind::EqualsToken: - return true; - } +function isBooleanExpression($expression) : bool +{ + if (!($expression instanceof Node\Expression\BinaryExpression)) { return false; } - - - /** - * Tries to get the parent property declaration given a Node - * @param Node $node - * @return Node\PropertyDeclaration | null $node - */ - public static function tryGetPropertyDeclaration(Node $node) - { - if ($node instanceof Node\Expression\Variable && - (($propertyDeclaration = $node->parent->parent) instanceof Node\PropertyDeclaration || - ($propertyDeclaration = $propertyDeclaration->parent) instanceof Node\PropertyDeclaration) - ) { - return $propertyDeclaration; - } - return null; - } - - /** - * Tries to get the parent ConstDeclaration or ClassConstDeclaration given a Node - * @param Node $node - * @return Node\Statement\ConstDeclaration | Node\ClassConstDeclaration | null $node - */ - public static function tryGetConstOrClassConstDeclaration(Node $node) - { - if ( - $node instanceof Node\ConstElement && ( - ($constDeclaration = $node->parent->parent) instanceof Node\ClassConstDeclaration || - $constDeclaration instanceof Node\Statement\ConstDeclaration ) - ) { - return $constDeclaration; - } - return null; - } - - /** - * Returns true if the node is a usage of `define`. - * e.g. define('TEST_DEFINE_CONSTANT', false); - * @param Node $node - * @return bool - */ - public static function isConstDefineExpression(Node $node): bool - { - return $node instanceof Node\Expression\CallExpression - && $node->callableExpression instanceof Node\QualifiedName - && strtolower($node->callableExpression->getText()) === 'define' - && isset($node->argumentExpressionList->children[0]) - && $node->argumentExpressionList->children[0]->expression instanceof Node\StringLiteral - && isset($node->argumentExpressionList->children[2]); + switch ($expression->operator->kind) { + case PhpParser\TokenKind::InstanceOfKeyword: + case PhpParser\TokenKind::GreaterThanToken: + case PhpParser\TokenKind::GreaterThanEqualsToken: + case PhpParser\TokenKind::LessThanToken: + case PhpParser\TokenKind::LessThanEqualsToken: + case PhpParser\TokenKind::AndKeyword: + case PhpParser\TokenKind::AmpersandAmpersandToken: + case PhpParser\TokenKind::LessThanEqualsGreaterThanToken: + case PhpParser\TokenKind::OrKeyword: + case PhpParser\TokenKind::BarBarToken: + case PhpParser\TokenKind::XorKeyword: + case PhpParser\TokenKind::ExclamationEqualsEqualsToken: + case PhpParser\TokenKind::ExclamationEqualsToken: + case PhpParser\TokenKind::CaretToken: + case PhpParser\TokenKind::EqualsEqualsEqualsToken: + case PhpParser\TokenKind::EqualsToken: + return true; } + return false; } + + +/** + * Tries to get the parent property declaration given a Node + * @param Node $node + * @return Node\PropertyDeclaration | null $node + */ +function tryGetPropertyDeclaration(Node $node) +{ + if ($node instanceof Node\Expression\Variable && + (($propertyDeclaration = $node->parent->parent) instanceof Node\PropertyDeclaration || + ($propertyDeclaration = $propertyDeclaration->parent) instanceof Node\PropertyDeclaration) + ) { + return $propertyDeclaration; + } + return null; +} + +/** + * Tries to get the parent ConstDeclaration or ClassConstDeclaration given a Node + * @param Node $node + * @return Node\Statement\ConstDeclaration | Node\ClassConstDeclaration | null $node + */ +function tryGetConstOrClassConstDeclaration(Node $node) +{ + if ( + $node instanceof Node\ConstElement && ( + ($constDeclaration = $node->parent->parent) instanceof Node\ClassConstDeclaration || + $constDeclaration instanceof Node\Statement\ConstDeclaration ) + ) { + return $constDeclaration; + } + return null; +} + +/** + * Returns true if the node is a usage of `define`. + * e.g. define('TEST_DEFINE_CONSTANT', false); + * @param Node $node + * @return bool + */ +function isConstDefineExpression(Node $node): bool +{ + return $node instanceof Node\Expression\CallExpression + && $node->callableExpression instanceof Node\QualifiedName + && strtolower($node->callableExpression->getText()) === 'define' + && isset($node->argumentExpressionList->children[0]) + && $node->argumentExpressionList->children[0]->expression instanceof Node\StringLiteral + && isset($node->argumentExpressionList->children[2]); +} \ No newline at end of file diff --git a/src/Protocol/SymbolInformation.php b/src/Protocol/SymbolInformation.php index b64cf91..6b4d39e 100644 --- a/src/Protocol/SymbolInformation.php +++ b/src/Protocol/SymbolInformation.php @@ -54,7 +54,7 @@ class SymbolInformation $symbol->kind = SymbolKind::CLASS_; } else if ($node instanceof Node\Statement\TraitDeclaration) { $symbol->kind = SymbolKind::CLASS_; - } else if (\LanguageServer\ParserHelpers::isConstDefineExpression($node)) { + } else if (\LanguageServer\ParserHelpers\isConstDefineExpression($node)) { // constants with define() like // define('TEST_DEFINE_CONSTANT', false); $symbol->kind = SymbolKind::CONSTANT; diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 7921d01..b954516 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -137,7 +137,7 @@ class TreeAnalyzer // Namespaced constant access and function calls also need to register a reference // to the global version because PHP falls back to global at runtime // http://php.net/manual/en/language.namespaces.fallback.php - if (ParserHelpers::isConstantFetch($node) || + if (ParserHelpers\isConstantFetch($node) || ($parent instanceof Node\Expression\CallExpression && !( $node instanceof Node\Expression\ScopedPropertyAccessExpression ||