Adopt Microsoft/tolerant-php-parser (#357)
parent
08cf1a3fd7
commit
7f427a1215
|
@ -5,3 +5,4 @@ vendor/
|
||||||
.phpls/
|
.phpls/
|
||||||
composer.lock
|
composer.lock
|
||||||
stubs
|
stubs
|
||||||
|
*.ast
|
|
@ -0,0 +1,27 @@
|
||||||
|
[submodule "validation/frameworks/php-language-server"]
|
||||||
|
path = validation/frameworks/php-language-server
|
||||||
|
url = https://github.com/felixfbecker/php-language-server
|
||||||
|
[submodule "validation/frameworks/wordpress"]
|
||||||
|
path = validation/frameworks/wordpress
|
||||||
|
url = https://github.com/wordpress/wordpress
|
||||||
|
[submodule "validation/frameworks/drupal"]
|
||||||
|
path = validation/frameworks/drupal
|
||||||
|
url = https://github.com/drupal/drupal
|
||||||
|
[submodule "validation/frameworks/tolerant-php-parser"]
|
||||||
|
path = validation/frameworks/tolerant-php-parser
|
||||||
|
url = https://github.com/microsoft/tolerant-php-parser
|
||||||
|
[submodule "validation/frameworks/symfony"]
|
||||||
|
path = validation/frameworks/symfony
|
||||||
|
url = https://github.com/symfony/symfony
|
||||||
|
[submodule "validation/frameworks/math-php"]
|
||||||
|
path = validation/frameworks/math-php
|
||||||
|
url = https://github.com/markrogoyski/math-php
|
||||||
|
[submodule "validation/frameworks/codeigniter"]
|
||||||
|
path = validation/frameworks/codeigniter
|
||||||
|
url = https://github.com/bcit-ci/codeigniter
|
||||||
|
[submodule "validation/frameworks/cakephp"]
|
||||||
|
path = validation/frameworks/cakephp
|
||||||
|
url = https://github.com/cakephp/cakephp
|
||||||
|
[submodule "validation/frameworks/phpunit"]
|
||||||
|
path = validation/frameworks/phpunit
|
||||||
|
url = https://github.com/sebastianbergmann/phpunit
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LanguageServer\Tests;
|
||||||
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use LanguageServer\Index\Index;
|
||||||
|
use LanguageServer\PhpDocument;
|
||||||
|
use LanguageServer\DefinitionResolver;
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
|
use RecursiveDirectoryIterator;
|
||||||
|
use RecursiveIteratorIterator;
|
||||||
|
|
||||||
|
$totalSize = 0;
|
||||||
|
|
||||||
|
$frameworks = ["drupal", "wordpress", "php-language-server", "tolerant-php-parser", "math-php", "symfony", "CodeIgniter", "cakephp"];
|
||||||
|
|
||||||
|
foreach($frameworks as $framework) {
|
||||||
|
$iterator = new RecursiveDirectoryIterator(__DIR__ . "/validation/frameworks/$framework");
|
||||||
|
$testProviderArray = array();
|
||||||
|
|
||||||
|
foreach (new RecursiveIteratorIterator($iterator) as $file) {
|
||||||
|
if (strpos((string)$file, ".php") !== false) {
|
||||||
|
$totalSize += $file->getSize();
|
||||||
|
$testProviderArray[] = $file->getPathname();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($testProviderArray) === 0) {
|
||||||
|
throw new Exception("ERROR: Validation testsuite frameworks not found - run `git submodule update --init --recursive` to download.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
|
|
||||||
|
foreach ($testProviderArray as $idx => $testCaseFile) {
|
||||||
|
if (filesize($testCaseFile) > 10000) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($idx % 1000 === 0) {
|
||||||
|
echo "$idx\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileContents = file_get_contents($testCaseFile);
|
||||||
|
|
||||||
|
$docBlockFactory = DocBlockFactory::createInstance();
|
||||||
|
$index = new Index;
|
||||||
|
$maxRecursion = [];
|
||||||
|
$definitions = [];
|
||||||
|
|
||||||
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
|
$parser = new PhpParser\Parser();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$document = new PhpDocument($testCaseFile, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "------------------------------\n";
|
||||||
|
|
||||||
|
echo "Time [$framework]: " . (microtime(true) - $start) . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.0",
|
"php": ">=7.0",
|
||||||
"nikic/php-parser": "^3.0.5",
|
|
||||||
"phpdocumentor/reflection-docblock": "^3.0",
|
"phpdocumentor/reflection-docblock": "^3.0",
|
||||||
"sabre/event": "^5.0",
|
"sabre/event": "^5.0",
|
||||||
"felixfbecker/advanced-json-rpc": "^2.0",
|
"felixfbecker/advanced-json-rpc": "^2.0",
|
||||||
|
@ -38,7 +37,8 @@
|
||||||
"webmozart/glob": "^4.1",
|
"webmozart/glob": "^4.1",
|
||||||
"sabre/uri": "^2.0",
|
"sabre/uri": "^2.0",
|
||||||
"jetbrains/phpstorm-stubs": "dev-master",
|
"jetbrains/phpstorm-stubs": "dev-master",
|
||||||
"composer/composer": "^1.3"
|
"composer/composer": "^1.3",
|
||||||
|
"Microsoft/tolerant-php-parser": "^0.0.2"
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
|
@ -47,7 +47,9 @@
|
||||||
"LanguageServer\\": "src/"
|
"LanguageServer\\": "src/"
|
||||||
},
|
},
|
||||||
"files" : [
|
"files" : [
|
||||||
"src/utils.php"
|
"src/utils.php",
|
||||||
|
"src/FqnUtilities.php",
|
||||||
|
"src/ParserHelpers.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<ruleset name="PHP Language Server">
|
<ruleset name="PHP Language Server">
|
||||||
<file>src</file>
|
<file>src</file>
|
||||||
<file>tests</file>
|
<file>tests</file>
|
||||||
|
<exclude-pattern>tests/Validation/cases</exclude-pattern>
|
||||||
<rule ref="PSR2">
|
<rule ref="PSR2">
|
||||||
<exclude name="PSR2.Namespaces.UseDeclaration.MultipleDeclarations"/>
|
<exclude name="PSR2.Namespaces.UseDeclaration.MultipleDeclarations"/>
|
||||||
<exclude name="PSR2.ControlStructures.ElseIfDeclaration.NotAllowed"/>
|
<exclude name="PSR2.ControlStructures.ElseIfDeclaration.NotAllowed"/>
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
</whitelist>
|
</whitelist>
|
||||||
</filter>
|
</filter>
|
||||||
<php>
|
<php>
|
||||||
<ini name="memory_limit" value="256M"/>
|
<ini name="memory_limit" value="1024M"/>
|
||||||
</php>
|
</php>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
@ -3,7 +3,6 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer;
|
namespace LanguageServer;
|
||||||
|
|
||||||
use PhpParser\Node;
|
|
||||||
use LanguageServer\Index\ReadableIndex;
|
use LanguageServer\Index\ReadableIndex;
|
||||||
use LanguageServer\Protocol\{
|
use LanguageServer\Protocol\{
|
||||||
TextEdit,
|
TextEdit,
|
||||||
|
@ -13,6 +12,8 @@ use LanguageServer\Protocol\{
|
||||||
CompletionItem,
|
CompletionItem,
|
||||||
CompletionItemKind
|
CompletionItemKind
|
||||||
};
|
};
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
class CompletionProvider
|
class CompletionProvider
|
||||||
{
|
{
|
||||||
|
@ -104,7 +105,7 @@ class CompletionProvider
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DefinitionResolver $definitionResolver
|
* @param DefinitionResolver $definitionResolver
|
||||||
* @param ReadableIndex $index
|
* @param ReadableIndex $index
|
||||||
*/
|
*/
|
||||||
public function __construct(DefinitionResolver $definitionResolver, ReadableIndex $index)
|
public function __construct(DefinitionResolver $definitionResolver, ReadableIndex $index)
|
||||||
{
|
{
|
||||||
|
@ -121,45 +122,74 @@ class CompletionProvider
|
||||||
*/
|
*/
|
||||||
public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList
|
public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList
|
||||||
{
|
{
|
||||||
|
// This can be made much more performant if the tree follows specific invariants.
|
||||||
$node = $doc->getNodeAtPosition($pos);
|
$node = $doc->getNodeAtPosition($pos);
|
||||||
|
|
||||||
if ($node instanceof Node\Expr\Error) {
|
$offset = $node === null ? -1 : $pos->toOffset($node->getFileContents());
|
||||||
$node = $node->getAttribute('parentNode');
|
if (
|
||||||
|
$node !== null
|
||||||
|
&& $offset > $node->getEndPosition()
|
||||||
|
&& $node->parent !== null
|
||||||
|
&& $node->parent->getLastChild() instanceof PhpParser\MissingToken
|
||||||
|
) {
|
||||||
|
$node = $node->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
$list = new CompletionList;
|
$list = new CompletionList;
|
||||||
$list->isIncomplete = true;
|
$list->isIncomplete = true;
|
||||||
|
|
||||||
// A non-free node means we do NOT suggest global symbols
|
if ($node instanceof Node\Expression\Variable &&
|
||||||
if (
|
$node->parent instanceof Node\Expression\ObjectCreationExpression &&
|
||||||
$node instanceof Node\Expr\MethodCall
|
$node->name instanceof PhpParser\MissingToken
|
||||||
|| $node instanceof Node\Expr\PropertyFetch
|
|
||||||
|| $node instanceof Node\Expr\StaticCall
|
|
||||||
|| $node instanceof Node\Expr\StaticPropertyFetch
|
|
||||||
|| $node instanceof Node\Expr\ClassConstFetch
|
|
||||||
) {
|
) {
|
||||||
// If the name is an Error node, just filter by the class
|
$node = $node->parent;
|
||||||
if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) {
|
}
|
||||||
// For instances, resolve the variable type
|
|
||||||
$prefixes = DefinitionResolver::getFqnsFromType(
|
if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) {
|
||||||
$this->definitionResolver->resolveExpressionNodeToType($node->var)
|
$item = new CompletionItem('<?php', CompletionItemKind::KEYWORD);
|
||||||
|
$item->textEdit = new TextEdit(
|
||||||
|
new Range($pos, $pos),
|
||||||
|
stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), '<?php')
|
||||||
|
);
|
||||||
|
$list->items[] = $item;
|
||||||
|
} /*
|
||||||
|
|
||||||
|
VARIABLES */
|
||||||
|
elseif (
|
||||||
|
$node instanceof Node\Expression\Variable &&
|
||||||
|
!(
|
||||||
|
$node->parent instanceof Node\Expression\ScopedPropertyAccessExpression &&
|
||||||
|
$node->parent->memberName === $node)
|
||||||
|
) {
|
||||||
|
// Find variables, parameters and use statements in the scope
|
||||||
|
$namePrefix = $node->getName() ?? '';
|
||||||
|
foreach ($this->suggestVariablesAtNode($node, $namePrefix) as $var) {
|
||||||
|
$item = new CompletionItem;
|
||||||
|
$item->kind = CompletionItemKind::VARIABLE;
|
||||||
|
$item->label = '$' . $var->getName();
|
||||||
|
$item->documentation = $this->definitionResolver->getDocumentationFromNode($var);
|
||||||
|
$item->detail = (string)$this->definitionResolver->getTypeFromNode($var);
|
||||||
|
$item->textEdit = new TextEdit(
|
||||||
|
new Range($pos, $pos),
|
||||||
|
stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), $item->label)
|
||||||
);
|
);
|
||||||
} else {
|
$list->items[] = $item;
|
||||||
// Static member reference
|
|
||||||
$prefixes = [$node->class instanceof Node\Name ? (string)$node->class : ''];
|
|
||||||
}
|
}
|
||||||
|
} /*
|
||||||
|
|
||||||
|
MEMBER ACCESS EXPRESSIONS
|
||||||
|
$a->c#
|
||||||
|
$a-># */
|
||||||
|
elseif ($node instanceof Node\Expression\MemberAccessExpression) {
|
||||||
|
$prefixes = FqnUtilities\getFqnsFromType(
|
||||||
|
$this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression)
|
||||||
|
);
|
||||||
$prefixes = $this->expandParentFqns($prefixes);
|
$prefixes = $this->expandParentFqns($prefixes);
|
||||||
// If we are just filtering by the class, add the appropiate operator to the prefix
|
|
||||||
// to filter the type of symbol
|
|
||||||
foreach ($prefixes as &$prefix) {
|
foreach ($prefixes as &$prefix) {
|
||||||
if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) {
|
$prefix .= '->';
|
||||||
$prefix .= '->';
|
|
||||||
} else if ($node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\ClassConstFetch) {
|
|
||||||
$prefix .= '::';
|
|
||||||
} else if ($node instanceof Node\Expr\StaticPropertyFetch) {
|
|
||||||
$prefix .= '::$';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($prefix);
|
unset($prefix);
|
||||||
|
|
||||||
foreach ($this->index->getDefinitions() as $fqn => $def) {
|
foreach ($this->index->getDefinitions() as $fqn => $def) {
|
||||||
|
@ -169,122 +199,114 @@ class CompletionProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} /*
|
||||||
// A ConstFetch means any static reference, like a class, interface, etc. or keyword
|
|
||||||
($node instanceof Node\Name && $node->getAttribute('parentNode') instanceof Node\Expr\ConstFetch)
|
SCOPED PROPERTY ACCESS EXPRESSIONS
|
||||||
|| $node instanceof Node\Expr\New_
|
A\B\C::$a#
|
||||||
|
A\B\C::#
|
||||||
|
A\B\C::$#
|
||||||
|
A\B\C::foo#
|
||||||
|
TODO: $a::# */
|
||||||
|
elseif (
|
||||||
|
($scoped = $node->parent) instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
|
($scoped = $node) instanceof Node\Expression\ScopedPropertyAccessExpression
|
||||||
) {
|
) {
|
||||||
$prefix = '';
|
$prefixes = FqnUtilities\getFqnsFromType(
|
||||||
$prefixLen = 0;
|
$classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier)
|
||||||
if ($node instanceof Node\Name) {
|
);
|
||||||
$isFullyQualified = $node->isFullyQualified();
|
|
||||||
$prefix = (string)$node;
|
$prefixes = $this->expandParentFqns($prefixes);
|
||||||
$prefixLen = strlen($prefix);
|
|
||||||
$namespacedPrefix = (string)$node->getAttribute('namespacedName');
|
foreach ($prefixes as &$prefix) {
|
||||||
$namespacedPrefixLen = strlen($prefix);
|
$prefix .= '::';
|
||||||
}
|
}
|
||||||
// Find closest namespace
|
|
||||||
$namespace = getClosestNode($node, Node\Stmt\Namespace_::class);
|
unset($prefix);
|
||||||
/** Map from alias to Definition */
|
|
||||||
$aliasedDefs = [];
|
foreach ($this->index->getDefinitions() as $fqn => $def) {
|
||||||
if ($namespace) {
|
foreach ($prefixes as $prefix) {
|
||||||
foreach ($namespace->stmts as $stmt) {
|
if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && !$def->isGlobal) {
|
||||||
if ($stmt instanceof Node\Stmt\Use_ || $stmt instanceof Node\Stmt\GroupUse) {
|
|
||||||
foreach ($stmt->uses as $use) {
|
|
||||||
// Get the definition for the used namespace, class-like, function or constant
|
|
||||||
// And save it under the alias
|
|
||||||
$fqn = (string)Node\Name::concat($stmt->prefix ?? null, $use->name);
|
|
||||||
if ($def = $this->index->getDefinition($fqn)) {
|
|
||||||
$aliasedDefs[$use->alias] = $def;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Use statements are always the first statements in a namespace
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If there is a prefix that does not start with a slash, suggest `use`d symbols
|
|
||||||
if ($prefix && !$isFullyQualified) {
|
|
||||||
// Suggest symbols that have been `use`d
|
|
||||||
// Search the aliases for the typed-in name
|
|
||||||
foreach ($aliasedDefs as $alias => $def) {
|
|
||||||
if (substr($alias, 0, $prefixLen) === $prefix) {
|
|
||||||
$list->items[] = CompletionItem::fromDefinition($def);
|
$list->items[] = CompletionItem::fromDefinition($def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Additionally, suggest global symbols that either
|
} elseif (ParserHelpers\isConstantFetch($node) ||
|
||||||
// - start with the current namespace + prefix, if the Name node is not fully qualified
|
($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression ||
|
||||||
// - start with just the prefix, if the Name node is fully qualified
|
(($creation = $node) instanceof Node\Expression\ObjectCreationExpression)) {
|
||||||
|
$class = isset($creation) ? $creation->classTypeDesignator : $node;
|
||||||
|
|
||||||
|
$prefix = $class instanceof Node\QualifiedName
|
||||||
|
? (string)PhpParser\ResolvedName::buildName($class->nameParts, $class->getFileContents())
|
||||||
|
: $class->getText($node->getFileContents());
|
||||||
|
|
||||||
|
$namespaceDefinition = $node->getNamespaceDefinition();
|
||||||
|
|
||||||
|
list($namespaceImportTable,,) = $node->getImportTablesForCurrentScope();
|
||||||
|
foreach ($namespaceImportTable as $alias => $name) {
|
||||||
|
$namespaceImportTable[$alias] = (string)$name;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->index->getDefinitions() as $fqn => $def) {
|
foreach ($this->index->getDefinitions() as $fqn => $def) {
|
||||||
if (
|
$fqnStartsWithPrefix = substr($fqn, 0, strlen($prefix)) === $prefix;
|
||||||
$def->isGlobal // exclude methods, properties etc.
|
$fqnContainsPrefix = empty($prefix) || strpos($fqn, $prefix) !== false;
|
||||||
&& (
|
if (($def->canBeInstantiated || ($def->isGlobal && !isset($creation))) && $fqnContainsPrefix) {
|
||||||
!$prefix
|
if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) {
|
||||||
|| (
|
$namespacePrefix = (string)PhpParser\ResolvedName::buildName($namespaceDefinition->name->nameParts, $node->getFileContents());
|
||||||
((!$namespace || $isFullyQualified) && substr($fqn, 0, $prefixLen) === $prefix)
|
|
||||||
|| (
|
$isAliased = false;
|
||||||
$namespace
|
|
||||||
&& !$isFullyQualified
|
$isNotFullyQualified = !($class instanceof Node\QualifiedName) || !$class->isFullyQualifiedName();
|
||||||
&& substr($fqn, 0, $namespacedPrefixLen) === $namespacedPrefix
|
if ($isNotFullyQualified) {
|
||||||
)
|
foreach ($namespaceImportTable as $alias => $name) {
|
||||||
)
|
if (substr($fqn, 0, strlen($name)) === $name) {
|
||||||
)
|
$fqn = $alias;
|
||||||
// Only suggest classes for `new`
|
$isAliased = true;
|
||||||
&& (!($node instanceof Node\Expr\New_) || $def->canBeInstantiated)
|
break;
|
||||||
) {
|
}
|
||||||
$item = CompletionItem::fromDefinition($def);
|
}
|
||||||
// Find the shortest name to reference the symbol
|
}
|
||||||
if ($namespace && ($alias = array_search($def, $aliasedDefs, true)) !== false) {
|
|
||||||
// $alias is the name under which this definition is aliased in the current namespace
|
$prefixWithNamespace = $namespacePrefix . "\\" . $prefix;
|
||||||
$item->insertText = $alias;
|
$fqnMatchesPrefixWithNamespace = substr($fqn, 0, strlen($prefixWithNamespace)) === $prefixWithNamespace;
|
||||||
} else if ($namespace && !($prefix && $isFullyQualified)) {
|
$isFullyQualifiedAndPrefixMatches = !$isNotFullyQualified && ($fqnStartsWithPrefix || $fqnMatchesPrefixWithNamespace);
|
||||||
// Insert the global FQN with trailing backslash
|
if (!$isFullyQualifiedAndPrefixMatches && !$isAliased) {
|
||||||
$item->insertText = '\\' . $fqn;
|
if (!array_search($fqn, array_values($namespaceImportTable))) {
|
||||||
} else {
|
if (empty($prefix)) {
|
||||||
// Insert the FQN without trailing backlash
|
$fqn = '\\' . $fqn;
|
||||||
$item->insertText = $fqn;
|
} elseif ($fqnMatchesPrefixWithNamespace) {
|
||||||
|
$fqn = substr($fqn, strlen($namespacePrefix) + 1);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($fqnStartsWithPrefix && $class instanceof Node\QualifiedName && $class->isFullyQualifiedName()) {
|
||||||
|
$fqn = '\\' . $fqn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$item = CompletionItem::fromDefinition($def);
|
||||||
|
|
||||||
|
$item->insertText = $fqn;
|
||||||
$list->items[] = $item;
|
$list->items[] = $item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Suggest keywords
|
|
||||||
if ($node instanceof Node\Name && $node->getAttribute('parentNode') instanceof Node\Expr\ConstFetch) {
|
if (!isset($creation)) {
|
||||||
foreach (self::KEYWORDS as $keyword) {
|
foreach (self::KEYWORDS as $keyword) {
|
||||||
if (substr($keyword, 0, $prefixLen) === $prefix) {
|
$item = new CompletionItem($keyword, CompletionItemKind::KEYWORD);
|
||||||
$item = new CompletionItem($keyword, CompletionItemKind::KEYWORD);
|
$item->insertText = $keyword . ' ';
|
||||||
$item->insertText = $keyword . ' ';
|
$list->items[] = $item;
|
||||||
$list->items[] = $item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} elseif (ParserHelpers\isConstantFetch($node)) {
|
||||||
$node instanceof Node\Expr\Variable
|
$prefix = (string) ($node->getResolvedName() ?? PhpParser\ResolvedName::buildName($node->nameParts, $node->getFileContents()));
|
||||||
|| ($node && $node->getAttribute('parentNode') instanceof Node\Expr\Variable)
|
foreach (self::KEYWORDS as $keyword) {
|
||||||
) {
|
$item = new CompletionItem($keyword, CompletionItemKind::KEYWORD);
|
||||||
// Find variables, parameters and use statements in the scope
|
$item->insertText = $keyword . ' ';
|
||||||
// If there was only a $ typed, $node will be instanceof Node\Error
|
|
||||||
$namePrefix = $node instanceof Node\Expr\Variable && is_string($node->name) ? $node->name : '';
|
|
||||||
foreach ($this->suggestVariablesAtNode($node, $namePrefix) as $var) {
|
|
||||||
$item = new CompletionItem;
|
|
||||||
$item->kind = CompletionItemKind::VARIABLE;
|
|
||||||
$item->label = '$' . ($var instanceof Node\Expr\ClosureUse ? $var->var : $var->name);
|
|
||||||
$item->documentation = $this->definitionResolver->getDocumentationFromNode($var);
|
|
||||||
$item->detail = (string)$this->definitionResolver->getTypeFromNode($var);
|
|
||||||
$item->textEdit = new TextEdit(
|
|
||||||
new Range($pos, $pos),
|
|
||||||
stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), $item->label)
|
|
||||||
);
|
|
||||||
$list->items[] = $item;
|
$list->items[] = $item;
|
||||||
}
|
}
|
||||||
} else if ($node instanceof Node\Stmt\InlineHTML || $pos == new Position(0, 0)) {
|
|
||||||
$item = new CompletionItem('<?php', CompletionItemKind::KEYWORD);
|
|
||||||
$item->textEdit = new TextEdit(
|
|
||||||
new Range($pos, $pos),
|
|
||||||
stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), '<?php')
|
|
||||||
);
|
|
||||||
$list->items[] = $item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $list;
|
return $list;
|
||||||
|
@ -302,7 +324,7 @@ class CompletionProvider
|
||||||
foreach ($fqns as $fqn) {
|
foreach ($fqns as $fqn) {
|
||||||
$def = $this->index->getDefinition($fqn);
|
$def = $this->index->getDefinition($fqn);
|
||||||
if ($def) {
|
if ($def) {
|
||||||
foreach ($this->expandParentFqns($def->extends) as $parent) {
|
foreach ($this->expandParentFqns($def->extends ?? []) as $parent) {
|
||||||
$expanded[] = $parent;
|
$expanded[] = $parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,30 +357,34 @@ class CompletionProvider
|
||||||
|
|
||||||
// Walk the AST upwards until a scope boundary is met
|
// Walk the AST upwards until a scope boundary is met
|
||||||
$level = $node;
|
$level = $node;
|
||||||
while ($level && !($level instanceof Node\FunctionLike)) {
|
while ($level && !ParserHelpers\isFunctionLike($level)) {
|
||||||
// Walk siblings before the node
|
// Walk siblings before the node
|
||||||
$sibling = $level;
|
$sibling = $level;
|
||||||
while ($sibling = $sibling->getAttribute('previousSibling')) {
|
while ($sibling = $sibling->getPreviousSibling()) {
|
||||||
// Collect all variables inside the sibling node
|
// Collect all variables inside the sibling node
|
||||||
foreach ($this->findVariableDefinitionsInNode($sibling, $namePrefix) as $var) {
|
foreach ($this->findVariableDefinitionsInNode($sibling, $namePrefix) as $var) {
|
||||||
$vars[$var->name] = $var;
|
$vars[$var->getName()] = $var;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$level = $level->getAttribute('parentNode');
|
$level = $level->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the traversal ended because a function was met,
|
// If the traversal ended because a function was met,
|
||||||
// also add its parameters and closure uses to the result list
|
// also add its parameters and closure uses to the result list
|
||||||
if ($level instanceof Node\FunctionLike) {
|
if ($level && ParserHelpers\isFunctionLike($level) && $level->parameters !== null) {
|
||||||
foreach ($level->params as $param) {
|
foreach ($level->parameters->getValues() as $param) {
|
||||||
if (!isset($vars[$param->name]) && substr($param->name, 0, strlen($namePrefix)) === $namePrefix) {
|
$paramName = $param->getName();
|
||||||
$vars[$param->name] = $param;
|
if (empty($namePrefix) || strpos($paramName, $namePrefix) !== false) {
|
||||||
|
$vars[$paramName] = $param;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($level instanceof Node\Expr\Closure) {
|
|
||||||
foreach ($level->uses as $use) {
|
if ($level instanceof Node\Expression\AnonymousFunctionCreationExpression && $level->anonymousFunctionUseClause !== null &&
|
||||||
if (!isset($vars[$use->var]) && substr($use->var, 0, strlen($namePrefix)) === $namePrefix) {
|
$level->anonymousFunctionUseClause->useVariableNameList !== null) {
|
||||||
$vars[$use->var] = $use;
|
foreach ($level->anonymousFunctionUseClause->useVariableNameList->getValues() as $use) {
|
||||||
|
$useName = $use->getName();
|
||||||
|
if (empty($namePrefix) || strpos($useName, $namePrefix) !== false) {
|
||||||
|
$vars[$useName] = $use;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -372,38 +398,36 @@ class CompletionProvider
|
||||||
*
|
*
|
||||||
* @param Node $node
|
* @param Node $node
|
||||||
* @param string $namePrefix Prefix to filter
|
* @param string $namePrefix Prefix to filter
|
||||||
* @return Node\Expr\Variable[]
|
* @return Node\Expression\Variable[]
|
||||||
*/
|
*/
|
||||||
private function findVariableDefinitionsInNode(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
|
||||||
$parent = $node->getAttribute('parentNode');
|
|
||||||
if (
|
$isAssignmentToVariable = function ($node) {
|
||||||
$node instanceof Node\Expr\Variable
|
return $node instanceof Node\Expression\AssignmentExpression;
|
||||||
&& ($parent instanceof Node\Expr\Assign || $parent instanceof Node\Expr\AssignOp)
|
};
|
||||||
&& is_string($node->name) // Variable variables are of no use
|
|
||||||
&& substr($node->name, 0, strlen($namePrefix)) === $namePrefix
|
if ($this->isAssignmentToVariableWithPrefix($node, $namePrefix)) {
|
||||||
) {
|
$vars[] = $node->leftOperand;
|
||||||
$vars[] = $node;
|
} else {
|
||||||
}
|
// Get all descendent variables, then filter to ones that start with $namePrefix.
|
||||||
// Iterate over subnodes
|
// Avoiding closure usage in tight loop
|
||||||
foreach ($node->getSubNodeNames() as $attr) {
|
foreach ($node->getDescendantNodes($isAssignmentToVariable) as $descendantNode) {
|
||||||
if (!isset($node->$attr)) {
|
if ($this->isAssignmentToVariableWithPrefix($descendantNode, $namePrefix)) {
|
||||||
continue;
|
$vars[] = $descendantNode->leftOperand;
|
||||||
}
|
|
||||||
$children = is_array($node->$attr) ? $node->$attr : [$node->$attr];
|
|
||||||
foreach ($children as $child) {
|
|
||||||
// Dont try to traverse scalars
|
|
||||||
// Dont traverse functions, the contained variables are in a different scope
|
|
||||||
if (!($child instanceof Node) || $child instanceof Node\FunctionLike) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach ($this->findVariableDefinitionsInNode($child, $namePrefix) as $var) {
|
|
||||||
$vars[] = $var;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $vars;
|
return $vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function isAssignmentToVariableWithPrefix(Node $node, string $namePrefix): bool
|
||||||
|
{
|
||||||
|
return $node instanceof Node\Expression\AssignmentExpression
|
||||||
|
&& $node->leftOperand instanceof Node\Expression\Variable
|
||||||
|
&& ($namePrefix === '' || strpos($node->leftOperand->getName(), $namePrefix) !== false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +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;
|
||||||
|
|
||||||
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)) {
|
||||||
|
@ -29,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 Parser;
|
$parser = new PhpParser\Parser();
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
|
|
||||||
$stubsLocation = null;
|
$stubsLocation = null;
|
||||||
|
|
|
@ -3,7 +3,6 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer;
|
namespace LanguageServer;
|
||||||
|
|
||||||
use PhpParser\Node;
|
|
||||||
use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver};
|
use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver};
|
||||||
use LanguageServer\Protocol\SymbolInformation;
|
use LanguageServer\Protocol\SymbolInformation;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LanguageServer\FqnUtilities;
|
||||||
|
|
||||||
|
use phpDocumentor\Reflection\{Type, Types};
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all possible FQNs in a type
|
||||||
|
*
|
||||||
|
* @param Type|null $type
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
function getFqnsFromType($type): array
|
||||||
|
{
|
||||||
|
$fqns = [];
|
||||||
|
if ($type instanceof Types\Object_) {
|
||||||
|
$fqsen = $type->getFqsen();
|
||||||
|
if ($fqsen !== null) {
|
||||||
|
$fqns[] = substr((string)$fqsen, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type instanceof Types\Compound) {
|
||||||
|
for ($i = 0; $t = $type->get($i); $i++) {
|
||||||
|
foreach (getFqnsFromType($type) as $fqn) {
|
||||||
|
$fqns[] = $fqn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $fqns;
|
||||||
|
}
|
|
@ -150,6 +150,17 @@ class Index implements ReadableIndex, \Serializable
|
||||||
return $this->references[$fqn] ?? [];
|
return $this->references[$fqn] ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For test use.
|
||||||
|
* Returns all references, keyed by fqn.
|
||||||
|
*
|
||||||
|
* @return string[][]
|
||||||
|
*/
|
||||||
|
public function getReferences(): array
|
||||||
|
{
|
||||||
|
return $this->references;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a document URI as a referencee of a specific symbol
|
* Adds a document URI as a referencee of a specific symbol
|
||||||
*
|
*
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace LanguageServer;
|
||||||
use LanguageServer\Cache\Cache;
|
use LanguageServer\Cache\Cache;
|
||||||
use LanguageServer\FilesFinder\FilesFinder;
|
use LanguageServer\FilesFinder\FilesFinder;
|
||||||
use LanguageServer\Index\{DependenciesIndex, Index};
|
use LanguageServer\Index\{DependenciesIndex, Index};
|
||||||
|
use LanguageServer\Protocol\Message;
|
||||||
use LanguageServer\Protocol\MessageType;
|
use LanguageServer\Protocol\MessageType;
|
||||||
use Webmozart\PathUtil\Path;
|
use Webmozart\PathUtil\Path;
|
||||||
use Composer\Semver\VersionParser;
|
use Composer\Semver\VersionParser;
|
||||||
|
|
|
@ -8,10 +8,7 @@ use LanguageServer\Protocol\{
|
||||||
ClientCapabilities,
|
ClientCapabilities,
|
||||||
TextDocumentSyncKind,
|
TextDocumentSyncKind,
|
||||||
Message,
|
Message,
|
||||||
MessageType,
|
|
||||||
InitializeResult,
|
InitializeResult,
|
||||||
SymbolInformation,
|
|
||||||
TextDocumentIdentifier,
|
|
||||||
CompletionOptions
|
CompletionOptions
|
||||||
};
|
};
|
||||||
use LanguageServer\FilesFinder\{FilesFinder, ClientFilesFinder, FileSystemFilesFinder};
|
use LanguageServer\FilesFinder\{FilesFinder, ClientFilesFinder, FileSystemFilesFinder};
|
||||||
|
@ -19,12 +16,10 @@ use LanguageServer\ContentRetriever\{ContentRetriever, ClientContentRetriever, F
|
||||||
use LanguageServer\Index\{DependenciesIndex, GlobalIndex, Index, ProjectIndex, StubsIndex};
|
use LanguageServer\Index\{DependenciesIndex, GlobalIndex, Index, ProjectIndex, StubsIndex};
|
||||||
use LanguageServer\Cache\{FileSystemCache, ClientCache};
|
use LanguageServer\Cache\{FileSystemCache, ClientCache};
|
||||||
use AdvancedJsonRpc;
|
use AdvancedJsonRpc;
|
||||||
use Sabre\Event\{Loop, Promise};
|
use Sabre\Event\Promise;
|
||||||
use function Sabre\Event\coroutine;
|
use function Sabre\Event\coroutine;
|
||||||
use Exception;
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Webmozart\PathUtil\Path;
|
use Webmozart\PathUtil\Path;
|
||||||
use Sabre\Uri;
|
|
||||||
|
|
||||||
class LanguageServer extends AdvancedJsonRpc\Dispatcher
|
class LanguageServer extends AdvancedJsonRpc\Dispatcher
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace LanguageServer\NodeVisitor;
|
|
||||||
|
|
||||||
use PhpParser\{NodeVisitorAbstract, Node};
|
|
||||||
|
|
||||||
class ColumnCalculator extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
private $code;
|
|
||||||
private $codeLength;
|
|
||||||
|
|
||||||
public function __construct($code)
|
|
||||||
{
|
|
||||||
$this->code = $code;
|
|
||||||
$this->codeLength = strlen($code);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
$startFilePos = $node->getAttribute('startFilePos');
|
|
||||||
$endFilePos = $node->getAttribute('endFilePos');
|
|
||||||
|
|
||||||
if ($startFilePos > $this->codeLength || $endFilePos > $this->codeLength) {
|
|
||||||
throw new \RuntimeException('Invalid position information');
|
|
||||||
}
|
|
||||||
|
|
||||||
$startLinePos = strrpos($this->code, "\n", $startFilePos - $this->codeLength);
|
|
||||||
if ($startLinePos === false) {
|
|
||||||
$startLinePos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$endLinePos = strrpos($this->code, "\n", $endFilePos - $this->codeLength);
|
|
||||||
if ($endLinePos === false) {
|
|
||||||
$endLinePos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$node->setAttribute('startColumn', $startFilePos - $startLinePos);
|
|
||||||
$node->setAttribute('endColumn', $endFilePos - $endLinePos);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace LanguageServer\NodeVisitor;
|
|
||||||
|
|
||||||
use PhpParser\{NodeVisitorAbstract, Node};
|
|
||||||
use LanguageServer\{Definition, DefinitionResolver};
|
|
||||||
use LanguageServer\Protocol\SymbolInformation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects definitions of classes, interfaces, traits, methods, properties and constants
|
|
||||||
* Depends on ReferencesAdder and NameResolver
|
|
||||||
*/
|
|
||||||
class DefinitionCollector extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Map from fully qualified name (FQN) to Definition
|
|
||||||
*
|
|
||||||
* @var Definition[]
|
|
||||||
*/
|
|
||||||
public $definitions = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map from fully qualified name (FQN) to Node
|
|
||||||
*
|
|
||||||
* @var Node[]
|
|
||||||
*/
|
|
||||||
public $nodes = [];
|
|
||||||
|
|
||||||
private $definitionResolver;
|
|
||||||
|
|
||||||
public function __construct(DefinitionResolver $definitionResolver)
|
|
||||||
{
|
|
||||||
$this->definitionResolver = $definitionResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
$fqn = DefinitionResolver::getDefinedFqn($node);
|
|
||||||
// Only index definitions with an FQN (no variables)
|
|
||||||
if ($fqn === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->nodes[$fqn] = $node;
|
|
||||||
$this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace LanguageServer\NodeVisitor;
|
|
||||||
|
|
||||||
use PhpParser;
|
|
||||||
use PhpParser\{NodeVisitorAbstract, Node, Comment};
|
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decorates all nodes with a docBlock attribute that is an instance of phpDocumentor\Reflection\DocBlock
|
|
||||||
*/
|
|
||||||
class DocBlockParser extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var DocBlockFactory
|
|
||||||
*/
|
|
||||||
private $docBlockFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current namespace context
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $namespace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prefix from a parent group use declaration
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $prefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Namespace aliases in the current context
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
private $aliases;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var PhpParser\Error[]
|
|
||||||
*/
|
|
||||||
public $errors = [];
|
|
||||||
|
|
||||||
public function __construct(DocBlockFactory $docBlockFactory)
|
|
||||||
{
|
|
||||||
$this->docBlockFactory = $docBlockFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function beforeTraverse(array $nodes)
|
|
||||||
{
|
|
||||||
$this->namespace = '';
|
|
||||||
$this->prefix = '';
|
|
||||||
$this->aliases = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($node instanceof Node\Stmt\Namespace_) {
|
|
||||||
$this->namespace = (string)$node->name;
|
|
||||||
} else if ($node instanceof Node\Stmt\GroupUse) {
|
|
||||||
$this->prefix = (string)$node->prefix . '\\';
|
|
||||||
} else if ($node instanceof Node\Stmt\UseUse) {
|
|
||||||
$this->aliases[$node->alias] = $this->prefix . (string)$node->name;
|
|
||||||
}
|
|
||||||
$docComment = $node->getDocComment();
|
|
||||||
if ($docComment === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$context = new Context($this->namespace, $this->aliases);
|
|
||||||
try {
|
|
||||||
$docBlock = $this->docBlockFactory->create($docComment->getText(), $context);
|
|
||||||
$node->setAttribute('docBlock', $docBlock);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->errors[] = new PhpParser\Error($e->getMessage(), [
|
|
||||||
'startFilePos' => $docComment->getFilePos(),
|
|
||||||
'endFilePos' => $docComment->getFilePos() + strlen($docComment->getText()),
|
|
||||||
'startLine' => $docComment->getLine(),
|
|
||||||
'endLine' => $docComment->getLine() + preg_match_all('/[\\n\\r]/', $docComment->getText()) + 1
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function leaveNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($node instanceof Node\Stmt\Namespace_) {
|
|
||||||
$this->namespace = '';
|
|
||||||
$this->aliases = [];
|
|
||||||
} else if ($node instanceof Node\Stmt\GroupUse) {
|
|
||||||
$this->prefix = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace LanguageServer\NodeVisitor;
|
|
||||||
|
|
||||||
use PhpParser\{NodeVisitorAbstract, Node, NodeTraverser};
|
|
||||||
use LanguageServer\Protocol\{Position, Range};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the Node at a specified position
|
|
||||||
* Depends on ColumnCalculator
|
|
||||||
*/
|
|
||||||
class NodeAtPositionFinder extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The node at the position, if found
|
|
||||||
*
|
|
||||||
* @var Node|null
|
|
||||||
*/
|
|
||||||
public $node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Position
|
|
||||||
*/
|
|
||||||
private $position;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Position $position The position where the node is located
|
|
||||||
*/
|
|
||||||
public function __construct(Position $position)
|
|
||||||
{
|
|
||||||
$this->position = $position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function leaveNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($this->node === null) {
|
|
||||||
$range = Range::fromNode($node);
|
|
||||||
if ($range->includes($this->position)) {
|
|
||||||
$this->node = $node;
|
|
||||||
return NodeTraverser::STOP_TRAVERSAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace LanguageServer\NodeVisitor;
|
|
||||||
|
|
||||||
use PhpParser\{NodeVisitorAbstract, Node};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decorates all nodes with parent and sibling references (similar to DOM nodes)
|
|
||||||
*/
|
|
||||||
class ReferencesAdder extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Node[]
|
|
||||||
*/
|
|
||||||
private $stack = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Node
|
|
||||||
*/
|
|
||||||
private $previous;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private $document;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $document The value for the ownerDocument attribute
|
|
||||||
*/
|
|
||||||
public function __construct($document = null)
|
|
||||||
{
|
|
||||||
$this->document = $document;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
$node->setAttribute('ownerDocument', $this->document);
|
|
||||||
if (!empty($this->stack)) {
|
|
||||||
$node->setAttribute('parentNode', end($this->stack));
|
|
||||||
}
|
|
||||||
if (isset($this->previous) && $this->previous->getAttribute('parentNode') === $node->getAttribute('parentNode')) {
|
|
||||||
$node->setAttribute('previousSibling', $this->previous);
|
|
||||||
$this->previous->setAttribute('nextSibling', $node);
|
|
||||||
}
|
|
||||||
$this->stack[] = $node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function leaveNode(Node $node)
|
|
||||||
{
|
|
||||||
$this->previous = $node;
|
|
||||||
array_pop($this->stack);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace LanguageServer\NodeVisitor;
|
|
||||||
|
|
||||||
use PhpParser\{NodeVisitorAbstract, Node};
|
|
||||||
use LanguageServer\DefinitionResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects references to classes, interfaces, traits, methods, properties and constants
|
|
||||||
* Depends on ReferencesAdder and NameResolver
|
|
||||||
*/
|
|
||||||
class ReferencesCollector extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Map from fully qualified name (FQN) to array of nodes that reference the symbol
|
|
||||||
*
|
|
||||||
* @var Node[][]
|
|
||||||
*/
|
|
||||||
public $nodes = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var DefinitionResolver
|
|
||||||
*/
|
|
||||||
private $definitionResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DefinitionResolver $definitionResolver The DefinitionResolver to resolve reference nodes to definitions
|
|
||||||
*/
|
|
||||||
public function __construct(DefinitionResolver $definitionResolver)
|
|
||||||
{
|
|
||||||
$this->definitionResolver = $definitionResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
// Check if the node references any global symbol
|
|
||||||
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
|
||||||
if ($fqn) {
|
|
||||||
$parent = $node->getAttribute('parentNode');
|
|
||||||
$grandParent = $parent ? $parent->getAttribute('parentNode') : null;
|
|
||||||
$this->addReference($fqn, $node);
|
|
||||||
if (
|
|
||||||
$node instanceof Node\Name
|
|
||||||
&& $node->isQualified()
|
|
||||||
&& !($parent instanceof Node\Stmt\Namespace_ && $parent->name === $node)
|
|
||||||
) {
|
|
||||||
// Add references for each referenced namespace
|
|
||||||
$ns = $fqn;
|
|
||||||
while (($pos = strrpos($ns, '\\')) !== false) {
|
|
||||||
$ns = substr($ns, 0, $pos);
|
|
||||||
$this->addReference($ns, $node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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 ($parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall) {
|
|
||||||
$parts = explode('\\', $fqn);
|
|
||||||
if (count($parts) > 1) {
|
|
||||||
$globalFqn = end($parts);
|
|
||||||
$this->addReference($globalFqn, $node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addReference(string $fqn, Node $node)
|
|
||||||
{
|
|
||||||
if (!isset($this->nodes[$fqn])) {
|
|
||||||
$this->nodes[$fqn] = [];
|
|
||||||
}
|
|
||||||
$this->nodes[$fqn][] = $node;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
namespace LanguageServer\NodeVisitor;
|
|
||||||
|
|
||||||
use PhpParser\{NodeVisitorAbstract, Node, NodeTraverser};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects all references to a variable
|
|
||||||
*/
|
|
||||||
class VariableReferencesCollector extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Array of references to the variable
|
|
||||||
*
|
|
||||||
* @var Node\Expr\Variable[]
|
|
||||||
*/
|
|
||||||
public $nodes = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name The variable name
|
|
||||||
*/
|
|
||||||
public function __construct(string $name)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($node instanceof Node\Expr\Variable && $node->name === $this->name) {
|
|
||||||
$this->nodes[] = $node;
|
|
||||||
} else if ($node instanceof Node\FunctionLike) {
|
|
||||||
// If we meet a function node, dont traverse its statements, they are in another scope
|
|
||||||
// except it is a closure that has imported the variable through use
|
|
||||||
if ($node instanceof Node\Expr\Closure) {
|
|
||||||
foreach ($node->uses as $use) {
|
|
||||||
if ($use->var === $this->name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace LanguageServer;
|
|
||||||
|
|
||||||
use PhpParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom PHP Parser class configured for our needs
|
|
||||||
*/
|
|
||||||
class Parser extends PhpParser\Parser\Php7
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$lexer = new PhpParser\Lexer([
|
|
||||||
'usedAttributes' => [
|
|
||||||
'comments',
|
|
||||||
'startLine',
|
|
||||||
'endLine',
|
|
||||||
'startFilePos',
|
|
||||||
'endFilePos'
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
parent::__construct($lexer);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LanguageServer\ParserHelpers;
|
||||||
|
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
|
function isConstantFetch(Node $node) : bool
|
||||||
|
{
|
||||||
|
$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) ||
|
||||||
|
(
|
||||||
|
$parent instanceof Node\Expression\BinaryExpression &&
|
||||||
|
$parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFunctionLikeDeclarationFromParameter(Node\Parameter $node)
|
||||||
|
{
|
||||||
|
return $node->parent->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFunctionLike(Node $node)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
$node instanceof Node\Statement\FunctionDeclaration ||
|
||||||
|
$node instanceof Node\MethodDeclaration ||
|
||||||
|
$node instanceof Node\Expression\AnonymousFunctionCreationExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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]);
|
||||||
|
}
|
|
@ -3,27 +3,20 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer;
|
namespace LanguageServer;
|
||||||
|
|
||||||
use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, TextEdit};
|
|
||||||
use LanguageServer\NodeVisitor\{
|
|
||||||
NodeAtPositionFinder,
|
|
||||||
ReferencesAdder,
|
|
||||||
DocBlockParser,
|
|
||||||
DefinitionCollector,
|
|
||||||
ColumnCalculator,
|
|
||||||
ReferencesCollector
|
|
||||||
};
|
|
||||||
use LanguageServer\Index\Index;
|
use LanguageServer\Index\Index;
|
||||||
use PhpParser\{Error, ErrorHandler, Node, NodeTraverser};
|
use LanguageServer\Protocol\{
|
||||||
use PhpParser\NodeVisitor\NameResolver;
|
Diagnostic, Position, Range
|
||||||
|
};
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use Sabre\Uri;
|
|
||||||
|
|
||||||
class PhpDocument
|
class PhpDocument
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The PHPParser instance
|
* The PHPParser instance
|
||||||
*
|
*
|
||||||
* @var Parser
|
* @var PhpParser\Parser
|
||||||
*/
|
*/
|
||||||
private $parser;
|
private $parser;
|
||||||
|
|
||||||
|
@ -63,7 +56,7 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* The AST of the document
|
* The AST of the document
|
||||||
*
|
*
|
||||||
* @var Node[]
|
* @var Node
|
||||||
*/
|
*/
|
||||||
private $stmts;
|
private $stmts;
|
||||||
|
|
||||||
|
@ -77,7 +70,7 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* Map from fully qualified name (FQN) to Node
|
* Map from fully qualified name (FQN) to Node
|
||||||
*
|
*
|
||||||
* @var Node[]
|
* @var Node
|
||||||
*/
|
*/
|
||||||
private $definitionNodes;
|
private $definitionNodes;
|
||||||
|
|
||||||
|
@ -96,18 +89,18 @@ class PhpDocument
|
||||||
private $diagnostics;
|
private $diagnostics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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 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
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $uri,
|
string $uri,
|
||||||
string $content,
|
string $content,
|
||||||
Index $index,
|
Index $index,
|
||||||
Parser $parser,
|
$parser,
|
||||||
DocBlockFactory $docBlockFactory,
|
DocBlockFactory $docBlockFactory,
|
||||||
DefinitionResolver $definitionResolver
|
DefinitionResolver $definitionResolver
|
||||||
) {
|
) {
|
||||||
|
@ -133,7 +126,7 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* Updates the content on this document.
|
* Updates the content on this document.
|
||||||
* Re-parses a source file, updates symbols and reports parsing errors
|
* Re-parses a source file, updates symbols and reports parsing errors
|
||||||
* that may have occured as diagnostics.
|
* that may have occurred as diagnostics.
|
||||||
*
|
*
|
||||||
* @param string $content
|
* @param string $content
|
||||||
* @return void
|
* @return void
|
||||||
|
@ -160,64 +153,26 @@ class PhpDocument
|
||||||
$this->definitions = null;
|
$this->definitions = null;
|
||||||
$this->definitionNodes = null;
|
$this->definitionNodes = null;
|
||||||
|
|
||||||
$errorHandler = new ErrorHandler\Collecting;
|
$treeAnalyzer = new TreeAnalyzer($this->parser, $content, $this->docBlockFactory, $this->definitionResolver, $this->uri);
|
||||||
$stmts = $this->parser->parse($content, $errorHandler);
|
|
||||||
|
|
||||||
$this->diagnostics = [];
|
$this->diagnostics = $treeAnalyzer->getDiagnostics();
|
||||||
foreach ($errorHandler->getErrors() as $error) {
|
|
||||||
$this->diagnostics[] = Diagnostic::fromError($error, $this->content, DiagnosticSeverity::ERROR, 'php');
|
$this->definitions = $treeAnalyzer->getDefinitions();
|
||||||
|
|
||||||
|
$this->definitionNodes = $treeAnalyzer->getDefinitionNodes();
|
||||||
|
|
||||||
|
$this->referenceNodes = $treeAnalyzer->getReferenceNodes();
|
||||||
|
|
||||||
|
foreach ($this->definitions as $fqn => $definition) {
|
||||||
|
$this->index->setDefinition($fqn, $definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// $stmts can be null in case of a fatal parsing error
|
// Register this document on the project for references
|
||||||
if ($stmts) {
|
foreach ($this->referenceNodes as $fqn => $nodes) {
|
||||||
$traverser = new NodeTraverser;
|
$this->index->addReferenceUri($fqn, $this->uri);
|
||||||
|
|
||||||
// Resolve aliased names to FQNs
|
|
||||||
$traverser->addVisitor(new NameResolver($errorHandler));
|
|
||||||
|
|
||||||
// Add parentNode, previousSibling, nextSibling attributes
|
|
||||||
$traverser->addVisitor(new ReferencesAdder($this));
|
|
||||||
|
|
||||||
// Add column attributes to nodes
|
|
||||||
$traverser->addVisitor(new ColumnCalculator($content));
|
|
||||||
|
|
||||||
// Parse docblocks and add docBlock attributes to nodes
|
|
||||||
$docBlockParser = new DocBlockParser($this->docBlockFactory);
|
|
||||||
$traverser->addVisitor($docBlockParser);
|
|
||||||
|
|
||||||
$traverser->traverse($stmts);
|
|
||||||
|
|
||||||
// Report errors from parsing docblocks
|
|
||||||
foreach ($docBlockParser->errors as $error) {
|
|
||||||
$this->diagnostics[] = Diagnostic::fromError($error, $this->content, DiagnosticSeverity::WARNING, 'php');
|
|
||||||
}
|
|
||||||
|
|
||||||
$traverser = new NodeTraverser;
|
|
||||||
|
|
||||||
// Collect all definitions
|
|
||||||
$definitionCollector = new DefinitionCollector($this->definitionResolver);
|
|
||||||
$traverser->addVisitor($definitionCollector);
|
|
||||||
|
|
||||||
// Collect all references
|
|
||||||
$referencesCollector = new ReferencesCollector($this->definitionResolver);
|
|
||||||
$traverser->addVisitor($referencesCollector);
|
|
||||||
|
|
||||||
$traverser->traverse($stmts);
|
|
||||||
|
|
||||||
// Register this document on the project for all the symbols defined in it
|
|
||||||
$this->definitions = $definitionCollector->definitions;
|
|
||||||
$this->definitionNodes = $definitionCollector->nodes;
|
|
||||||
foreach ($definitionCollector->definitions as $fqn => $definition) {
|
|
||||||
$this->index->setDefinition($fqn, $definition);
|
|
||||||
}
|
|
||||||
// Register this document on the project for references
|
|
||||||
$this->referenceNodes = $referencesCollector->nodes;
|
|
||||||
foreach ($referencesCollector->nodes as $fqn => $nodes) {
|
|
||||||
$this->index->addReferenceUri($fqn, $this->uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->stmts = $stmts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->stmts = $treeAnalyzer->getStmts();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,9 +221,9 @@ class PhpDocument
|
||||||
/**
|
/**
|
||||||
* Returns the AST of the document
|
* Returns the AST of the document
|
||||||
*
|
*
|
||||||
* @return Node[]
|
* @return Node | null
|
||||||
*/
|
*/
|
||||||
public function getStmts(): array
|
public function getStmts()
|
||||||
{
|
{
|
||||||
return $this->stmts;
|
return $this->stmts;
|
||||||
}
|
}
|
||||||
|
@ -284,11 +239,13 @@ class PhpDocument
|
||||||
if ($this->stmts === null) {
|
if ($this->stmts === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$traverser = new NodeTraverser;
|
|
||||||
$finder = new NodeAtPositionFinder($position);
|
$offset = $position->toOffset($this->stmts->getFileContents());
|
||||||
$traverser->addVisitor($finder);
|
$node = $this->stmts->getDescendantNodeAtPosition($offset);
|
||||||
$traverser->traverse($this->stmts);
|
if ($node !== null && $node->getStart() > $offset) {
|
||||||
return $finder->node;
|
return null;
|
||||||
|
}
|
||||||
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,6 +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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes care of loading documents and managing "open" documents
|
* Takes care of loading documents and managing "open" documents
|
||||||
|
@ -36,6 +37,11 @@ class PhpDocumentLoader
|
||||||
*/
|
*/
|
||||||
private $parser;
|
private $parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PhpParser\Parser
|
||||||
|
*/
|
||||||
|
private $tolerantParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DocBlockFactory
|
* @var DocBlockFactory
|
||||||
*/
|
*/
|
||||||
|
@ -47,9 +53,10 @@ class PhpDocumentLoader
|
||||||
private $definitionResolver;
|
private $definitionResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ContentRetriever $contentRetriever
|
* @param ContentRetriever $contentRetriever
|
||||||
* @param ProjectIndex $project
|
* @param ProjectIndex $projectIndex
|
||||||
* @param DefinitionResolver $definitionResolver
|
* @param DefinitionResolver $definitionResolver
|
||||||
|
* @internal param ProjectIndex $project
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ContentRetriever $contentRetriever,
|
ContentRetriever $contentRetriever,
|
||||||
|
@ -59,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 Parser;
|
$this->parser = new PhpParser\Parser();
|
||||||
$this->docBlockFactory = DocBlockFactory::createInstance();
|
$this->docBlockFactory = DocBlockFactory::createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace LanguageServer\Protocol;
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
use PhpParser\Error;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a diagnostic, such as a compiler error or warning. Diagnostic objects are only valid in the scope of a
|
* Represents a diagnostic, such as a compiler error or warning. Diagnostic objects are only valid in the scope of a
|
||||||
* resource.
|
* resource.
|
||||||
|
@ -47,26 +45,6 @@ class Diagnostic
|
||||||
*/
|
*/
|
||||||
public $message;
|
public $message;
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a diagnostic from a PhpParser Error
|
|
||||||
*
|
|
||||||
* @param Error $error Message and code will be used
|
|
||||||
* @param string $content The file content to calculate the column info
|
|
||||||
* @param int $severity DiagnosticSeverity
|
|
||||||
* @param string $source A human-readable string describing the source of this diagnostic
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public static function fromError(Error $error, string $content, int $severity = null, string $source = null): self
|
|
||||||
{
|
|
||||||
return new self(
|
|
||||||
$error->getRawMessage(), // Do not include "on line ..." in the error message
|
|
||||||
Range::fromError($error, $content),
|
|
||||||
$error->getCode(),
|
|
||||||
$severity,
|
|
||||||
$source
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $message The diagnostic's message
|
* @param string $message The diagnostic's message
|
||||||
* @param Range $range The range at which the message applies
|
* @param Range $range The range at which the message applies
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace LanguageServer\Protocol;
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
use PhpParser\Node;
|
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.
|
||||||
|
@ -25,9 +26,13 @@ class Location
|
||||||
* @param Node $node
|
* @param Node $node
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function fromNode(Node $node)
|
public static function fromNode($node)
|
||||||
{
|
{
|
||||||
return new self($node->getAttribute('ownerDocument')->getUri(), Range::fromNode($node));
|
$range = PhpParser\PositionUtilities::getRangeFromPosition($node->getStart(), $node->getWidth(), $node->getFileContents());
|
||||||
|
return new self($node->getUri(), new Range(
|
||||||
|
new Position($range->start->line, $range->start->character),
|
||||||
|
new Position($range->end->line, $range->end->character)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(string $uri = null, Range $range = null)
|
public function __construct(string $uri = null, Range $range = null)
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace LanguageServer\Protocol;
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
use PhpParser\{Error, Node};
|
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.
|
||||||
|
@ -31,26 +32,12 @@ class Range
|
||||||
*/
|
*/
|
||||||
public static function fromNode(Node $node)
|
public static function fromNode(Node $node)
|
||||||
{
|
{
|
||||||
return new self(
|
$range = PhpParser\PositionUtilities::getRangeFromPosition($node->getStart(), $node->getWidth(), $node->getFileContents());
|
||||||
new Position($node->getAttribute('startLine') - 1, $node->getAttribute('startColumn') - 1),
|
|
||||||
new Position($node->getAttribute('endLine') - 1, $node->getAttribute('endColumn'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return new self(
|
||||||
* Returns the range where an error occured
|
new Position($range->start->line, $range->start->character),
|
||||||
*
|
new Position($range->end->line, $range->end->character)
|
||||||
* @param \PhpParser\Error $error
|
);
|
||||||
* @param string $content
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public static function fromError(Error $error, string $content)
|
|
||||||
{
|
|
||||||
$startLine = max($error->getStartLine() - 1, 0);
|
|
||||||
$endLine = max($error->getEndLine() - 1, $startLine);
|
|
||||||
$startColumn = $error->hasColumnInfo() ? $error->getStartColumn($content) - 1 : 0;
|
|
||||||
$endColumn = $error->hasColumnInfo() ? $error->getEndColumn($content) : 0;
|
|
||||||
return new self(new Position($startLine, $startColumn), new Position($endLine, $endColumn));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(Position $start = null, Position $end = null)
|
public function __construct(Position $start = null, Position $end = null)
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace LanguageServer\Protocol;
|
namespace LanguageServer\Protocol;
|
||||||
|
|
||||||
use PhpParser\Node;
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,66 +45,68 @@ class SymbolInformation
|
||||||
*
|
*
|
||||||
* @param 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 self|null
|
* @return SymbolInformation|null
|
||||||
*/
|
*/
|
||||||
public static function fromNode(Node $node, string $fqn = null)
|
public static function fromNode($node, string $fqn = null)
|
||||||
{
|
{
|
||||||
$parent = $node->getAttribute('parentNode');
|
|
||||||
$symbol = new self;
|
$symbol = new self;
|
||||||
|
if ($node instanceof Node\Statement\ClassDeclaration) {
|
||||||
if (
|
$symbol->kind = SymbolKind::CLASS_;
|
||||||
$node instanceof Node\Expr\FuncCall
|
} else if ($node instanceof Node\Statement\TraitDeclaration) {
|
||||||
&& $node->name instanceof Node\Name
|
$symbol->kind = SymbolKind::CLASS_;
|
||||||
&& strtolower((string)$node->name) === 'define'
|
} else if (\LanguageServer\ParserHelpers\isConstDefineExpression($node)) {
|
||||||
&& isset($node->args[0])
|
|
||||||
&& $node->args[0]->value instanceof Node\Scalar\String_
|
|
||||||
&& isset($node->args[1])
|
|
||||||
) {
|
|
||||||
// constants with define() like
|
// constants with define() like
|
||||||
// define('TEST_DEFINE_CONSTANT', false);
|
// define('TEST_DEFINE_CONSTANT', false);
|
||||||
$symbol->kind = SymbolKind::CONSTANT;
|
$symbol->kind = SymbolKind::CONSTANT;
|
||||||
$symbol->name = (string)$node->args[0]->value->value;
|
$symbol->name = $node->argumentExpressionList->children[0]->expression->getStringContentsText();
|
||||||
} else if ($node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_) {
|
} else if ($node instanceof Node\Statement\InterfaceDeclaration) {
|
||||||
$symbol->kind = SymbolKind::CLASS_;
|
|
||||||
} else if ($node instanceof Node\Stmt\Interface_) {
|
|
||||||
$symbol->kind = SymbolKind::INTERFACE;
|
$symbol->kind = SymbolKind::INTERFACE;
|
||||||
} else if ($node instanceof Node\Name && $parent instanceof Node\Stmt\Namespace_) {
|
} else if ($node instanceof Node\Statement\NamespaceDefinition) {
|
||||||
$symbol->kind = SymbolKind::NAMESPACE;
|
$symbol->kind = SymbolKind::NAMESPACE;
|
||||||
} else if ($node instanceof Node\Stmt\Function_) {
|
} else if ($node instanceof Node\Statement\FunctionDeclaration) {
|
||||||
$symbol->kind = SymbolKind::FUNCTION;
|
$symbol->kind = SymbolKind::FUNCTION;
|
||||||
} else if ($node instanceof Node\Stmt\ClassMethod && ($node->name === '__construct' || $node->name === '__destruct')) {
|
} else if ($node instanceof Node\MethodDeclaration) {
|
||||||
$symbol->kind = SymbolKind::CONSTRUCTOR;
|
$nameText = $node->getName();
|
||||||
} else if ($node instanceof Node\Stmt\ClassMethod) {
|
if ($nameText === '__construct' || $nameText === '__destruct') {
|
||||||
$symbol->kind = SymbolKind::METHOD;
|
$symbol->kind = SymbolKind::CONSTRUCTOR;
|
||||||
} else if ($node instanceof Node\Stmt\PropertyProperty) {
|
} else {
|
||||||
|
$symbol->kind = SymbolKind::METHOD;
|
||||||
|
}
|
||||||
|
} 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 Node\Const_) {
|
} else if ($node instanceof Node\ConstElement) {
|
||||||
$symbol->kind = SymbolKind::CONSTANT;
|
$symbol->kind = SymbolKind::CONSTANT;
|
||||||
} else if (
|
} else if (
|
||||||
(
|
(
|
||||||
($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp)
|
($node instanceof Node\Expression\AssignmentExpression)
|
||||||
&& $node->var instanceof Node\Expr\Variable
|
&& $node->leftOperand instanceof Node\Expression\Variable
|
||||||
)
|
)
|
||||||
|| $node instanceof Node\Expr\ClosureUse
|
|| $node instanceof Node\UseVariableName
|
||||||
|| $node instanceof Node\Param
|
|| $node instanceof Node\Parameter
|
||||||
) {
|
) {
|
||||||
$symbol->kind = SymbolKind::VARIABLE;
|
$symbol->kind = SymbolKind::VARIABLE;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($symbol->name)) {
|
if ($node instanceof Node\Expression\AssignmentExpression) {
|
||||||
if ($node instanceof Node\Name) {
|
if ($node->leftOperand instanceof Node\Expression\Variable) {
|
||||||
$symbol->name = (string)$node;
|
$symbol->name = $node->leftOperand->getName();
|
||||||
} else if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) {
|
} elseif ($node->leftOperand instanceof PhpParser\Token) {
|
||||||
$symbol->name = $node->var->name;
|
$symbol->name = trim($node->leftOperand->getText($node->getFileContents()), "$");
|
||||||
} else if ($node instanceof Node\Expr\ClosureUse) {
|
|
||||||
$symbol->name = $node->var;
|
|
||||||
} else if (isset($node->name)) {
|
|
||||||
$symbol->name = (string)$node->name;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
} else if ($node instanceof Node\UseVariableName) {
|
||||||
|
$symbol->name = $node->getName();
|
||||||
|
} else if (isset($node->name)) {
|
||||||
|
if ($node->name instanceof Node\QualifiedName) {
|
||||||
|
$symbol->name = (string)PhpParser\ResolvedName::buildName($node->name->nameParts, $node->getFileContents());
|
||||||
|
} else {
|
||||||
|
$symbol->name = ltrim((string)$node->name->getText($node->getFileContents()), "$");
|
||||||
|
}
|
||||||
|
} else if (isset($node->variableName)) {
|
||||||
|
$symbol->name = $node->variableName->getText($node);
|
||||||
|
} else if (!isset($symbol->name)) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$symbol->location = Location::fromNode($node);
|
$symbol->location = Location::fromNode($node);
|
||||||
|
|
|
@ -3,34 +3,21 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer\Server;
|
namespace LanguageServer\Server;
|
||||||
|
|
||||||
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
|
use LanguageServer\{
|
||||||
use PhpParser\{Node, NodeTraverser};
|
CompletionProvider, LanguageClient, PhpDocument, PhpDocumentLoader, DefinitionResolver
|
||||||
use LanguageServer\{LanguageClient, PhpDocumentLoader, PhpDocument, DefinitionResolver, CompletionProvider};
|
|
||||||
use LanguageServer\NodeVisitor\VariableReferencesCollector;
|
|
||||||
use LanguageServer\Protocol\{
|
|
||||||
SymbolLocationInformation,
|
|
||||||
SymbolDescriptor,
|
|
||||||
TextDocumentItem,
|
|
||||||
TextDocumentIdentifier,
|
|
||||||
VersionedTextDocumentIdentifier,
|
|
||||||
Position,
|
|
||||||
Range,
|
|
||||||
FormattingOptions,
|
|
||||||
TextEdit,
|
|
||||||
Location,
|
|
||||||
SymbolInformation,
|
|
||||||
ReferenceContext,
|
|
||||||
Hover,
|
|
||||||
MarkedString,
|
|
||||||
SymbolKind,
|
|
||||||
CompletionItem,
|
|
||||||
CompletionItemKind
|
|
||||||
};
|
};
|
||||||
use LanguageServer\Index\ReadableIndex;
|
use LanguageServer\Index\ReadableIndex;
|
||||||
|
use LanguageServer\Protocol\{
|
||||||
|
FormattingOptions, Hover, Location, MarkedString, Position, Range, ReferenceContext, SymbolDescriptor, SymbolLocationInformation, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier
|
||||||
|
};
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use Sabre\Event\Promise;
|
use Sabre\Event\Promise;
|
||||||
use Sabre\Uri;
|
use Sabre\Uri;
|
||||||
|
use function LanguageServer\{
|
||||||
|
isVendored, waitForEvent
|
||||||
|
};
|
||||||
use function Sabre\Event\coroutine;
|
use function Sabre\Event\coroutine;
|
||||||
use function LanguageServer\{waitForEvent, isVendored};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides method handlers for all textDocument/* methods
|
* Provides method handlers for all textDocument/* methods
|
||||||
|
@ -49,11 +36,6 @@ class TextDocument
|
||||||
*/
|
*/
|
||||||
protected $project;
|
protected $project;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var PrettyPrinter
|
|
||||||
*/
|
|
||||||
protected $prettyPrinter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DefinitionResolver
|
* @var DefinitionResolver
|
||||||
*/
|
*/
|
||||||
|
@ -80,12 +62,12 @@ class TextDocument
|
||||||
protected $composerLock;
|
protected $composerLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param PhpDocumentLoader $documentLoader
|
* @param PhpDocumentLoader $documentLoader
|
||||||
* @param DefinitionResolver $definitionResolver
|
* @param DefinitionResolver $definitionResolver
|
||||||
* @param LanguageClient $client
|
* @param LanguageClient $client
|
||||||
* @param ReadableIndex $index
|
* @param ReadableIndex $index
|
||||||
* @param \stdClass $composerJson
|
* @param \stdClass $composerJson
|
||||||
* @param \stdClass $composerLock
|
* @param \stdClass $composerLock
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
PhpDocumentLoader $documentLoader,
|
PhpDocumentLoader $documentLoader,
|
||||||
|
@ -97,7 +79,6 @@ class TextDocument
|
||||||
) {
|
) {
|
||||||
$this->documentLoader = $documentLoader;
|
$this->documentLoader = $documentLoader;
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->prettyPrinter = new PrettyPrinter();
|
|
||||||
$this->definitionResolver = $definitionResolver;
|
$this->definitionResolver = $definitionResolver;
|
||||||
$this->completionProvider = new CompletionProvider($this->definitionResolver, $index);
|
$this->completionProvider = new CompletionProvider($this->definitionResolver, $index);
|
||||||
$this->index = $index;
|
$this->index = $index;
|
||||||
|
@ -202,31 +183,34 @@ class TextDocument
|
||||||
// Variables always stay in the boundary of the file and need to be searched inside their function scope
|
// Variables always stay in the boundary of the file and need to be searched inside their function scope
|
||||||
// by traversing the AST
|
// by traversing the AST
|
||||||
if (
|
if (
|
||||||
$node instanceof Node\Expr\Variable
|
|
||||||
|| $node instanceof Node\Param
|
($node instanceof Node\Expression\Variable && !($node->getParent()->getParent() instanceof Node\PropertyDeclaration))
|
||||||
|| $node instanceof Node\Expr\ClosureUse
|
|| $node instanceof Node\Parameter
|
||||||
|
|| $node instanceof Node\UseVariableName
|
||||||
) {
|
) {
|
||||||
if ($node->name instanceof Node\Expr) {
|
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;
|
||||||
while (isset($n) && !($n instanceof Node\FunctionLike)) {
|
|
||||||
$n = $n->getAttribute('parentNode');
|
$n = $n->getFirstAncestor(Node\Statement\FunctionDeclaration::class, Node\MethodDeclaration::class, Node\Expression\AnonymousFunctionCreationExpression::class, Node\SourceFileNode::class);
|
||||||
|
|
||||||
|
if ($n === null) {
|
||||||
|
$n = $node->getFirstAncestor(Node\Statement\ExpressionStatement::class)->getParent();
|
||||||
}
|
}
|
||||||
if (!isset($n)) {
|
|
||||||
$n = $node->getAttribute('ownerDocument');
|
foreach ($n->getDescendantNodes() as $descendantNode) {
|
||||||
}
|
if ($descendantNode instanceof Node\Expression\Variable &&
|
||||||
$traverser = new NodeTraverser;
|
$descendantNode->getName() === $node->getName()
|
||||||
$refCollector = new VariableReferencesCollector($node->name);
|
) {
|
||||||
$traverser->addVisitor($refCollector);
|
$locations[] = Location::fromNode($descendantNode);
|
||||||
$traverser->traverse($n->getStmts());
|
}
|
||||||
foreach ($refCollector->nodes as $ref) {
|
|
||||||
$locations[] = Location::fromNode($ref);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Definition with a global FQN
|
// Definition with a global FQN
|
||||||
$fqn = DefinitionResolver::getDefinedFqn($node);
|
$fqn = DefinitionResolver::getDefinedFqn($node);
|
||||||
|
|
||||||
// Wait until indexing finished
|
// Wait until indexing finished
|
||||||
if (!$this->index->isComplete()) {
|
if (!$this->index->isComplete()) {
|
||||||
yield waitForEvent($this->index, 'complete');
|
yield waitForEvent($this->index, 'complete');
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer;
|
||||||
|
|
||||||
|
use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, TextEdit};
|
||||||
|
use LanguageServer\Index\Index;
|
||||||
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
|
use Sabre\Uri;
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
|
class TreeAnalyzer
|
||||||
|
{
|
||||||
|
/** @var PhpParser\Parser */
|
||||||
|
private $parser;
|
||||||
|
|
||||||
|
/** @var Node */
|
||||||
|
private $stmts;
|
||||||
|
|
||||||
|
/** @var Diagnostic[] */
|
||||||
|
private $diagnostics;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $content;
|
||||||
|
|
||||||
|
/** @var Node[] */
|
||||||
|
private $referenceNodes;
|
||||||
|
|
||||||
|
/** @var Definition[] */
|
||||||
|
private $definitions;
|
||||||
|
|
||||||
|
/** @var Node[] */
|
||||||
|
private $definitionNodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PhpParser\Parser $parser
|
||||||
|
* @param string $content
|
||||||
|
* @param DocBlockFactory $docBlockFactory
|
||||||
|
* @param DefinitionResolver $definitionResolver
|
||||||
|
* @param string $uri
|
||||||
|
*/
|
||||||
|
public function __construct(PhpParser\Parser $parser, string $content, DocBlockFactory $docBlockFactory, DefinitionResolver $definitionResolver, string $uri)
|
||||||
|
{
|
||||||
|
$this->parser = $parser;
|
||||||
|
$this->docBlockFactory = $docBlockFactory;
|
||||||
|
$this->definitionResolver = $definitionResolver;
|
||||||
|
$this->content = $content;
|
||||||
|
$this->stmts = $this->parser->parseSourceFile($content, $uri);
|
||||||
|
|
||||||
|
// TODO - docblock errors
|
||||||
|
|
||||||
|
$this->collectDefinitionsAndReferences($this->stmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectDefinitionsAndReferences(Node $stmts)
|
||||||
|
{
|
||||||
|
foreach ($stmts::CHILD_NAMES as $name) {
|
||||||
|
$node = $stmts->$name;
|
||||||
|
|
||||||
|
if ($node === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_array($node)) {
|
||||||
|
foreach ($node as $child) {
|
||||||
|
if ($child instanceof Node) {
|
||||||
|
$this->update($child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node) {
|
||||||
|
$this->update($node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) {
|
||||||
|
$range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->content);
|
||||||
|
|
||||||
|
$this->diagnostics[] = new Diagnostic(
|
||||||
|
$error->message,
|
||||||
|
new Range(
|
||||||
|
new Position($range->start->line, $range->start->character),
|
||||||
|
new Position($range->end->line, $range->start->character)
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
DiagnosticSeverity::ERROR,
|
||||||
|
'php'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect definitions and references for the given node
|
||||||
|
*
|
||||||
|
* @param Node $node
|
||||||
|
*/
|
||||||
|
private function update(Node $node)
|
||||||
|
{
|
||||||
|
$fqn = ($this->definitionResolver)::getDefinedFqn($node);
|
||||||
|
// Only index definitions with an FQN (no variables)
|
||||||
|
if ($fqn !== null) {
|
||||||
|
$this->definitionNodes[$fqn] = $node;
|
||||||
|
$this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn);
|
||||||
|
} else {
|
||||||
|
$parent = $node->parent;
|
||||||
|
if (!(
|
||||||
|
(
|
||||||
|
// $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
|
($node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
|
$node instanceof Node\Expression\MemberAccessExpression)
|
||||||
|
&& !(
|
||||||
|
$node->parent instanceof Node\Expression\CallExpression ||
|
||||||
|
$node->memberName instanceof PhpParser\Token
|
||||||
|
))
|
||||||
|
|| ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart()))
|
||||||
|
) {
|
||||||
|
$fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node);
|
||||||
|
if ($fqn !== null) {
|
||||||
|
$this->addReference($fqn, $node);
|
||||||
|
|
||||||
|
if (
|
||||||
|
$node instanceof Node\QualifiedName
|
||||||
|
&& ($node->isQualifiedName() || $node->parent instanceof Node\NamespaceUseClause)
|
||||||
|
&& !($parent instanceof Node\Statement\NamespaceDefinition && $parent->name->getStart() === $node->getStart()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Add references for each referenced namespace
|
||||||
|
$ns = $fqn;
|
||||||
|
while (($pos = strrpos($ns, '\\')) !== false) {
|
||||||
|
$ns = substr($ns, 0, $pos);
|
||||||
|
$this->addReference($ns, $node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) ||
|
||||||
|
($parent instanceof Node\Expression\CallExpression
|
||||||
|
&& !(
|
||||||
|
$node instanceof Node\Expression\ScopedPropertyAccessExpression ||
|
||||||
|
$node instanceof Node\Expression\MemberAccessExpression
|
||||||
|
))) {
|
||||||
|
$parts = explode('\\', $fqn);
|
||||||
|
if (count($parts) > 1) {
|
||||||
|
$globalFqn = end($parts);
|
||||||
|
$this->addReference($globalFqn, $node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->collectDefinitionsAndReferences($node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Diagnostic[]
|
||||||
|
*/
|
||||||
|
public function getDiagnostics(): array
|
||||||
|
{
|
||||||
|
return $this->diagnostics ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function addReference(string $fqn, Node $node)
|
||||||
|
{
|
||||||
|
if (!isset($this->referenceNodes[$fqn])) {
|
||||||
|
$this->referenceNodes[$fqn] = [];
|
||||||
|
}
|
||||||
|
$this->referenceNodes[$fqn][] = $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Definition
|
||||||
|
*/
|
||||||
|
public function getDefinitions()
|
||||||
|
{
|
||||||
|
return $this->definitions ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Node[]
|
||||||
|
*/
|
||||||
|
public function getDefinitionNodes()
|
||||||
|
{
|
||||||
|
return $this->definitionNodes ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Node[]
|
||||||
|
*/
|
||||||
|
public function getReferenceNodes()
|
||||||
|
{
|
||||||
|
return $this->referenceNodes ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Node[]
|
||||||
|
*/
|
||||||
|
public function getStmts()
|
||||||
|
{
|
||||||
|
return $this->stmts;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ namespace LanguageServer;
|
||||||
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use PhpParser\Node;
|
|
||||||
use Sabre\Event\{Loop, Promise, EmitterInterface};
|
use Sabre\Event\{Loop, Promise, EmitterInterface};
|
||||||
use Sabre\Uri;
|
use Sabre\Uri;
|
||||||
|
|
||||||
|
@ -94,23 +93,6 @@ function waitForEvent(EmitterInterface $emitter, string $event): Promise
|
||||||
return $p;
|
return $p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the closest node of a specific type
|
|
||||||
*
|
|
||||||
* @param Node $node
|
|
||||||
* @param string $type The node class name
|
|
||||||
* @return Node|null $type
|
|
||||||
*/
|
|
||||||
function getClosestNode(Node $node, string $type)
|
|
||||||
{
|
|
||||||
$n = $node;
|
|
||||||
while ($n = $n->getAttribute('parentNode')) {
|
|
||||||
if ($n instanceof $type) {
|
|
||||||
return $n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the part of $b that is not overlapped by $a
|
* Returns the part of $b that is not overlapped by $a
|
||||||
* Example:
|
* Example:
|
||||||
|
|
|
@ -5,32 +5,33 @@ namespace LanguageServer\Tests;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Index\Index;
|
use LanguageServer\Index\Index;
|
||||||
use LanguageServer\{DefinitionResolver, Parser};
|
use LanguageServer\DefinitionResolver;
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
class DefinitionResolverTest extends TestCase
|
class DefinitionResolverTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testCreateDefinitionFromNode()
|
public function testCreateDefinitionFromNode()
|
||||||
{
|
{
|
||||||
$parser = new Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$stmts = $parser->parse("<?php\ndefine('TEST_DEFINE', true);");
|
$doc = new MockPhpDocument;
|
||||||
$stmts[0]->setAttribute('ownerDocument', new MockPhpDocument);
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
||||||
|
|
||||||
$index = new Index;
|
$index = new Index;
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
$def = $definitionResolver->createDefinitionFromNode($stmts[0], '\TEST_DEFINE');
|
$def = $definitionResolver->createDefinitionFromNode($stmts->statementList[1]->expression, '\TEST_DEFINE');
|
||||||
|
|
||||||
$this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $def->type);
|
$this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $def->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetTypeFromNode()
|
public function testGetTypeFromNode()
|
||||||
{
|
{
|
||||||
$parser = new Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$stmts = $parser->parse("<?php\ndefine('TEST_DEFINE', true);");
|
$doc = new MockPhpDocument;
|
||||||
$stmts[0]->setAttribute('ownerDocument', new MockPhpDocument);
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
||||||
|
|
||||||
$index = new Index;
|
$index = new Index;
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
$type = $definitionResolver->getTypeFromNode($stmts[0]);
|
$type = $definitionResolver->getTypeFromNode($stmts->statementList[1]->expression);
|
||||||
|
|
||||||
$this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $type);
|
$this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $type);
|
||||||
}
|
}
|
||||||
|
@ -38,26 +39,26 @@ 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 Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$stmts = $parser->parse("<?php\ndefine('TEST_DEFINE');");
|
$doc = new MockPhpDocument;
|
||||||
$stmts[0]->setAttribute('ownerDocument', new MockPhpDocument);
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE');", $doc->getUri());
|
||||||
|
|
||||||
$index = new Index;
|
$index = new Index;
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
$fqn = $definitionResolver->getDefinedFqn($stmts[0]);
|
$fqn = $definitionResolver->getDefinedFqn($stmts->statementList[1]->expression);
|
||||||
|
|
||||||
$this->assertNull($fqn);
|
$this->assertNull($fqn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetDefinedFqnForDefine()
|
public function testGetDefinedFqnForDefine()
|
||||||
{
|
{
|
||||||
$parser = new Parser;
|
$parser = new PhpParser\Parser;
|
||||||
$stmts = $parser->parse("<?php\ndefine('TEST_DEFINE', true);");
|
$doc = new MockPhpDocument;
|
||||||
$stmts[0]->setAttribute('ownerDocument', new MockPhpDocument);
|
$stmts = $parser->parseSourceFile("<?php\ndefine('TEST_DEFINE', true);", $doc->getUri());
|
||||||
|
|
||||||
$index = new Index;
|
$index = new Index;
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
$fqn = $definitionResolver->getDefinedFqn($stmts[0]);
|
$fqn = $definitionResolver->getDefinedFqn($stmts->statementList[1]->expression);
|
||||||
|
|
||||||
$this->assertEquals('TEST_DEFINE', $fqn);
|
$this->assertEquals('TEST_DEFINE', $fqn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,39 +4,21 @@ declare(strict_types = 1);
|
||||||
namespace LanguageServer\Tests\Server\TextDocument;
|
namespace LanguageServer\Tests\Server\TextDocument;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use PhpParser\{NodeTraverser, Node};
|
|
||||||
use PhpParser\NodeVisitor\NameResolver;
|
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use LanguageServer\{LanguageClient, PhpDocument, PhpDocumentLoader, Parser, DefinitionResolver};
|
use LanguageServer\{
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
DefinitionResolver, TreeAnalyzer
|
||||||
use LanguageServer\Protocol\ClientCapabilities;
|
};
|
||||||
use LanguageServer\Index\{ProjectIndex, Index, DependenciesIndex};
|
use LanguageServer\Index\{Index};
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
|
||||||
use LanguageServer\NodeVisitor\{ReferencesAdder, DefinitionCollector};
|
|
||||||
use function LanguageServer\pathToUri;
|
use function LanguageServer\pathToUri;
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
|
|
||||||
class DefinitionCollectorTest extends TestCase
|
class DefinitionCollectorTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testCollectsSymbols()
|
public function testCollectsSymbols()
|
||||||
{
|
{
|
||||||
$path = realpath(__DIR__ . '/../../fixtures/symbols.php');
|
$path = realpath(__DIR__ . '/../../fixtures/symbols.php');
|
||||||
$uri = pathToUri($path);
|
$defNodes = $this->collectDefinitions($path);
|
||||||
$parser = new Parser;
|
|
||||||
$docBlockFactory = DocBlockFactory::createInstance();
|
|
||||||
$index = new Index;
|
|
||||||
$definitionResolver = new DefinitionResolver($index);
|
|
||||||
$content = file_get_contents($path);
|
|
||||||
$document = new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver);
|
|
||||||
$stmts = $parser->parse($content);
|
|
||||||
|
|
||||||
$traverser = new NodeTraverser;
|
|
||||||
$traverser->addVisitor(new NameResolver);
|
|
||||||
$traverser->addVisitor(new ReferencesAdder($document));
|
|
||||||
$definitionCollector = new DefinitionCollector($definitionResolver);
|
|
||||||
$traverser->addVisitor($definitionCollector);
|
|
||||||
$traverser->traverse($stmts);
|
|
||||||
|
|
||||||
$defNodes = $definitionCollector->nodes;
|
|
||||||
|
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
'TestNamespace',
|
'TestNamespace',
|
||||||
|
@ -55,46 +37,48 @@ class DefinitionCollectorTest extends TestCase
|
||||||
'TestNamespace\\Example->__construct()',
|
'TestNamespace\\Example->__construct()',
|
||||||
'TestNamespace\\Example->__destruct()'
|
'TestNamespace\\Example->__destruct()'
|
||||||
], array_keys($defNodes));
|
], array_keys($defNodes));
|
||||||
$this->assertInstanceOf(Node\Const_::class, $defNodes['TestNamespace\\TEST_CONST']);
|
|
||||||
$this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\TestClass']);
|
$this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TEST_CONST']);
|
||||||
$this->assertInstanceOf(Node\Const_::class, $defNodes['TestNamespace\\TestClass::TEST_CLASS_CONST']);
|
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\TestClass']);
|
||||||
$this->assertInstanceOf(Node\Stmt\PropertyProperty::class, $defNodes['TestNamespace\\TestClass::$staticTestProperty']);
|
$this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TestClass::TEST_CLASS_CONST']);
|
||||||
$this->assertInstanceOf(Node\Stmt\PropertyProperty::class, $defNodes['TestNamespace\\TestClass->testProperty']);
|
// TODO - should we parse properties more strictly?
|
||||||
$this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\TestClass::staticTestMethod()']);
|
$this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass::$staticTestProperty']);
|
||||||
$this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\TestClass->testMethod()']);
|
$this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass->testProperty']);
|
||||||
$this->assertInstanceOf(Node\Stmt\Trait_::class, $defNodes['TestNamespace\\TestTrait']);
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass::staticTestMethod()']);
|
||||||
$this->assertInstanceOf(Node\Stmt\Interface_::class, $defNodes['TestNamespace\\TestInterface']);
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass->testMethod()']);
|
||||||
$this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\test_function()']);
|
$this->assertInstanceOf(Node\Statement\TraitDeclaration::class, $defNodes['TestNamespace\\TestTrait']);
|
||||||
$this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\ChildClass']);
|
$this->assertInstanceOf(Node\Statement\InterfaceDeclaration::class, $defNodes['TestNamespace\\TestInterface']);
|
||||||
$this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\Example']);
|
$this->assertInstanceOf(Node\Statement\FunctionDeclaration::class, $defNodes['TestNamespace\\test_function()']);
|
||||||
$this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__construct()']);
|
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\ChildClass']);
|
||||||
$this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__destruct()']);
|
$this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\Example']);
|
||||||
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__construct()']);
|
||||||
|
$this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\Example->__destruct()']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDoesNotCollectReferences()
|
public function testDoesNotCollectReferences()
|
||||||
{
|
{
|
||||||
$path = realpath(__DIR__ . '/../../fixtures/references.php');
|
$path = realpath(__DIR__ . '/../../fixtures/references.php');
|
||||||
|
$defNodes = $this->collectDefinitions($path);
|
||||||
|
|
||||||
|
$this->assertEquals(['TestNamespace', 'TestNamespace\\whatever()'], array_keys($defNodes));
|
||||||
|
$this->assertInstanceOf(Node\Statement\NamespaceDefinition::class, $defNodes['TestNamespace']);
|
||||||
|
$this->assertInstanceOf(Node\Statement\FunctionDeclaration::class, $defNodes['TestNamespace\\whatever()']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $path
|
||||||
|
*/
|
||||||
|
private function collectDefinitions(string $path): array
|
||||||
|
{
|
||||||
$uri = pathToUri($path);
|
$uri = pathToUri($path);
|
||||||
$parser = new 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);
|
||||||
$content = file_get_contents($path);
|
$content = file_get_contents($path);
|
||||||
$document = new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver);
|
|
||||||
$stmts = $parser->parse($content);
|
|
||||||
|
|
||||||
$traverser = new NodeTraverser;
|
$treeAnalyzer = new TreeAnalyzer($parser, $content, $docBlockFactory, $definitionResolver, $uri);
|
||||||
$traverser->addVisitor(new NameResolver);
|
return $treeAnalyzer->getDefinitionNodes();
|
||||||
$traverser->addVisitor(new ReferencesAdder($document));
|
|
||||||
$definitionCollector = new DefinitionCollector($definitionResolver);
|
|
||||||
$traverser->addVisitor($definitionCollector);
|
|
||||||
$traverser->traverse($stmts);
|
|
||||||
|
|
||||||
$defNodes = $definitionCollector->nodes;
|
|
||||||
|
|
||||||
$this->assertEquals(['TestNamespace', 'TestNamespace\\whatever()'], array_keys($defNodes));
|
|
||||||
$this->assertInstanceOf(Node\Name::class, $defNodes['TestNamespace']);
|
|
||||||
$this->assertInstanceOf(Node\Stmt\Namespace_::class, $defNodes['TestNamespace']->getAttribute('parentNode'));
|
|
||||||
$this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\whatever()']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,14 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer\Tests\Server;
|
namespace LanguageServer\Tests\Server;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use LanguageServer\{
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
PhpDocument, PhpDocumentLoader, Project, DefinitionResolver
|
||||||
use LanguageServer\{Server, Client, LanguageClient, Project, PhpDocument, PhpDocumentLoader, DefinitionResolver};
|
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
|
||||||
use LanguageServer\Protocol\{
|
|
||||||
TextDocumentItem,
|
|
||||||
TextDocumentIdentifier,
|
|
||||||
SymbolKind,
|
|
||||||
DiagnosticSeverity,
|
|
||||||
FormattingOptions,
|
|
||||||
ClientCapabilities
|
|
||||||
};
|
};
|
||||||
use AdvancedJsonRpc\{Request as RequestBody, Response as ResponseBody};
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
|
use LanguageServer\Index\{
|
||||||
|
DependenciesIndex, Index, ProjectIndex
|
||||||
|
};
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
use function LanguageServer\pathToUri;
|
use function LanguageServer\pathToUri;
|
||||||
|
|
||||||
class PhpDocumentLoaderTest extends TestCase
|
class PhpDocumentLoaderTest extends TestCase
|
||||||
|
|
|
@ -3,22 +3,26 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer\Tests\Server;
|
namespace LanguageServer\Tests\Server;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use LanguageServer\{
|
||||||
|
PhpDocument, DefinitionResolver
|
||||||
|
};
|
||||||
|
use LanguageServer\Index\{
|
||||||
|
Index
|
||||||
|
};
|
||||||
|
use LanguageServer\Protocol\{
|
||||||
|
Position
|
||||||
|
};
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
use Microsoft\PhpParser\Node;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\{LanguageClient, PhpDocument, DefinitionResolver, Parser};
|
|
||||||
use LanguageServer\NodeVisitor\NodeAtPositionFinder;
|
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
|
||||||
use LanguageServer\Protocol\{SymbolKind, Position, ClientCapabilities};
|
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
|
||||||
use PhpParser\Node;
|
|
||||||
use function LanguageServer\isVendored;
|
use function LanguageServer\isVendored;
|
||||||
|
|
||||||
class PhpDocumentTest extends TestCase
|
class PhpDocumentTest extends TestCase
|
||||||
{
|
{
|
||||||
public function createDocument(string $uri, string $content)
|
public function createDocument(string $uri, string $content)
|
||||||
{
|
{
|
||||||
$parser = new 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);
|
||||||
|
@ -36,10 +40,15 @@ class PhpDocumentTest extends TestCase
|
||||||
{
|
{
|
||||||
$document = $this->createDocument('whatever', "<?php\n$\$a = new SomeClass;");
|
$document = $this->createDocument('whatever', "<?php\n$\$a = new SomeClass;");
|
||||||
$node = $document->getNodeAtPosition(new Position(1, 13));
|
$node = $document->getNodeAtPosition(new Position(1, 13));
|
||||||
$this->assertInstanceOf(Node\Name\FullyQualified::class, $node);
|
$this->assertQualifiedName($node);
|
||||||
$this->assertEquals('SomeClass', (string)$node);
|
$this->assertEquals('SomeClass', (string)$node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertQualifiedName($node)
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf(Node\QualifiedName::class, $node);
|
||||||
|
}
|
||||||
|
|
||||||
public function testIsVendored()
|
public function testIsVendored()
|
||||||
{
|
{
|
||||||
$document = $this->createDocument('file:///dir/vendor/x.php', "<?php\n$\$a = new SomeClass;");
|
$document = $this->createDocument('file:///dir/vendor/x.php', "<?php\n$\$a = new SomeClass;");
|
||||||
|
|
|
@ -5,12 +5,13 @@ namespace LanguageServer\Tests\Server;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
|
use LanguageServer\{
|
||||||
use LanguageServer\Index\{ProjectIndex, StubsIndex, GlobalIndex, DependenciesIndex, Index};
|
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
|
||||||
|
};
|
||||||
|
use LanguageServer\Index\{ProjectIndex, DependenciesIndex, Index};
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Protocol\{Position, Location, Range, ClientCapabilities};
|
use LanguageServer\Protocol\{Position, Location, Range};
|
||||||
use function LanguageServer\pathToUri;
|
use function LanguageServer\pathToUri;
|
||||||
use Sabre\Event\Promise;
|
|
||||||
|
|
||||||
abstract class ServerTestCase extends TestCase
|
abstract class ServerTestCase extends TestCase
|
||||||
{
|
{
|
||||||
|
@ -87,13 +88,13 @@ abstract class ServerTestCase extends TestCase
|
||||||
'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))),
|
'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))),
|
||||||
|
|
||||||
// Namespaced
|
// Namespaced
|
||||||
'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))),
|
'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 0), new Position( 2, 24))),
|
||||||
'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 10), new Position( 2, 29))),
|
'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 0), new Position( 2, 30))),
|
||||||
'TestNamespace\\TEST_CONST' => new Location($symbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))),
|
'TestNamespace\\TEST_CONST' => new Location($symbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))),
|
||||||
'TestNamespace\\TestClass' => new Location($symbolsUri, new Range(new Position(20, 0), new Position(61, 1))),
|
'TestNamespace\\TestClass' => new Location($symbolsUri, new Range(new Position(20, 0), new Position(61, 1))),
|
||||||
'TestNamespace\\ChildClass' => new Location($symbolsUri, new Range(new Position(99, 0), new Position(99, 37))),
|
'TestNamespace\\ChildClass' => new Location($symbolsUri, new Range(new Position(99, 0), new Position(99, 37))),
|
||||||
'TestNamespace\\TestTrait' => new Location($symbolsUri, new Range(new Position(63, 0), new Position(66, 1))),
|
'TestNamespace\\TestTrait' => new Location($symbolsUri, new Range(new Position(63, 0), new Position(66, 1))),
|
||||||
'TestNamespace\\TestInterface' => new Location($symbolsUri, new Range(new Position(68, 0), new Position(71, 1))),
|
'TestNamespace\\TestInterface' => new Location($symbolsUri, new Range(new Position(68, 0), new Position(71, 1))),
|
||||||
'TestNamespace\\TestClass::TEST_CLASS_CONST' => new Location($symbolsUri, new Range(new Position(27, 10), new Position(27, 32))),
|
'TestNamespace\\TestClass::TEST_CLASS_CONST' => new Location($symbolsUri, new Range(new Position(27, 10), new Position(27, 32))),
|
||||||
'TestNamespace\\TestClass::testProperty' => new Location($symbolsUri, new Range(new Position(41, 11), new Position(41, 24))),
|
'TestNamespace\\TestClass::testProperty' => new Location($symbolsUri, new Range(new Position(41, 11), new Position(41, 24))),
|
||||||
'TestNamespace\\TestClass::staticTestProperty' => new Location($symbolsUri, new Range(new Position(34, 18), new Position(34, 37))),
|
'TestNamespace\\TestClass::staticTestProperty' => new Location($symbolsUri, new Range(new Position(34, 18), new Position(34, 37))),
|
||||||
|
@ -112,7 +113,7 @@ abstract class ServerTestCase extends TestCase
|
||||||
'TestNamespace' => [
|
'TestNamespace' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))), // use function TestNamespace\test_function;
|
0 => new Location($referencesUri, new Range(new Position(31, 13), new Position(31, 40))), // use function TestNamespace\test_function;
|
||||||
1 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass;
|
1 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass;
|
||||||
2 => new Location($useUri, new Range(new Position( 5, 4), new Position( 5, 17))) // use TestNamespace\{TestTrait, TestInterface};
|
2 => new Location($useUri, new Range(new Position( 5, 4), new Position( 5, 18))) // use TestNamespace\{TestTrait, TestInterface};
|
||||||
],
|
],
|
||||||
'TestNamespace\\TEST_CONST' => [
|
'TestNamespace\\TEST_CONST' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15)))
|
||||||
|
@ -147,16 +148,16 @@ abstract class ServerTestCase extends TestCase
|
||||||
3 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty;
|
3 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||||
],
|
],
|
||||||
'TestNamespace\\TestClass::staticTestProperty' => [
|
'TestNamespace\\TestClass::staticTestProperty' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 35))), // echo TestClass::$staticTestProperty;
|
0 => new Location($referencesUri, new Range(new Position( 8, 16), new Position( 8, 35))), // echo TestClass::$staticTestProperty;
|
||||||
1 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty;
|
1 => new Location($referencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||||
],
|
],
|
||||||
'TestNamespace\\TestClass::staticTestMethod()' => [
|
'TestNamespace\\TestClass::staticTestMethod()' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 29)))
|
0 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 27)))
|
||||||
],
|
],
|
||||||
'TestNamespace\\TestClass::testMethod()' => [
|
'TestNamespace\\TestClass::testMethod()' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position( 5, 0), new Position( 5, 18))), // $obj->testMethod();
|
0 => new Location($referencesUri, new Range(new Position( 5, 0), new Position( 5, 16))), // $obj->testMethod();
|
||||||
1 => new Location($referencesUri, new Range(new Position(38, 0), new Position(38, 32))), // $obj->testProperty->testMethod();
|
1 => new Location($referencesUri, new Range(new Position(38, 0), new Position(38, 30))), // $obj->testProperty->testMethod();
|
||||||
2 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))) // $child->testMethod();
|
2 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod();
|
||||||
],
|
],
|
||||||
'TestNamespace\\test_function()' => [
|
'TestNamespace\\test_function()' => [
|
||||||
0 => new Location($referencesUri, new Range(new Position(10, 0), new Position(10, 13))),
|
0 => new Location($referencesUri, new Range(new Position(10, 0), new Position(10, 13))),
|
||||||
|
@ -186,7 +187,7 @@ abstract class ServerTestCase extends TestCase
|
||||||
],
|
],
|
||||||
'TestInterface' => [
|
'TestInterface' => [
|
||||||
0 => new Location($globalSymbolsUri, new Range(new Position(20, 27), new Position(20, 40))), // class TestClass implements TestInterface
|
0 => new Location($globalSymbolsUri, new Range(new Position(20, 27), new Position(20, 40))), // class TestClass implements TestInterface
|
||||||
1 => new Location($globalSymbolsUri, new Range(new Position(57, 48), new Position(57, 61))), // public function testMethod($testParameter): TestInterface
|
1 => new Location($globalSymbolsUri, new Range(new Position(57, 49), new Position(57, 61))), // public function testMethod($testParameter) : TestInterface
|
||||||
2 => new Location($globalReferencesUri, new Range(new Position(33, 20), new Position(33, 33))) // if ($abc instanceof TestInterface)
|
2 => new Location($globalReferencesUri, new Range(new Position(33, 20), new Position(33, 33))) // if ($abc instanceof TestInterface)
|
||||||
],
|
],
|
||||||
'TestClass::TEST_CLASS_CONST' => [
|
'TestClass::TEST_CLASS_CONST' => [
|
||||||
|
@ -200,16 +201,16 @@ abstract class ServerTestCase extends TestCase
|
||||||
3 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty;
|
3 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 49))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||||
],
|
],
|
||||||
'TestClass::staticTestProperty' => [
|
'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;
|
1 => new Location($globalReferencesUri, new Range(new Position(39, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty;
|
||||||
],
|
],
|
||||||
'TestClass::staticTestMethod()' => [
|
'TestClass::staticTestMethod()' => [
|
||||||
0 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 29)))
|
0 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 27)))
|
||||||
],
|
],
|
||||||
'TestClass::testMethod()' => [
|
'TestClass::testMethod()' => [
|
||||||
0 => new Location($globalReferencesUri, new Range(new Position( 5, 0), new Position( 5, 18))), // $obj->testMethod();
|
0 => new Location($globalReferencesUri, new Range(new Position( 5, 0), new Position( 5, 16))), // $obj->testMethod();
|
||||||
1 => new Location($globalReferencesUri, new Range(new Position(38, 0), new Position(38, 32))), // $obj->testProperty->testMethod();
|
1 => new Location($globalReferencesUri, new Range(new Position(38, 0), new Position(38, 30))), // $obj->testProperty->testMethod();
|
||||||
2 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))) // $child->testMethod();
|
2 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod();
|
||||||
],
|
],
|
||||||
'test_function()' => [
|
'test_function()' => [
|
||||||
0 => new Location($globalReferencesUri, new Range(new Position(10, 0), new Position(10, 13))),
|
0 => new Location($globalReferencesUri, new Range(new Position(10, 0), new Position(10, 13))),
|
||||||
|
|
|
@ -5,15 +5,16 @@ namespace LanguageServer\Tests\Server\TextDocument;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, CompletionProvider, DefinitionResolver};
|
use LanguageServer\{
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex, GlobalIndex, StubsIndex};
|
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
|
||||||
|
};
|
||||||
|
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Protocol\{
|
use LanguageServer\Protocol\{
|
||||||
TextDocumentIdentifier,
|
TextDocumentIdentifier,
|
||||||
TextEdit,
|
TextEdit,
|
||||||
Range,
|
Range,
|
||||||
Position,
|
Position,
|
||||||
ClientCapabilities,
|
|
||||||
CompletionList,
|
CompletionList,
|
||||||
CompletionItem,
|
CompletionItem,
|
||||||
CompletionItemKind
|
CompletionItemKind
|
||||||
|
@ -52,7 +53,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(3, 7)
|
new Position(3, 7)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'testProperty',
|
'testProperty',
|
||||||
CompletionItemKind::PROPERTY,
|
CompletionItemKind::PROPERTY,
|
||||||
|
@ -76,7 +77,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(3, 6)
|
new Position(3, 6)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'testProperty',
|
'testProperty',
|
||||||
CompletionItemKind::PROPERTY,
|
CompletionItemKind::PROPERTY,
|
||||||
|
@ -100,7 +101,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(8, 5)
|
new Position(8, 5)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'$var',
|
'$var',
|
||||||
CompletionItemKind::VARIABLE,
|
CompletionItemKind::VARIABLE,
|
||||||
|
@ -132,7 +133,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(8, 6)
|
new Position(8, 6)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'$param',
|
'$param',
|
||||||
CompletionItemKind::VARIABLE,
|
CompletionItemKind::VARIABLE,
|
||||||
|
@ -154,7 +155,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(6, 10)
|
new Position(6, 10)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
// Global TestClass definition (inserted as \TestClass)
|
// Global TestClass definition (inserted as \TestClass)
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'TestClass',
|
'TestClass',
|
||||||
|
@ -223,17 +224,20 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(6, 5)
|
new Position(6, 5)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'TestClass',
|
'TestClass',
|
||||||
CompletionItemKind::CLASS_,
|
CompletionItemKind::CLASS_,
|
||||||
'TestNamespace',
|
'TestNamespace',
|
||||||
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
||||||
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
||||||
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
||||||
'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" .
|
'veniam officia deserunt et non dolore mollit ea quis eiusmod sit non. Occaecat' . "\n" .
|
||||||
'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" .
|
'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" .
|
||||||
'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.'
|
'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'TestClass'
|
||||||
)
|
)
|
||||||
], true), $items);
|
], true), $items);
|
||||||
}
|
}
|
||||||
|
@ -246,7 +250,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(2, 14)
|
new Position(2, 14)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'staticTestProperty',
|
'staticTestProperty',
|
||||||
CompletionItemKind::PROPERTY,
|
CompletionItemKind::PROPERTY,
|
||||||
|
@ -267,7 +271,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(2, 11)
|
new Position(2, 11)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'TEST_CLASS_CONST',
|
'TEST_CLASS_CONST',
|
||||||
CompletionItemKind::VARIABLE,
|
CompletionItemKind::VARIABLE,
|
||||||
|
@ -300,7 +304,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(2, 13)
|
new Position(2, 13)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'TEST_CLASS_CONST',
|
'TEST_CLASS_CONST',
|
||||||
CompletionItemKind::VARIABLE,
|
CompletionItemKind::VARIABLE,
|
||||||
|
@ -333,7 +337,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(2, 13)
|
new Position(2, 13)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'TEST_CLASS_CONST',
|
'TEST_CLASS_CONST',
|
||||||
CompletionItemKind::VARIABLE,
|
CompletionItemKind::VARIABLE,
|
||||||
|
@ -366,7 +370,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(6, 6)
|
new Position(6, 6)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'TestClass',
|
'TestClass',
|
||||||
CompletionItemKind::CLASS_,
|
CompletionItemKind::CLASS_,
|
||||||
|
@ -392,7 +396,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(2, 1)
|
new Position(2, 1)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class '),
|
new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class '),
|
||||||
new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone ')
|
new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone ')
|
||||||
], true), $items);
|
], true), $items);
|
||||||
|
@ -406,7 +410,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(0, 0)
|
new Position(0, 0)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'<?php',
|
'<?php',
|
||||||
CompletionItemKind::KEYWORD,
|
CompletionItemKind::KEYWORD,
|
||||||
|
@ -428,7 +432,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(0, 1)
|
new Position(0, 1)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'<?php',
|
'<?php',
|
||||||
CompletionItemKind::KEYWORD,
|
CompletionItemKind::KEYWORD,
|
||||||
|
@ -450,7 +454,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(4, 6)
|
new Position(4, 6)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'SomeNamespace',
|
'SomeNamespace',
|
||||||
CompletionItemKind::MODULE,
|
CompletionItemKind::MODULE,
|
||||||
|
@ -471,7 +475,7 @@ class CompletionTest extends TestCase
|
||||||
new TextDocumentIdentifier($completionUri),
|
new TextDocumentIdentifier($completionUri),
|
||||||
new Position(4, 8)
|
new Position(4, 8)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new CompletionList([
|
$this->assertCompletionsListSubset(new CompletionList([
|
||||||
new CompletionItem(
|
new CompletionItem(
|
||||||
'$abc2',
|
'$abc2',
|
||||||
CompletionItemKind::VARIABLE,
|
CompletionItemKind::VARIABLE,
|
||||||
|
@ -494,4 +498,13 @@ class CompletionTest extends TestCase
|
||||||
)
|
)
|
||||||
], true), $items);
|
], true), $items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertCompletionsListSubset(CompletionList $subsetList, CompletionList $list)
|
||||||
|
{
|
||||||
|
foreach ($subsetList->items as $expectedItem) {
|
||||||
|
$this->assertContains($expectedItem, $list->items, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEquals($subsetList->isIncomplete, $list->isIncomplete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,12 @@ namespace LanguageServer\Tests\Server\TextDocument\Definition;
|
||||||
|
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\Tests\Server\ServerTestCase;
|
use LanguageServer\Tests\Server\ServerTestCase;
|
||||||
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
|
use LanguageServer\{
|
||||||
|
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
|
||||||
|
};
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Protocol\{TextDocumentIdentifier, Position, Range, Location, ClientCapabilities};
|
use LanguageServer\Protocol\{TextDocumentIdentifier, Position, Range, Location};
|
||||||
use Sabre\Event\Promise;
|
|
||||||
|
|
||||||
class GlobalFallbackTest extends ServerTestCase
|
class GlobalFallbackTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,7 +24,7 @@ class GlobalTest extends ServerTestCase
|
||||||
// namespace keyword
|
// namespace keyword
|
||||||
$result = $this->textDocument->definition(
|
$result = $this->textDocument->definition(
|
||||||
new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))),
|
new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))),
|
||||||
new Position(2, 4)
|
new Position(1, 0)
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals([], $result);
|
$this->assertEquals([], $result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,16 @@ namespace LanguageServer\Tests\Server\TextDocument;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\{Server, Client, LanguageClient, PhpDocumentLoader, DefinitionResolver};
|
use LanguageServer\{
|
||||||
|
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
|
||||||
|
};
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
||||||
use LanguageServer\Protocol\{
|
use LanguageServer\Protocol\{
|
||||||
TextDocumentIdentifier,
|
|
||||||
TextDocumentItem,
|
|
||||||
VersionedTextDocumentIdentifier,
|
VersionedTextDocumentIdentifier,
|
||||||
TextDocumentContentChangeEvent,
|
TextDocumentContentChangeEvent,
|
||||||
Range,
|
Range,
|
||||||
Position,
|
Position
|
||||||
ClientCapabilities
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DidChangeTest extends TestCase
|
class DidChangeTest extends TestCase
|
||||||
|
|
|
@ -5,11 +5,12 @@ namespace LanguageServer\Tests\Server\TextDocument;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\{Server, Client, LanguageClient, PhpDocumentLoader, DefinitionResolver};
|
use LanguageServer\{
|
||||||
|
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
|
||||||
|
};
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
||||||
use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier, ClientCapabilities};
|
use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier};
|
||||||
use Exception;
|
|
||||||
|
|
||||||
class DidCloseTest extends TestCase
|
class DidCloseTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,14 +5,15 @@ namespace LanguageServer\Tests\Server\TextDocument;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\{Server, Client, LanguageClient, PhpDocumentLoader, DefinitionResolver};
|
use LanguageServer\{
|
||||||
|
Server, LanguageClient, PhpDocumentLoader, DefinitionResolver
|
||||||
|
};
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Protocol\{
|
use LanguageServer\Protocol\{
|
||||||
TextDocumentIdentifier,
|
TextDocumentIdentifier,
|
||||||
TextDocumentItem,
|
TextDocumentItem,
|
||||||
FormattingOptions,
|
FormattingOptions,
|
||||||
ClientCapabilities,
|
|
||||||
TextEdit,
|
TextEdit,
|
||||||
Range,
|
Range,
|
||||||
Position
|
Position
|
||||||
|
|
|
@ -21,7 +21,7 @@ class HoverTest extends ServerTestCase
|
||||||
$reference->range->start
|
$reference->range->start
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new Hover([
|
$this->assertEquals(new Hover([
|
||||||
new MarkedString('php', "<?php\nclass TestClass implements \\TestInterface"),
|
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
|
||||||
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
||||||
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
||||||
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
||||||
|
@ -41,7 +41,7 @@ class HoverTest extends ServerTestCase
|
||||||
$definition->range->start
|
$definition->range->start
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new Hover([
|
$this->assertEquals(new Hover([
|
||||||
new MarkedString('php', "<?php\nclass TestClass implements \\TestInterface"),
|
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
|
||||||
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
||||||
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
||||||
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
||||||
|
@ -61,7 +61,7 @@ class HoverTest extends ServerTestCase
|
||||||
$reference->range->end
|
$reference->range->end
|
||||||
)->wait();
|
)->wait();
|
||||||
$this->assertEquals(new Hover([
|
$this->assertEquals(new Hover([
|
||||||
new MarkedString('php', "<?php\npublic function testMethod(\$testParameter) : \TestInterface"),
|
new MarkedString('php', "<?php\npublic function testMethod(\$testParameter): TestInterface"),
|
||||||
'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.'
|
'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.'
|
||||||
], $reference->range), $result);
|
], $reference->range), $result);
|
||||||
}
|
}
|
||||||
|
@ -165,8 +165,9 @@ class HoverTest extends ServerTestCase
|
||||||
new TextDocumentIdentifier($reference->uri),
|
new TextDocumentIdentifier($reference->uri),
|
||||||
$reference->range->end
|
$reference->range->end
|
||||||
)->wait();
|
)->wait();
|
||||||
|
// TODO - should pretty print with fqns, like \define, \false. Not yet supported by tolerant-php-parser
|
||||||
$this->assertEquals(new Hover([
|
$this->assertEquals(new Hover([
|
||||||
new MarkedString('php', "<?php\n\\define('TEST_DEFINE_CONSTANT', \\false);"),
|
new MarkedString('php', "<?php\ndefine('TEST_DEFINE_CONSTANT', false)"),
|
||||||
'Lorem ipsum dolor sit amet, consectetur.'
|
'Lorem ipsum dolor sit amet, consectetur.'
|
||||||
], $reference->range), $result);
|
], $reference->range), $result);
|
||||||
}
|
}
|
||||||
|
@ -178,7 +179,7 @@ class HoverTest extends ServerTestCase
|
||||||
$uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
|
$uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
|
||||||
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(13, 7))->wait();
|
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(13, 7))->wait();
|
||||||
$this->assertEquals(new Hover(
|
$this->assertEquals(new Hover(
|
||||||
[new MarkedString('php', "<?php\n\$var = 123;")],
|
[new MarkedString('php', "<?php\n\$var = 123")],
|
||||||
new Range(new Position(13, 5), new Position(13, 9))
|
new Range(new Position(13, 5), new Position(13, 9))
|
||||||
), $result);
|
), $result);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +192,7 @@ class HoverTest extends ServerTestCase
|
||||||
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(22, 11))->wait();
|
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(22, 11))->wait();
|
||||||
$this->assertEquals(new Hover(
|
$this->assertEquals(new Hover(
|
||||||
[
|
[
|
||||||
new MarkedString('php', "<?php\n\TestNamespace\TestClass \$param"),
|
new MarkedString('php', "<?php\nTestClass \$param"),
|
||||||
'Adipisicing non non cillum sint incididunt cillum enim mollit.'
|
'Adipisicing non non cillum sint incididunt cillum enim mollit.'
|
||||||
],
|
],
|
||||||
new Range(new Position(22, 9), new Position(22, 15))
|
new Range(new Position(22, 9), new Position(22, 15))
|
||||||
|
@ -205,7 +206,7 @@ class HoverTest extends ServerTestCase
|
||||||
$uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/global_symbols.php'));
|
$uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/global_symbols.php'));
|
||||||
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(59, 11))->wait();
|
$result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(59, 11))->wait();
|
||||||
$this->assertEquals(new Hover([
|
$this->assertEquals(new Hover([
|
||||||
new MarkedString('php', "<?php\nclass TestClass implements \\TestInterface"),
|
new MarkedString('php', "<?php\nclass TestClass implements TestInterface"),
|
||||||
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\n" .
|
||||||
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
'Deserunt enim minim sunt sint ea nisi. Deserunt excepteur tempor id nostrud' . "\n" .
|
||||||
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
'laboris commodo ad commodo velit mollit qui non officia id. Nulla duis veniam' . "\n" .
|
||||||
|
|
|
@ -5,10 +5,12 @@ namespace LanguageServer\Tests\Server\TextDocument;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\{Server, Client, LanguageClient, ClientHandler, PhpDocumentLoader, DefinitionResolver};
|
use LanguageServer\{
|
||||||
|
Server, Client, LanguageClient, ClientHandler, PhpDocumentLoader, DefinitionResolver
|
||||||
|
};
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Protocol\{TextDocumentIdentifier, TextDocumentItem, DiagnosticSeverity, ClientCapabilities};
|
use LanguageServer\Protocol\{TextDocumentItem, DiagnosticSeverity};
|
||||||
use Sabre\Event\Promise;
|
use Sabre\Event\Promise;
|
||||||
use JsonMapper;
|
use JsonMapper;
|
||||||
|
|
||||||
|
@ -62,7 +64,55 @@ class ParseErrorsTest extends TestCase
|
||||||
'range' => [
|
'range' => [
|
||||||
'start' => [
|
'start' => [
|
||||||
'line' => 2,
|
'line' => 2,
|
||||||
'character' => 10
|
'character' => 9
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 2,
|
||||||
|
'character' => 9
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'severity' => DiagnosticSeverity::ERROR,
|
||||||
|
'code' => null,
|
||||||
|
'source' => 'php',
|
||||||
|
'message' => "'Name' expected."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 2,
|
||||||
|
'character' => 9
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 2,
|
||||||
|
'character' => 9
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'severity' => DiagnosticSeverity::ERROR,
|
||||||
|
'code' => null,
|
||||||
|
'source' => 'php',
|
||||||
|
'message' => "'{' expected."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 2,
|
||||||
|
'character' => 9
|
||||||
|
],
|
||||||
|
'end' => [
|
||||||
|
'line' => 2,
|
||||||
|
'character' => 9
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'severity' => DiagnosticSeverity::ERROR,
|
||||||
|
'code' => null,
|
||||||
|
'source' => 'php',
|
||||||
|
'message' => "'}' expected."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'range' => [
|
||||||
|
'start' => [
|
||||||
|
'line' => 2,
|
||||||
|
'character' => 15
|
||||||
],
|
],
|
||||||
'end' => [
|
'end' => [
|
||||||
'line' => 2,
|
'line' => 2,
|
||||||
|
@ -72,13 +122,14 @@ class ParseErrorsTest extends TestCase
|
||||||
'severity' => DiagnosticSeverity::ERROR,
|
'severity' => DiagnosticSeverity::ERROR,
|
||||||
'code' => null,
|
'code' => null,
|
||||||
'source' => 'php',
|
'source' => 'php',
|
||||||
'message' => "Syntax error, unexpected T_CLASS, expecting T_STRING"
|
'message' => "'Name' expected."
|
||||||
]]
|
]]
|
||||||
], json_decode(json_encode($this->args), true));
|
], json_decode(json_encode($this->args), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testParseErrorsWithOnlyStartLine()
|
public function testParseErrorsWithOnlyStartLine()
|
||||||
{
|
{
|
||||||
|
$this->markTestIncomplete('This diagnostic not yet implemented in tolerant-php-parser');
|
||||||
$this->openFile(__DIR__ . '/../../../fixtures/namespace_not_first.php');
|
$this->openFile(__DIR__ . '/../../../fixtures/namespace_not_first.php');
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
'whatever',
|
'whatever',
|
||||||
|
|
|
@ -3,12 +3,17 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
namespace LanguageServer\Tests\Server\TextDocument\References;
|
namespace LanguageServer\Tests\Server\TextDocument\References;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use LanguageServer\{
|
||||||
use LanguageServer\Tests\MockProtocolStream;
|
LanguageClient, PhpDocumentLoader, Server, DefinitionResolver
|
||||||
use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver};
|
};
|
||||||
use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex};
|
|
||||||
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
|
||||||
use LanguageServer\Protocol\{TextDocumentIdentifier, Position, ReferenceContext, Location, Range, ClientCapabilities};
|
use LanguageServer\Index\{
|
||||||
|
DependenciesIndex, Index, ProjectIndex
|
||||||
|
};
|
||||||
|
use LanguageServer\Protocol\{
|
||||||
|
Location, Position, Range, ReferenceContext, TextDocumentIdentifier
|
||||||
|
};
|
||||||
|
use LanguageServer\Tests\MockProtocolStream;
|
||||||
use LanguageServer\Tests\Server\ServerTestCase;
|
use LanguageServer\Tests\Server\ServerTestCase;
|
||||||
|
|
||||||
class GlobalFallbackTest extends ServerTestCase
|
class GlobalFallbackTest extends ServerTestCase
|
||||||
|
|
|
@ -29,7 +29,7 @@ class SymbolTest extends ServerTestCase
|
||||||
$referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
|
$referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php'));
|
||||||
// @codingStandardsIgnoreStart
|
// @codingStandardsIgnoreStart
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 10), new Position(2, 23))), ''),
|
new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 0), new Position(2, 24))), ''),
|
||||||
// Namespaced
|
// Namespaced
|
||||||
new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'),
|
new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'),
|
||||||
new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'),
|
new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'),
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace LanguageServer\Tests;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use LanguageServer\Definition;
|
||||||
|
use LanguageServer\Index\Index;
|
||||||
|
use LanguageServer\PhpDocument;
|
||||||
|
use LanguageServer\DefinitionResolver;
|
||||||
|
use phpDocumentor\Reflection\DocBlock;
|
||||||
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use LanguageServer\ClientHandler;
|
||||||
|
use LanguageServer\Protocol\Message;
|
||||||
|
use AdvancedJsonRpc;
|
||||||
|
use RecursiveDirectoryIterator;
|
||||||
|
use RecursiveIteratorIterator;
|
||||||
|
use Sabre\Event\Loop;
|
||||||
|
use Microsoft\PhpParser;
|
||||||
|
|
||||||
|
class ValidationTest extends TestCase
|
||||||
|
{
|
||||||
|
public function validationTestProvider()
|
||||||
|
{
|
||||||
|
$testProviderArray = array();
|
||||||
|
$testCasesDir = realpath(__DIR__ . '/cases');
|
||||||
|
|
||||||
|
$iterator = new RecursiveDirectoryIterator($testCasesDir);
|
||||||
|
$disabled = json_decode(file_get_contents(__DIR__ . '/disabled.json'));
|
||||||
|
|
||||||
|
foreach (new RecursiveIteratorIterator($iterator) as $file) {
|
||||||
|
if (strpos(\strrev((string)$file), \strrev(".php")) === 0 && !\in_array(basename((string)$file), $disabled)) {
|
||||||
|
if ($file->getSize() < 100000) {
|
||||||
|
$testProviderArray[] = [$file->getPathname()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $testProviderArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test loads the test cases specified in .php files under cases/ and looks at the whole set of
|
||||||
|
* Definitions and References produced. It reads the expected results from associated .json files
|
||||||
|
* and compares to the actual result. If they don't match, the test fails and it writes the new baseline
|
||||||
|
* to the .json file.
|
||||||
|
* @group validation
|
||||||
|
* @dataProvider validationTestProvider
|
||||||
|
* @param $testCaseFile
|
||||||
|
*/
|
||||||
|
public function testDefinitionsAndReferences($testCaseFile)
|
||||||
|
{
|
||||||
|
$fileContents = file_get_contents($testCaseFile);
|
||||||
|
$actualValues = $this->getActualTestValues($testCaseFile, $fileContents);
|
||||||
|
|
||||||
|
$outputFile = getExpectedValuesFile($testCaseFile);
|
||||||
|
if (!file_exists($outputFile)) {
|
||||||
|
file_put_contents($outputFile, json_encode($actualValues, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
|
||||||
|
}
|
||||||
|
|
||||||
|
$expectedValues = (array)json_decode(file_get_contents($outputFile));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->assertEquals($expectedValues['definitions'], $actualValues['definitions']);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->assertArraySubset((array)$expectedValues['references'], (array)$actualValues['references'], false, 'references don\'t match.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->assertEquals((array)$expectedValues['references'], (array)$actualValues['references'], 'references don\'t match.');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$outputFile = getExpectedValuesFile($testCaseFile);
|
||||||
|
file_put_contents($outputFile, json_encode($actualValues, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getActualTestValues($filename, $fileContents): array
|
||||||
|
{
|
||||||
|
$index = new Index();
|
||||||
|
$parser = new PhpParser\Parser();
|
||||||
|
$docBlockFactory = DocBlockFactory::createInstance();
|
||||||
|
$definitionResolver = new DefinitionResolver($index);
|
||||||
|
|
||||||
|
$document = new PhpDocument($filename, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver);
|
||||||
|
|
||||||
|
$actualRefs = $index->getReferences();
|
||||||
|
$actualDefs = $this->getTestValuesFromDefs($document->getDefinitions());
|
||||||
|
|
||||||
|
// There's probably a more PHP-typical way to do this. Need to compare the objects parsed from json files
|
||||||
|
// to the real objects.
|
||||||
|
$refsAndDefs = array(
|
||||||
|
'references' => json_decode(json_encode($actualRefs)),
|
||||||
|
'definitions' => json_decode(json_encode($actualDefs))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Turn references into relative paths
|
||||||
|
$testCasesDir = realpath(__DIR__ . '/cases');
|
||||||
|
foreach ($refsAndDefs['references'] as $key => $list) {
|
||||||
|
$fixedPathRefs = array_map(function ($ref) use ($testCasesDir) {
|
||||||
|
return str_replace($testCasesDir, '.', $ref);
|
||||||
|
}, $list);
|
||||||
|
|
||||||
|
$refsAndDefs['references']->$key = $fixedPathRefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn def locations into relative paths
|
||||||
|
foreach ($refsAndDefs['definitions'] as $key => $def) {
|
||||||
|
if ($def !== null && $def->symbolInformation !== null &&
|
||||||
|
$def->symbolInformation->location !== null && $def->symbolInformation->location->uri !== null) {
|
||||||
|
$def->symbolInformation->location->uri = str_replace($testCasesDir, '.', $def->symbolInformation->location->uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $refsAndDefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $definitions Definition[]
|
||||||
|
* @return array|\array[]
|
||||||
|
*/
|
||||||
|
private function getTestValuesFromDefs($definitions): array
|
||||||
|
{
|
||||||
|
$propertyNames = get_class_vars(Definition::class);
|
||||||
|
|
||||||
|
$defsForAssert = [];
|
||||||
|
foreach ($definitions as $definition) {
|
||||||
|
$fqn = $definition->fqn;
|
||||||
|
|
||||||
|
foreach ($propertyNames as $propertyName => $value) {
|
||||||
|
if ($propertyName === 'symbolInformation') {
|
||||||
|
// Range is very often different - don't check it, for now
|
||||||
|
unset($definition->$propertyName->location->range);
|
||||||
|
} elseif ($propertyName === 'extends') {
|
||||||
|
$definition->$propertyName = $definition->$propertyName ?? [];
|
||||||
|
} elseif ($propertyName === 'type' && $definition->type !== null) {
|
||||||
|
// Class info is not captured by json_encode. It's important for 'type'.
|
||||||
|
$defsForAssert[$fqn]['type__class'] = get_class($definition->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
$defsForAssert[$fqn][$propertyName] = $definition->$propertyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $defsForAssert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExpectedValuesFile($testCaseFile): string
|
||||||
|
{
|
||||||
|
return $testCaseFile . '.expected.json';
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Fixtures\Prophecy;
|
||||||
|
|
||||||
|
class WithReturnTypehints extends EmptyClass
|
||||||
|
{
|
||||||
|
public function getSelf(): self {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string {
|
||||||
|
return __CLASS__;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent(): parent {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"Fixtures\\Prophecy\\EmptyClass": [
|
||||||
|
"./WithReturnTypehints.php"
|
||||||
|
],
|
||||||
|
"self": [
|
||||||
|
"./WithReturnTypehints.php"
|
||||||
|
],
|
||||||
|
"parent": [
|
||||||
|
"./WithReturnTypehints.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"Fixtures\\Prophecy": {
|
||||||
|
"fqn": "Fixtures\\Prophecy",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "Fixtures\\Prophecy",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./WithReturnTypehints.php"
|
||||||
|
},
|
||||||
|
"containerName": "Fixtures"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace Fixtures\\Prophecy;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"Fixtures\\Prophecy\\WithReturnTypehints": {
|
||||||
|
"fqn": "Fixtures\\Prophecy\\WithReturnTypehints",
|
||||||
|
"extends": [
|
||||||
|
"Fixtures\\Prophecy\\EmptyClass"
|
||||||
|
],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "WithReturnTypehints",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./WithReturnTypehints.php"
|
||||||
|
},
|
||||||
|
"containerName": "Fixtures\\Prophecy"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class WithReturnTypehints extends EmptyClass",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"Fixtures\\Prophecy\\WithReturnTypehints->getSelf()": {
|
||||||
|
"fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "getSelf",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./WithReturnTypehints.php"
|
||||||
|
},
|
||||||
|
"containerName": "Fixtures\\Prophecy\\WithReturnTypehints"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Object_",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public function getSelf(): self {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"Fixtures\\Prophecy\\WithReturnTypehints->getName()": {
|
||||||
|
"fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getName()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "getName",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./WithReturnTypehints.php"
|
||||||
|
},
|
||||||
|
"containerName": "Fixtures\\Prophecy\\WithReturnTypehints"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\String_",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public function getName(): string {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"Fixtures\\Prophecy\\WithReturnTypehints->getParent()": {
|
||||||
|
"fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getParent()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "getParent",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./WithReturnTypehints.php"
|
||||||
|
},
|
||||||
|
"containerName": "Fixtures\\Prophecy\\WithReturnTypehints"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Object_",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public function getParent(): parent {",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
$a = new class () {
|
||||||
|
public $a;
|
||||||
|
const HI = 3;
|
||||||
|
|
||||||
|
function b () {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./anonymousClassMembersShouldNotBeSymbols.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
protected $foo = ['hello' => TRUE];
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"A": {
|
||||||
|
"fqn": "A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./arrayValueShouldBeBoolean.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"A->foo": {
|
||||||
|
"fqn": "A->foo",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "foo",
|
||||||
|
"kind": 7,
|
||||||
|
"location": {
|
||||||
|
"uri": "./arrayValueShouldBeBoolean.php"
|
||||||
|
},
|
||||||
|
"containerName": "A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Array_",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "protected $foo;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
switch ($a) {
|
||||||
|
case A:
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\A": [
|
||||||
|
"./caseStatement1.php"
|
||||||
|
],
|
||||||
|
"A": [
|
||||||
|
"./caseStatement1.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./caseStatement1.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TestNamespace;
|
||||||
|
|
||||||
|
$a = new A;
|
||||||
|
|
||||||
|
echo $a->a;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public $a = 3;
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"TestNamespace\\A": [
|
||||||
|
"./classDefinition1.php"
|
||||||
|
],
|
||||||
|
"TestNamespace\\A->a": [
|
||||||
|
"./classDefinition1.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"TestNamespace": {
|
||||||
|
"fqn": "TestNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "TestNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./classDefinition1.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace TestNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"TestNamespace\\A": {
|
||||||
|
"fqn": "TestNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./classDefinition1.php"
|
||||||
|
},
|
||||||
|
"containerName": "TestNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"TestNamespace\\A->a": {
|
||||||
|
"fqn": "TestNamespace\\A->a",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "a",
|
||||||
|
"kind": 7,
|
||||||
|
"location": {
|
||||||
|
"uri": "./classDefinition1.php"
|
||||||
|
},
|
||||||
|
"containerName": "TestNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Integer",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public $a;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TestNamespace;
|
||||||
|
|
||||||
|
use SomeNamespace\Goo;
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
{
|
||||||
|
public $testProperty;
|
||||||
|
|
||||||
|
public function testMethod($testParameter)
|
||||||
|
{
|
||||||
|
$testVariable = 123;
|
||||||
|
|
||||||
|
if (empty($testParameter)) {
|
||||||
|
echo 'Empty';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"SomeNamespace\\Goo": [
|
||||||
|
"./classProperty1.php"
|
||||||
|
],
|
||||||
|
"SomeNamespace": [
|
||||||
|
"./classProperty1.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"TestNamespace": {
|
||||||
|
"fqn": "TestNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "TestNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./classProperty1.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace TestNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"TestNamespace\\TestClass": {
|
||||||
|
"fqn": "TestNamespace\\TestClass",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "TestClass",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./classProperty1.php"
|
||||||
|
},
|
||||||
|
"containerName": "TestNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class TestClass",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"TestNamespace\\TestClass->testProperty": {
|
||||||
|
"fqn": "TestNamespace\\TestClass->testProperty",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "testProperty",
|
||||||
|
"kind": 7,
|
||||||
|
"location": {
|
||||||
|
"uri": "./classProperty1.php"
|
||||||
|
},
|
||||||
|
"containerName": "TestNamespace\\TestClass"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public $testProperty;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"TestNamespace\\TestClass->testMethod()": {
|
||||||
|
"fqn": "TestNamespace\\TestClass->testMethod()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "testMethod",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./classProperty1.php"
|
||||||
|
},
|
||||||
|
"containerName": "TestNamespace\\TestClass"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public function testMethod($testParameter)",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public static function suite()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"hi" => BYE
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\BYE": [
|
||||||
|
"./constants.php"
|
||||||
|
],
|
||||||
|
"BYE": [
|
||||||
|
"./constants.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A::suite()": {
|
||||||
|
"fqn": "MyNamespace\\A::suite()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": true,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "suite",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public static function suite()",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public static function suite()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
BYE => "hi"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\BYE": [
|
||||||
|
"./constants2.php"
|
||||||
|
],
|
||||||
|
"BYE": [
|
||||||
|
"./constants2.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants2.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants2.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A::suite()": {
|
||||||
|
"fqn": "MyNamespace\\A::suite()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": true,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "suite",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants2.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public static function suite()",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public static function suite()
|
||||||
|
{
|
||||||
|
return array(T_NEW);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\T_NEW": [
|
||||||
|
"./constants3.php"
|
||||||
|
],
|
||||||
|
"T_NEW": [
|
||||||
|
"./constants3.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants3.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants3.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A::suite()": {
|
||||||
|
"fqn": "MyNamespace\\A::suite()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": true,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "suite",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants3.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public static function suite()",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public function suite()
|
||||||
|
{
|
||||||
|
return HI;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\HI": [
|
||||||
|
"./constants4.php"
|
||||||
|
],
|
||||||
|
"HI": [
|
||||||
|
"./constants4.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants4.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants4.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A->suite()": {
|
||||||
|
"fqn": "MyNamespace\\A->suite()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "suite",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants4.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public function suite()",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class Mbstring
|
||||||
|
{
|
||||||
|
const MB_CASE_FOLD = PHP_INT_MAX;
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\PHP_INT_MAX": [
|
||||||
|
"./constants5.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants5.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\Mbstring": {
|
||||||
|
"fqn": "MyNamespace\\Mbstring",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "Mbstring",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants5.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class Mbstring",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\Mbstring::MB_CASE_FOLD": {
|
||||||
|
"fqn": "MyNamespace\\Mbstring::MB_CASE_FOLD",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MB_CASE_FOLD",
|
||||||
|
"kind": 14,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constants5.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\Mbstring"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Object_",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "const MB_CASE_FOLD = PHP_INT_MAX;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
function b ($a = MY_CONSTANT);
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MY_CONSTANT": [
|
||||||
|
"./constantsInFunctionParamDefault.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"A": {
|
||||||
|
"fqn": "A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 11,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constantsInFunctionParamDefault.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "interface A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"A->b()": {
|
||||||
|
"fqn": "A->b()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "b",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./constantsInFunctionParamDefault.php"
|
||||||
|
},
|
||||||
|
"containerName": "A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "function b ($a = MY_CONSTANT);",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a file comment, not NS comment
|
||||||
|
*/
|
||||||
|
namespace MyNamespace;
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./docBlocksOnNamespaceDefinition.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./exceptions1.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class ForLoopReference1 {
|
||||||
|
public function getThat() {
|
||||||
|
for ($that = $this; null !== $that; $that = $that->foo()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function foo() {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"ForLoopReference1->foo()": [
|
||||||
|
"./_cases/forLoopReference1.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"ForLoopReference1": {
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "ForLoopReference1",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./_cases/forLoopReference1.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type__class": "LanguageServer\\Tests\\ValidationTest",
|
||||||
|
"type": null,
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"ForLoopReference1->getThat()": {
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "getThat",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./_cases/forLoopReference1.php"
|
||||||
|
},
|
||||||
|
"containerName": "ForLoopReference1"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"ForLoopReference1->foo()": {
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "foo",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./_cases/forLoopReference1.php"
|
||||||
|
},
|
||||||
|
"containerName": "ForLoopReference1"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$a = new A;
|
||||||
|
|
||||||
|
$b = function () use ($a) {
|
||||||
|
echo $a->b();
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"A": [
|
||||||
|
"./functionUse.php"
|
||||||
|
],
|
||||||
|
"A->b()": [
|
||||||
|
"./functionUse.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": []
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
use function LanguageServer\{pathToUri, timeout};
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"LanguageServer": [
|
||||||
|
"./functionUse2.php"
|
||||||
|
],
|
||||||
|
"LanguageServer\\timeout()": [
|
||||||
|
"./functionUse2.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": []
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
if (A) {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\A": [
|
||||||
|
"./ifStatement1.php"
|
||||||
|
],
|
||||||
|
"A": [
|
||||||
|
"./ifStatement1.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./ifStatement1.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
interface A {
|
||||||
|
// props ignored in interface
|
||||||
|
var $a = 3;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"A": {
|
||||||
|
"fqn": "A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 11,
|
||||||
|
"location": {
|
||||||
|
"uri": "./interfaceProperty.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "interface A {",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace B;
|
||||||
|
|
||||||
|
echo __FILE__;
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"B": {
|
||||||
|
"fqn": "B",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "B",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./magicConstantsShouldBeGlobal.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace B;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
private static $deprecationsTriggered = array(
|
||||||
|
__CLASS__ => true
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"A": {
|
||||||
|
"fqn": "A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./magicConsts.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"A::$deprecationsTriggered": {
|
||||||
|
"fqn": "A::$deprecationsTriggered",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": true,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "deprecationsTriggered",
|
||||||
|
"kind": 7,
|
||||||
|
"location": {
|
||||||
|
"uri": "./magicConsts.php"
|
||||||
|
},
|
||||||
|
"containerName": "A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Array_",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "private static $deprecationsTriggered;",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
static function a() {
|
||||||
|
$b = new a;
|
||||||
|
$c = $b->a();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\a": [
|
||||||
|
"./memberAccess1.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\a->a()": [
|
||||||
|
"./memberAccess1.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess1.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess1.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A::a()": {
|
||||||
|
"fqn": "MyNamespace\\A::a()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": true,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "a",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess1.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "static function a() {",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
static function a() {
|
||||||
|
$b = new a;
|
||||||
|
$c = $b->a();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\a": [
|
||||||
|
"./memberAccess2.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\a->a()": [
|
||||||
|
"./memberAccess2.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess2.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess2.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A::a()": {
|
||||||
|
"fqn": "MyNamespace\\A::a()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": true,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "a",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess2.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "static function a() {",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixesPsr0 = ComposerStaticInitIncludePath::$prefixesPsr0;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\ClassLoader": [
|
||||||
|
"./memberAccess3.php"
|
||||||
|
],
|
||||||
|
"Closure::bind()": [
|
||||||
|
"./memberAccess3.php"
|
||||||
|
],
|
||||||
|
"Closure": [
|
||||||
|
"./memberAccess3.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\ClassLoader->prefixesPsr0": [
|
||||||
|
"./memberAccess3.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\ComposerStaticInitIncludePath": [
|
||||||
|
"./memberAccess3.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\ComposerStaticInitIncludePath::$prefixesPsr0": [
|
||||||
|
"./memberAccess3.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\ClassLoader::class": [
|
||||||
|
"./memberAccess3.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess3.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess3.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A::getInitializer()": {
|
||||||
|
"fqn": "MyNamespace\\A::getInitializer()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": true,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "getInitializer",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess3.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public static function getInitializer(ClassLoader $loader)",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public function testRequest()
|
||||||
|
{
|
||||||
|
$request = Request::create((new Url('httpkernel_test.empty'))->toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"references": {
|
||||||
|
"MyNamespace\\Request::create()": [
|
||||||
|
"./memberAccess4.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\Request": [
|
||||||
|
"./memberAccess4.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\Url->toString()": [
|
||||||
|
"./memberAccess4.php"
|
||||||
|
],
|
||||||
|
"MyNamespace\\Url": [
|
||||||
|
"./memberAccess4.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess4.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A": {
|
||||||
|
"fqn": "MyNamespace\\A",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "A",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess4.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class A {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\A->testRequest()": {
|
||||||
|
"fqn": "MyNamespace\\A->testRequest()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "testRequest",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess4.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\A"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public function testRequest()",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyNamespace;
|
||||||
|
|
||||||
|
class ParseErrorsTest {
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$a = new class($this->args) { };
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"references": [],
|
||||||
|
"definitions": {
|
||||||
|
"MyNamespace": {
|
||||||
|
"fqn": "MyNamespace",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "MyNamespace",
|
||||||
|
"kind": 3,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess5.php"
|
||||||
|
},
|
||||||
|
"containerName": ""
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "namespace MyNamespace;",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\ParseErrorsTest": {
|
||||||
|
"fqn": "MyNamespace\\ParseErrorsTest",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": true,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": true,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "ParseErrorsTest",
|
||||||
|
"kind": 5,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess5.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace"
|
||||||
|
},
|
||||||
|
"type": null,
|
||||||
|
"declarationLine": "class ParseErrorsTest {",
|
||||||
|
"documentation": null
|
||||||
|
},
|
||||||
|
"MyNamespace\\ParseErrorsTest->setUp()": {
|
||||||
|
"fqn": "MyNamespace\\ParseErrorsTest->setUp()",
|
||||||
|
"extends": [],
|
||||||
|
"isGlobal": false,
|
||||||
|
"isStatic": false,
|
||||||
|
"canBeInstantiated": false,
|
||||||
|
"symbolInformation": {
|
||||||
|
"name": "setUp",
|
||||||
|
"kind": 6,
|
||||||
|
"location": {
|
||||||
|
"uri": "./memberAccess5.php"
|
||||||
|
},
|
||||||
|
"containerName": "MyNamespace\\ParseErrorsTest"
|
||||||
|
},
|
||||||
|
"type__class": "phpDocumentor\\Reflection\\Types\\Mixed",
|
||||||
|
"type": {},
|
||||||
|
"declarationLine": "public function setUp()",
|
||||||
|
"documentation": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue