diff --git a/src/LoggedTolerantDefinitionResolver.php b/src/LoggedTolerantDefinitionResolver.php new file mode 100644 index 0000000..9b8609c --- /dev/null +++ b/src/LoggedTolerantDefinitionResolver.php @@ -0,0 +1,201 @@ +getString($param1) . ", " . $this->getString($param2) . ")\n"; + if (self::$logger === true) { + echo $callStr; + } + $result = parent::$methodName($param1, $param2); + } else { + $callStr .= $this->getString($param1) . ")\n"; + if (self::$logger === true) { + echo $callStr; + } + $result = parent::$methodName($param1); + } + + if (self::$logger === true) { + if ($result instanceof Definition) { + $resultText = $result->fqn; + } elseif ($result instanceof DocBlock) { + $resultText = $result->getDescription(); + } else { + $resultText = $result ?? "NULL"; + } + echo "> RESULT[$methodName]: " . $resultText . "\n"; + } + return $result; + } + + private function getString($param) { + if ($param instanceof Tolerant\Node) { + return "[" . $param->getNodeKindName() . "] " . $param->getText(); + } + return (string)$param; + } + + /** + * Builds the declaration line for a given node. + * + * + * @param Tolerant\Node $node + * @return string + */ + public function getDeclarationLineFromNode($node): string + { + return $this->logMethod('getDeclarationLineFromNode', $node); + } + + /** + * Gets the documentation string for a node, if it has one + * + * @param Tolerant\Node $node + * @return string|null + */ + public function getDocumentationFromNode($node) + { + return $this->logMethod('getDocumentationFromNode', $node); + } + + function getDocBlock(Tolerant\Node $node) { + return $this->logMethod('getDocBlock', $node); + + } + + /** + * Create a Definition for a definition node + * + * @param Tolerant\Node $node + * @param string $fqn + * @return Definition + */ + public function createDefinitionFromNode($node, string $fqn = null): Definition + { + return $this->logMethod('createDefinitionFromNode', $node, $fqn); + } + + /** + * Given any node, returns the Definition object of the symbol that is referenced + * + * @param Tolerant\Node $node Any reference node + * @return Definition|null + */ + public function resolveReferenceNodeToDefinition($node) + { + self::$logger = true; + return $this->logMethod('resolveReferenceNodeToDefinition', $node); + } + + /** + * Given any node, returns the FQN of the symbol that is referenced + * Returns null if the FQN could not be resolved or the reference node references a variable + * + * @param Tolerant\Node $node + * @return string|null + */ + public function resolveReferenceNodeToFqn($node) { + return $this->logMethod('resolveReferenceNodeToFqn', $node); + } + + /** + * Returns the assignment or parameter node where a variable was defined + * + * @param Node\Expr\Variable|Node\Expr\ClosureUse $var The variable access + * @return Node\Expr\Assign|Node\Expr\AssignOp|Node\Param|Node\Expr\ClosureUse|null + */ + public function resolveVariableToNode(Tolerant\Node $var) + { + return $this->logMethod('resolveVariableToNode', $var); + } + + /** + * Given an expression node, resolves that expression recursively to a type. + * If the type could not be resolved, returns Types\Mixed. + * + * @param \PhpParser\Node\Expr $expr + * @return \phpDocumentor\Reflection\Type + */ + public function resolveExpressionNodeToType($expr): Type + { + return $this->logMethod('resolveExpressionNodeToType', $expr); + } + + /** + * 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 + * + * @param Tolerant\Node || Tolerant\Token $class + * @return Type + */ + public function resolveClassNameToType($class): Type + { + return $this->logMethod('resolveClassNameToType', $class); + } + + /** + * Returns the type a reference to this symbol will resolve to. + * For properties and constants, this is the type of the property/constant. + * For functions and methods, this is the return type. + * For parameters, this is the type of the parameter. + * For classes and interfaces, this is the class type (object). + * For variables / assignments, this is the documented type or type the assignment resolves to. + * Can also be a compound type. + * If it is unknown, will be Types\Mixed. + * Returns null if the node does not have a type. + * + * @param Tolerant\Node $node + * @return \phpDocumentor\Reflection\Type|null + */ + public function getTypeFromNode($node) + { + return $this->logMethod('getTypeFromNode', $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 + * + * @param Tolerant\Node $node + * @return string|null + */ + public static function getDefinedFqn($node) + { + $result = parent::getDefinedFqn($node); + if (self::$logger === true) { + echo "FUNCTION: getDefinedFqn(" . $node->getNodeKindName() . ")\n"; + var_dump($result); + } + return $result; + } +} diff --git a/src/ParserKind.php b/src/ParserKind.php index 3a1e498..b09145e 100644 --- a/src/ParserKind.php +++ b/src/ParserKind.php @@ -7,4 +7,5 @@ class ParserKind { const PHP_PARSER = 1; const TOLERANT_PHP_PARSER = 2; + const DIAGNOSTIC_TOLERANT_PHP_PARSER = 3; } \ No newline at end of file diff --git a/src/ParserResourceFactory.php b/src/ParserResourceFactory.php index 05cb146..02cd174 100644 --- a/src/ParserResourceFactory.php +++ b/src/ParserResourceFactory.php @@ -6,7 +6,7 @@ use Microsoft\PhpParser as Tolerant; use LanguageServer\Index\ReadableIndex; class ParserResourceFactory { - const PARSER_KIND = ParserKind::TOLERANT_PHP_PARSER; + const PARSER_KIND = ParserKind::DIAGNOSTIC_TOLERANT_PHP_PARSER; public static function getParser() { if (self::PARSER_KIND === ParserKind::PHP_PARSER) { @@ -19,8 +19,10 @@ class ParserResourceFactory { public static function getDefinitionResolver(ReadableIndex $index) { if (self::PARSER_KIND === ParserKind::PHP_PARSER) { return new DefinitionResolver($index); - } else { + } elseif (self::PARSER_KIND === ParserKind::TOLERANT_PHP_PARSER) { return new TolerantDefinitionResolver($index); + } elseif (self::PARSER_KIND === ParserKind::DIAGNOSTIC_TOLERANT_PHP_PARSER) { + return new LoggedTolerantDefinitionResolver($index); } } diff --git a/src/TolerantDefinitionResolver.php b/src/TolerantDefinitionResolver.php index f5692ab..4440bc7 100644 --- a/src/TolerantDefinitionResolver.php +++ b/src/TolerantDefinitionResolver.php @@ -155,6 +155,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface $def = new Definition; // this determines whether the suggestion will show after "new" + // TODO name $def->isClass = $node instanceof Tolerant\Node\Statement\ClassDeclaration; $def->isGlobal = ( @@ -162,12 +163,13 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface || $node instanceof Tolerant\Node\Statement\ClassDeclaration || $node instanceof Tolerant\Node\Statement\TraitDeclaration + // TODO namespaces? || $node instanceof Tolerant\Node\Statement\NamespaceDefinition && $node->name !== null || $node instanceof Tolerant\Node\Statement\FunctionDeclaration || $node instanceof Tolerant\Node\Statement\ConstDeclaration - || $node instanceof Tolerant\Node\ClassConstDeclaration + // || $node instanceof Tolerant\Node\ClassConstDeclaration ); $def->isStatic = ( @@ -206,14 +208,20 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface */ public function resolveReferenceNodeToDefinition($node) { + $parent = $node->getParent(); + if ($parent instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression) { + $node = $parent; + } // Variables are not indexed globally, as they stay in the file scope anyway if ($node instanceof Tolerant\Node\Expression\Variable) { // Resolve $this if ($node->getName() === 'this' && $fqn = $this->getContainingClassFqn($node)) { return $this->index->getDefinition($fqn, false); } + // TODO running throug thid for class constants or properties + // Resolve the variable to a definition node (assignment, param or closure use) - $defNode = self::resolveVariableToNode($node); + $defNode = $this->resolveVariableToNode($node); if ($defNode === null) { return null; } @@ -239,11 +247,11 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface * @param Node $node * @return string|null */ - public function resolveReferenceNodeToFqn($node) - { -// TODO all name tokens should be a part of a node - $parent = $node->getParent(); + public function resolveReferenceNodeToFqn($node) { + + $parent = $node->getParent(); +// TODO all name tokens should be a part of a node if ($node instanceof Tolerant\Node\QualifiedName) { // For extends, implements, type hints and classes of classes of static calls use the name directly $name = (string)$node->getResolvedName() ?? $node->getText(); @@ -260,10 +268,14 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface } if ($useClause->functionOrConst->kind === Tolerant\TokenKind::FunctionKeyword) { - $name .= "()"; + $name .= '()'; } } + if ($node->getFirstAncestor(Tolerant\Node\Expression\CallExpression::class) !== null) { + $name .= '()'; + } +// does this work for function calls? return $name; } /*elseif ($node instanceof Tolerant\Node\Expression\CallExpression || ($node = $node->getFirstAncestor(Tolerant\Node\Expression\CallExpression::class)) !== null) { @@ -352,7 +364,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface } return $classFqn . $memberSuffix; } - else if ($parent->parent instanceof Tolerant\Node\Expression\CallExpression && $node instanceof Tolerant\Node\DelimitedList\QualifiedNameParts) { + else if ($parent instanceof Tolerant\Node\Expression\CallExpression && $node instanceof Tolerant\Node\QualifiedName) { if ($parent->name instanceof Node\Expr) { return null; } @@ -365,10 +377,10 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface ($scoped = $node) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression || ($node instanceof Tolerant\Node\Expression\CallExpression && ($scoped = $node->callableExpression) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression) ) { - if ($scoped->memberName instanceof Tolerant\Node\Expression) { +// if ($scoped->memberName instanceof Tolerant\Node\Expression) { // Cannot get definition of dynamic names - return null; - } +// return null; +// } $className = (string)$scoped->scopeResolutionQualifier->getText(); if ($className === 'self' || $className === 'static' || $className === 'parent') { // self and static are resolved to the containing class @@ -408,6 +420,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface private function isConstantFetch(Tolerant\Node $node) : bool { return + $node instanceof Tolerant\Node\QualifiedName && ($node->parent instanceof Tolerant\Node\Statement\ExpressionStatement || $node->parent instanceof Tolerant\Node\Expression) && !( $node->parent instanceof Tolerant\Node\Expression\MemberAccessExpression || $node->parent instanceof Tolerant\Node\Expression\CallExpression || @@ -439,10 +452,11 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface * @param Node\Expr\Variable|Node\Expr\ClosureUse $var The variable access * @return Node\Expr\Assign|Node\Expr\AssignOp|Node\Param|Node\Expr\ClosureUse|null */ - private static function resolveVariableToNode(Tolerant\Node $var) + public function resolveVariableToNode(Tolerant\Node $var) { $n = $var; // When a use is passed, start outside the closure to not return immediately + // Use variable vs variable parsing? if ($var instanceof Tolerant\Node\UseVariableName) { $n = $var->getFirstAncestor(Tolerant\Node\Expression\AnonymousFunctionCreationExpression::class); $name = $var->getName(); @@ -539,7 +553,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface if ($expr->callableExpression instanceof Tolerant\Node\QualifiedName) { $fqn = $expr->callableExpression->getResolvedName() ?? $expr->callableExpression->getNamespacedName(); - $fqn .= "()"; + $fqn .= '()'; $def = $this->index->getDefinition($fqn, true); if ($def !== null) { return $def->type; @@ -575,12 +589,10 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface if ($this->isConstantFetch($expr)) { // Resolve constant - if ($expr instanceof Tolerant\Node\QualifiedName) { - $fqn = (string)$expr->getNamespacedName(); - $def = $this->index->getDefinition($fqn, true); - if ($def !== null) { - return $def->type; - } + $fqn = (string)$expr->getNamespacedName(); + $def = $this->index->getDefinition($fqn, true); + if ($def !== null) { + return $def->type; } } if (($expr instanceof Tolerant\Node\Expression\CallExpression && @@ -622,7 +634,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface $expr instanceof Tolerant\Node\Expression\CallExpression && ($scopedAccess = $expr->callableExpression) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression || ($scopedAccess = $expr) instanceof Tolerant\Node\Expression\ScopedPropertyAccessExpression ) { - $classType = self::resolveClassNameToType($scopedAccess->scopeResolutionQualifier); + $classType = $this->resolveClassNameToType($scopedAccess->scopeResolutionQualifier); if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null /*|| $expr->name instanceof Tolerant\Node\Expression*/) { return new Types\Mixed; } @@ -641,7 +653,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface return $def->type; } if ($expr instanceof Tolerant\Node\Expression\ObjectCreationExpression) { - return self::resolveClassNameToType($expr->classTypeDesignator); + return $this->resolveClassNameToType($expr->classTypeDesignator); } if ($expr instanceof Tolerant\Node\Expression\CloneExpression) { return $this->resolveExpressionNodeToType($expr->expression); @@ -812,7 +824,7 @@ class TolerantDefinitionResolver implements DefinitionResolverInterface * @param Tolerant\Node || Tolerant\Token $class * @return Type */ - private static function resolveClassNameToType($class): Type + public function resolveClassNameToType($class): Type { if ($class instanceof Tolerant\Node\Expression) { return new Types\Mixed; diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index a553dde..597cfb6 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -195,7 +195,7 @@ abstract class ServerTestCase extends TestCase 3 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty; ], 'TestClass::staticTestProperty' => [ - 0 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 35))), // echo TestClass::$staticTestProperty; + 0 => new Location($globalReferencesUri, new Range(new Position( 8, 16), new Position( 8, 35))), // echo TestClass::$staticTestProperty; 1 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty; ], 'TestClass::staticTestMethod()' => [