From 2f50f4a430cdc2370ca4347866dd601ed5cfa8cf Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 20 Jan 2017 21:25:23 +0200 Subject: [PATCH] Completion for inherited this --- src/CompletionProvider.php | 6 +++++- src/DefinitionResolver.php | 42 ++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 99a5d57..1e3f7e8 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -130,6 +130,10 @@ class CompletionProvider $list = new CompletionList; $list->isIncomplete = true; + //echo get_class($node->var); + //var_dump((string)$node->var->name); + //die(); + // A non-free node means we do NOT suggest global symbols if ( $node instanceof Node\Expr\MethodCall @@ -141,7 +145,7 @@ class CompletionProvider // If the name is an Error node, just filter by the class if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) { // For instances, resolve the variable type - $prefixes = DefinitionResolver::getFqnsFromType( + $prefixes = DefinitionResolver::getFqnsFromType( $this->definitionResolver->resolveExpressionNodeToType($node->var) ); } else { diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 754473b..7cd0bf9 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -423,7 +423,7 @@ class DefinitionResolver if ($expr instanceof Node\Expr\Variable && $expr->name === 'this') { $classNode = getClosestNode($expr, Node\Stmt\Class_::class); if ($classNode) { - return self::resolveClassNameToType($classNode->namespacedName); + return self::resolveClassNameToType($classNode->namespacedName); } return new Types\This; } @@ -479,13 +479,19 @@ class DefinitionResolver } else { $classFqn = substr((string)$t->getFqsen(), 1); } - $fqn = $classFqn . '->' . $expr->name; - if ($expr instanceof Node\Expr\MethodCall) { - $fqn .= '()'; - } - $def = $this->index->getDefinition($fqn); - if ($def !== null) { - return $def->type; + $extended = $this->expandParentFqns([$classFqn]); + foreach ($extended as $f) { + $fqn = $f . '->' . $expr->name; + if ($expr instanceof Node\Expr\MethodCall) { + $fqn .= '()'; + } + $def = $this->index->getDefinition($fqn); + if ($def !== null) { + if ($def->type instanceof Types\This || $def->type instanceof Types\Self_) { + return $this->resolveExpressionNodeToType($expr->var); + } + return $def->type; + } } } } @@ -651,6 +657,26 @@ class DefinitionResolver return new Types\Mixed; } + /** + * Adds the FQNs of all parent classes to an array of FQNs of classes + * + * @param string[] $fqns + * @return string[] + */ + private function expandParentFqns(array $fqns): array + { + $expanded = $fqns; + foreach ($fqns as $fqn) { + $def = $this->index->getDefinition($fqn); + if ($def) { + foreach ($this->expandParentFqns($def->extends) as $parent) { + $expanded[] = $parent; + } + } + } + return $expanded; + } + /** * 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