diff --git a/.gitignore b/.gitignore index 6476dbe1..a7c04f14 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ composer.lock stubs *.ast node_modules/ +/nbproject/ \ No newline at end of file diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 44cf5b8e..ff640f0b 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -237,6 +237,7 @@ public function provideCompletion(PhpDocument $doc, Position $pos, CompletionCon $this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression) ); + $isInMethodDeclaration = null !== $node->getFirstAncestor(\Microsoft\PhpParser\Node\MethodDeclaration::class); // Add the object access operator to only get members of all parents $prefixes = []; foreach ($this->expandParentFqns($fqns) as $prefix) { @@ -246,12 +247,14 @@ public function provideCompletion(PhpDocument $doc, Position $pos, CompletionCon // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { - if (substr($fqn, 0, strlen($prefix)) === $prefix && $def->isMember) { + if (substr($fqn, 0, strlen($prefix)) === $prefix && + $def->isMember && + $def->isVisible($prefix, $prefixes[0], $isInMethodDeclaration)) { $list->items[] = CompletionItemFactory::fromDefinition($def); } } } - + } elseif ( ($scoped = $node->parent) instanceof Node\Expression\ScopedPropertyAccessExpression || ($scoped = $node) instanceof Node\Expression\ScopedPropertyAccessExpression @@ -269,7 +272,7 @@ public function provideCompletion(PhpDocument $doc, Position $pos, CompletionCon $fqns = FqnUtilities\getFqnsFromType( $classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier) ); - + $isInMethodDeclaration = null !== $node->getFirstAncestor(\Microsoft\PhpParser\Node\MethodDeclaration::class); // Append :: operator to only get static members of all parents $prefixes = []; foreach ($this->expandParentFqns($fqns) as $prefix) { @@ -279,7 +282,9 @@ public function provideCompletion(PhpDocument $doc, Position $pos, CompletionCon // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { - if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && $def->isMember) { + if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && + $def->isMember && + $def->isVisible($prefix, $prefixes[0], $isInMethodDeclaration)) { $list->items[] = CompletionItemFactory::fromDefinition($def); } } diff --git a/src/Definition.php b/src/Definition.php index 9ea27f9c..39636bb4 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -133,4 +133,35 @@ public function getAncestorDefinitions(ReadableIndex $index, bool $includeSelf = } } } + + /** + * Checks the definition's visibility. + * @param string $match Owner of the FQNS + * @param string $caller Descendant of the FQNS owner + * @param bool $isInMethodDeclaration checking if the call is from inside a + * method + * @return bool + */ + public function isVisible(string $match, string $caller, bool $isInMethodDeclaration): bool + { + if ($isInMethodDeclaration) { + if ($match !== $caller && $this->isPrivate()) { + return false; + } + } else if ($this->isProtected() || $this->isPrivate()) { + return false; + } + + return true; + } + + private function isPrivate(): bool + { + return 'private' === substr($this->declarationLine, 0, 7); + } + + private function isProtected(): bool + { + return 'protected' === substr($this->declarationLine, 0, 9); + } }