From 546660f957623b2cdc179fe107b28581d60ba190 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Wed, 8 Mar 2017 16:19:56 +0100 Subject: [PATCH 001/117] Update README.md (#329) Updating the used by to no longer link to the deprecated repository. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fecd6c7..37800e0 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ Example: - [VS Code PHP IntelliSense](https://github.com/felixfbecker/vscode-php-intellisense) - [Eclipse Che](https://eclipse.org/che/) - [Eclipse IDE (LSP4E-PHP)](https://github.com/eclipselabs/lsp4e-php) - - [Neovim (nvim-cm-php-language-server)](https://github.com/roxma/nvim-cm-php-language-server) + - NeoVim: [LanguageServer-php-neovim](https://github.com/roxma/LanguageServer-php-neovim) with [LanguageClient neovim](https://github.com/autozimu/LanguageClient-neovim) ## Contributing From 4d0a0a2a1053111acb7d3d03d36b927ad9b56b76 Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Sun, 19 Mar 2017 12:15:39 +0100 Subject: [PATCH 002/117] show anything from a doc comment block (#315) --- src/DefinitionResolver.php | 9 ++++++- tests/Server/TextDocument/CompletionTest.php | 28 +++++++++++++++++--- tests/Server/TextDocument/HoverTest.php | 21 ++++++++++++--- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index ec295a5..6bfceb0 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -89,7 +89,14 @@ class DefinitionResolver } else { $docBlock = $node->getAttribute('docBlock'); if ($docBlock !== null) { - return $docBlock->getSummary(); + // check wether we have a description, when true, add a new paragraph + // with the description + $description = $docBlock->getDescription()->render(); + + if (empty($description)) { + return $docBlock->getSummary(); + } + return $docBlock->getSummary() . "\n\n" . $description; } } } diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index dd9e680..786fb55 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -160,7 +160,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, null, - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\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" . + '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" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', null, null, '\TestClass' @@ -179,7 +184,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, 'TestNamespace', - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\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" . + '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" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', null, null, 'TestClass' @@ -209,7 +219,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, 'TestNamespace', - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\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" . + '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" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.' ) ], true), $items); } @@ -347,7 +362,12 @@ class CompletionTest extends TestCase 'TestClass', CompletionItemKind::CLASS_, null, - 'Pariatur ut laborum tempor voluptate consequat ea deserunt.', + 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\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" . + '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" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', null, null, 'TestClass' diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 7d61354..cdc1718 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -22,7 +22,12 @@ class HoverTest extends ServerTestCase )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "range), $result); } @@ -37,7 +42,12 @@ class HoverTest extends ServerTestCase )->wait(); $this->assertEquals(new Hover([ new MarkedString('php', "range), $result); } @@ -181,7 +191,12 @@ class HoverTest extends ServerTestCase $result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(59, 11))->wait(); $this->assertEquals(new Hover([ new MarkedString('php', " Date: Sun, 2 Apr 2017 00:30:10 +0200 Subject: [PATCH 003/117] Fix missing '()' for function definition (#340) --- src/DefinitionResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 6bfceb0..7307de9 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -446,7 +446,7 @@ class DefinitionResolver // Cannot get type for dynamic function call return new Types\Mixed; } - $fqn = (string)($expr->getAttribute('namespacedName') ?? $expr->name); + $fqn = (string)($expr->getAttribute('namespacedName') ?? $expr->name) . '()'; $def = $this->index->getDefinition($fqn, true); if ($def !== null) { return $def->type; From f50df5cdaf47496f36fdff942008753665aca1c6 Mon Sep 17 00:00:00 2001 From: Sara Itani Date: Sun, 2 Apr 2017 05:08:45 -0700 Subject: [PATCH 004/117] Enforce memory limit in phpunit.xml (#320) This will help highlight memory regressions, make it easier for newcomers to get started with the codebase w/o editing php.ini defaults (128M), and also keep things consistent between local and travis runs. --- phpunit.xml.dist | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4e3f6be..6cd8787 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -11,4 +11,7 @@ ./src + + + From 97d1579f3738e1f6df43828a8bbf7de211772d24 Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Sun, 9 Apr 2017 18:23:46 +0200 Subject: [PATCH 005/117] Update PHPParser dependency (#345) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b29c1ee..1f23bca 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ }, "require": { "php": ">=7.0", - "nikic/php-parser": "^3.0.4", + "nikic/php-parser": "^3.0.5", "phpdocumentor/reflection-docblock": "^3.0", "sabre/event": "^5.0", "felixfbecker/advanced-json-rpc": "^2.0", From de6aed608c903845b9134cfbb94156065b67eff0 Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Sun, 9 Apr 2017 19:44:28 +0200 Subject: [PATCH 006/117] Show constructors and destructors with right symbol (#346) --- fixtures/symbols.php | 5 ++ src/Protocol/SymbolInformation.php | 2 + tests/NodeVisitor/DefinitionCollectorTest.php | 8 ++- tests/Server/ServerTestCase.php | 29 +++++----- tests/Server/TextDocument/CompletionTest.php | 9 +++ .../TextDocument/DocumentSymbolTest.php | 27 +++++---- tests/Server/Workspace/SymbolTest.php | 55 ++++++++++--------- 7 files changed, 83 insertions(+), 52 deletions(-) diff --git a/fixtures/symbols.php b/fixtures/symbols.php index d7700bc..4e9b0d4 100644 --- a/fixtures/symbols.php +++ b/fixtures/symbols.php @@ -98,3 +98,8 @@ new class { }; class ChildClass extends TestClass {} + +class Example { + public function __construct() {} + public function __destruct() {} +} diff --git a/src/Protocol/SymbolInformation.php b/src/Protocol/SymbolInformation.php index 5d442f0..299dc55 100644 --- a/src/Protocol/SymbolInformation.php +++ b/src/Protocol/SymbolInformation.php @@ -60,6 +60,8 @@ class SymbolInformation $symbol->kind = SymbolKind::NAMESPACE; } else if ($node instanceof Node\Stmt\Function_) { $symbol->kind = SymbolKind::FUNCTION; + } else if ($node instanceof Node\Stmt\ClassMethod && ($node->name === '__construct' || $node->name === '__destruct')) { + $symbol->kind = SymbolKind::CONSTRUCTOR; } else if ($node instanceof Node\Stmt\ClassMethod) { $symbol->kind = SymbolKind::METHOD; } else if ($node instanceof Node\Stmt\PropertyProperty) { diff --git a/tests/NodeVisitor/DefinitionCollectorTest.php b/tests/NodeVisitor/DefinitionCollectorTest.php index 3ee9995..7345f2c 100644 --- a/tests/NodeVisitor/DefinitionCollectorTest.php +++ b/tests/NodeVisitor/DefinitionCollectorTest.php @@ -50,7 +50,10 @@ class DefinitionCollectorTest extends TestCase 'TestNamespace\\TestTrait', 'TestNamespace\\TestInterface', 'TestNamespace\\test_function()', - 'TestNamespace\\ChildClass' + 'TestNamespace\\ChildClass', + 'TestNamespace\\Example', + 'TestNamespace\\Example->__construct()', + 'TestNamespace\\Example->__destruct()' ], array_keys($defNodes)); $this->assertInstanceOf(Node\Const_::class, $defNodes['TestNamespace\\TEST_CONST']); $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\TestClass']); @@ -63,6 +66,9 @@ class DefinitionCollectorTest extends TestCase $this->assertInstanceOf(Node\Stmt\Interface_::class, $defNodes['TestNamespace\\TestInterface']); $this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\test_function()']); $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\ChildClass']); + $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\Example']); + $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__construct()']); + $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__destruct()']); } public function testDoesNotCollectReferences() diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 94ba933..f5fec55 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -86,20 +86,23 @@ abstract class ServerTestCase extends TestCase 'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))), // Namespaced - 'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))), - 'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 10), new Position( 2, 29))), - '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\\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\\TestInterface' => new Location($symbolsUri, new Range(new Position(68, 0), new Position(71, 1))), + 'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))), + 'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 10), new Position( 2, 29))), + '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\\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\\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::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::staticTestMethod()' => new Location($symbolsUri, new Range(new Position(46, 4), new Position(49, 5))), - 'TestNamespace\\TestClass::testMethod()' => new Location($symbolsUri, new Range(new Position(57, 4), new Position(60, 5))), - 'TestNamespace\\test_function()' => new Location($symbolsUri, new Range(new Position(78, 0), new Position(81, 1))), - 'TestNamespace\\whatever()' => new Location($referencesUri, new Range(new Position(21, 0), new Position(23, 1))) + '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::staticTestMethod()' => new Location($symbolsUri, new Range(new Position(46, 4), new Position(49, 5))), + 'TestNamespace\\TestClass::testMethod()' => new Location($symbolsUri, new Range(new Position(57, 4), new Position(60, 5))), + 'TestNamespace\\test_function()' => new Location($symbolsUri, new Range(new Position(78, 0), new Position(81, 1))), + 'TestNamespace\\whatever()' => new Location($referencesUri, new Range(new Position(21, 0), new Position(23, 1))), + 'TestNamespace\\Example' => new Location($symbolsUri, new Range(new Position(101, 0), new Position(104, 1))), + 'TestNamespace\\Example::__construct' => new Location($symbolsUri, new Range(new Position(102, 4), new Position(102, 36))), + 'TestNamespace\\Example::__destruct' => new Location($symbolsUri, new Range(new Position(103, 4), new Position(103, 35))) ]; $this->referenceLocations = [ diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 786fb55..29851e0 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -203,6 +203,15 @@ class CompletionTest extends TestCase null, '\TestNamespace\ChildClass' ), + new CompletionItem( + 'Example', + CompletionItemKind::CLASS_, + 'TestNamespace', + null, + null, + null, + '\TestNamespace\Example' + ) ], true), $items); } diff --git a/tests/Server/TextDocument/DocumentSymbolTest.php b/tests/Server/TextDocument/DocumentSymbolTest.php index 89d24ee..155e4a2 100644 --- a/tests/Server/TextDocument/DocumentSymbolTest.php +++ b/tests/Server/TextDocument/DocumentSymbolTest.php @@ -18,18 +18,21 @@ class DocumentSymbolTest extends ServerTestCase $result = $this->textDocument->documentSymbol(new TextDocumentIdentifier($uri))->wait(); // @codingStandardsIgnoreStart $this->assertEquals([ - new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('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('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), - new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), - new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), - new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), + new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('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('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), + new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), + new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), + new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), + new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), + new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), + new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example') ], $result); // @codingStandardsIgnoreEnd } diff --git a/tests/Server/Workspace/SymbolTest.php b/tests/Server/Workspace/SymbolTest.php index d3d13b6..5d0ed34 100644 --- a/tests/Server/Workspace/SymbolTest.php +++ b/tests/Server/Workspace/SymbolTest.php @@ -29,35 +29,38 @@ class SymbolTest extends ServerTestCase $referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); // @codingStandardsIgnoreStart $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, 10), new Position(2, 23))), ''), // Namespaced - new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'), - new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'), - new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), - new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), - new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), - new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), - new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\whatever()'), '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('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), + new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), + new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), + new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), + new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), + new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), + new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example'), + new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\whatever()'), 'TestNamespace'), // Global - new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_CONST'), ''), - new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestClass'), ''), - new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), 'TestClass'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::staticTestProperty'), 'TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::testProperty'), 'TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass'), - new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestTrait'), ''), - new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestInterface'), ''), - new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('test_function()'), ''), - new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('ChildClass'), ''), - new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), + new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_CONST'), ''), + new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestClass'), ''), + new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), 'TestClass'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::staticTestProperty'), 'TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::testProperty'), 'TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass'), + new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestTrait'), ''), + new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestInterface'), ''), + new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('test_function()'), ''), + new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('ChildClass'), ''), + new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), - new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') + new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') ], $result); // @codingStandardsIgnoreEnd } From b1cc7bf6b0e4991bc4f1640c47675466785ebad5 Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Mon, 17 Apr 2017 17:03:08 +0200 Subject: [PATCH 007/117] Support constants with define() (#347) --- .../completion/constant_with_namespace.php | 23 ++++++++ fixtures/global_symbols.php | 7 +++ src/DefinitionResolver.php | 5 ++ src/Index/Index.php | 2 +- src/Protocol/SymbolInformation.php | 40 +++++++++---- tests/LanguageServerTest.php | 4 +- tests/Server/ServerTestCase.php | 28 +++++---- tests/Server/TextDocument/HoverTest.php | 15 +++++ tests/Server/Workspace/SymbolTest.php | 59 ++++++++++--------- 9 files changed, 126 insertions(+), 57 deletions(-) create mode 100644 fixtures/completion/constant_with_namespace.php diff --git a/fixtures/completion/constant_with_namespace.php b/fixtures/completion/constant_with_namespace.php new file mode 100644 index 0000000..cade11f --- /dev/null +++ b/fixtures/completion/constant_with_namespace.php @@ -0,0 +1,23 @@ +namespacedName . '::' . $node->name; } + } else if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && strtolower((string)$node->name) === 'define') { + if (!isset($node->args[0]) || !($node->args[0]->value instanceof Node\Scalar\String_)) { + return null; + } + return (string)$node->args[0]->value->value; } } } diff --git a/src/Index/Index.php b/src/Index/Index.php index 5c24813..b753476 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -117,7 +117,7 @@ class Index implements ReadableIndex, \Serializable * Registers a definition * * @param string $fqn The fully qualified name of the symbol - * @param string $definition The Definition object + * @param Definition $definition The Definition object * @return void */ public function setDefinition(string $fqn, Definition $definition) diff --git a/src/Protocol/SymbolInformation.php b/src/Protocol/SymbolInformation.php index 299dc55..4fb1d3f 100644 --- a/src/Protocol/SymbolInformation.php +++ b/src/Protocol/SymbolInformation.php @@ -50,9 +50,19 @@ class SymbolInformation { $parent = $node->getAttribute('parentNode'); $symbol = new self; - if ($node instanceof Node\Stmt\Class_) { - $symbol->kind = SymbolKind::CLASS_; - } else if ($node instanceof Node\Stmt\Trait_) { + + if ( + $node instanceof Node\Expr\FuncCall + && $node->name instanceof Node\Name + && strtolower((string)$node->name) === 'define' + && isset($node->args[0]) + && $node->args[0]->value instanceof Node\Scalar\String_ + ) { + // constants with define() like + // define('TEST_DEFINE_CONSTANT', false); + $symbol->kind = SymbolKind::CONSTANT; + $symbol->name = (string)$node->args[0]->value->value; + } else if ($node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_) { $symbol->kind = SymbolKind::CLASS_; } else if ($node instanceof Node\Stmt\Interface_) { $symbol->kind = SymbolKind::INTERFACE; @@ -80,17 +90,21 @@ class SymbolInformation } else { return null; } - if ($node instanceof Node\Name) { - $symbol->name = (string)$node; - } else if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) { - $symbol->name = $node->var->name; - } else if ($node instanceof Node\Expr\ClosureUse) { - $symbol->name = $node->var; - } else if (isset($node->name)) { - $symbol->name = (string)$node->name; - } else { - return null; + + if (!isset($symbol->name)) { + if ($node instanceof Node\Name) { + $symbol->name = (string)$node; + } else if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) { + $symbol->name = $node->var->name; + } else if ($node instanceof Node\Expr\ClosureUse) { + $symbol->name = $node->var; + } else if (isset($node->name)) { + $symbol->name = (string)$node->name; + } else { + return null; + } } + $symbol->location = Location::fromNode($node); if ($fqn !== null) { $parts = preg_split('/(::|->|\\\\)/', $fqn); diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index c32a76a..fb52ef6 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -57,7 +57,7 @@ class LanguageServerTest extends TestCase if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { if ($msg->body->params->type === MessageType::ERROR) { $promise->reject(new Exception($msg->body->params->message)); - } else if (strpos($msg->body->params->message, 'All 26 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 27 PHP files parsed') !== false) { $promise->fulfill(); } } @@ -103,7 +103,7 @@ class LanguageServerTest extends TestCase if ($promise->state === Promise::PENDING) { $promise->reject(new Exception($msg->body->params->message)); } - } else if (strpos($msg->body->params->message, 'All 26 PHP files parsed') !== false) { + } else if (strpos($msg->body->params->message, 'All 27 PHP files parsed') !== false) { $promise->fulfill(); } } diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index f5fec55..1154216 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -72,18 +72,19 @@ abstract class ServerTestCase extends TestCase $this->definitionLocations = [ // Global - 'TEST_CONST' => new Location($globalSymbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))), - 'TestClass' => new Location($globalSymbolsUri, new Range(new Position(20, 0), new Position(61, 1))), - 'ChildClass' => new Location($globalSymbolsUri, new Range(new Position(99, 0), new Position(99, 37))), - 'TestTrait' => new Location($globalSymbolsUri, new Range(new Position(63, 0), new Position(66, 1))), - 'TestInterface' => new Location($globalSymbolsUri, new Range(new Position(68, 0), new Position(71, 1))), - 'TestClass::TEST_CLASS_CONST' => new Location($globalSymbolsUri, new Range(new Position(27, 10), new Position(27, 32))), - 'TestClass::testProperty' => new Location($globalSymbolsUri, new Range(new Position(41, 11), new Position(41, 24))), - 'TestClass::staticTestProperty' => new Location($globalSymbolsUri, new Range(new Position(34, 18), new Position(34, 37))), - 'TestClass::staticTestMethod()' => new Location($globalSymbolsUri, new Range(new Position(46, 4), new Position(49, 5))), - 'TestClass::testMethod()' => new Location($globalSymbolsUri, new Range(new Position(57, 4), new Position(60, 5))), - 'test_function()' => new Location($globalSymbolsUri, new Range(new Position(78, 0), new Position(81, 1))), - 'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))), + 'TEST_DEFINE_CONSTANT' => new Location($globalSymbolsUri, new Range(new Position(104, 0), new Position(104, 37))), + 'TEST_CONST' => new Location($globalSymbolsUri, new Range(new Position( 9, 6), new Position( 9, 22))), + 'TestClass' => new Location($globalSymbolsUri, new Range(new Position(20, 0), new Position(61, 1))), + 'ChildClass' => new Location($globalSymbolsUri, new Range(new Position(99, 0), new Position(99, 37))), + 'TestTrait' => new Location($globalSymbolsUri, new Range(new Position(63, 0), new Position(66, 1))), + 'TestInterface' => new Location($globalSymbolsUri, new Range(new Position(68, 0), new Position(71, 1))), + 'TestClass::TEST_CLASS_CONST' => new Location($globalSymbolsUri, new Range(new Position(27, 10), new Position(27, 32))), + 'TestClass::testProperty' => new Location($globalSymbolsUri, new Range(new Position(41, 11), new Position(41, 24))), + 'TestClass::staticTestProperty' => new Location($globalSymbolsUri, new Range(new Position(34, 18), new Position(34, 37))), + 'TestClass::staticTestMethod()' => new Location($globalSymbolsUri, new Range(new Position(46, 4), new Position(49, 5))), + 'TestClass::testMethod()' => new Location($globalSymbolsUri, new Range(new Position(57, 4), new Position(60, 5))), + 'test_function()' => new Location($globalSymbolsUri, new Range(new Position(78, 0), new Position(81, 1))), + 'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))), // Namespaced 'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))), @@ -163,6 +164,9 @@ abstract class ServerTestCase extends TestCase ], // Global + 'TEST_DEFINE_CONSTANT' => [ + 0 => new Location($globalSymbolsUri, new Range(new Position(106, 6), new Position(106, 26))) + ], 'TEST_CONST' => [ 0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15))), 1 => new Location($globalReferencesUri, new Range(new Position(29, 5), new Position(29, 15))) diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index cdc1718..4da707a 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -156,6 +156,21 @@ class HoverTest extends ServerTestCase ], $reference->range), $result); } + public function testHoverForGlobalConstant() + { + // print TEST_DEFINE_CONSTANT ? 'true' : 'false'; + // Get hover for TEST_DEFINE_CONSTANT + $reference = $this->getReferenceLocations('TEST_DEFINE_CONSTANT')[0]; + $result = $this->textDocument->hover( + new TextDocumentIdentifier($reference->uri), + $reference->range->end + )->wait(); + $this->assertEquals(new Hover([ + new MarkedString('php', "range), $result); + } + public function testHoverForVariable() { // echo $var; diff --git a/tests/Server/Workspace/SymbolTest.php b/tests/Server/Workspace/SymbolTest.php index 5d0ed34..65ce804 100644 --- a/tests/Server/Workspace/SymbolTest.php +++ b/tests/Server/Workspace/SymbolTest.php @@ -29,38 +29,39 @@ class SymbolTest extends ServerTestCase $referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); // @codingStandardsIgnoreStart $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, 10), new Position(2, 23))), ''), // Namespaced - new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'), - new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'), - new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), - new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), - new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), - new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), - new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), - new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), - new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), - new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example'), - new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\whatever()'), '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('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TestClass::TEST_CLASS_CONST'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestNamespace\\TestClass::testProperty'), 'TestNamespace\\TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::staticTestMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestNamespace\\TestClass::testMethod()'), 'TestNamespace\\TestClass'), + new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestTrait'), 'TestNamespace'), + new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestNamespace\\TestInterface'), 'TestNamespace'), + new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\test_function()'), 'TestNamespace'), + new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\ChildClass'), 'TestNamespace'), + new SymbolInformation('Example', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\Example'), 'TestNamespace'), + new SymbolInformation('__construct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__construct'), 'TestNamespace\\Example'), + new SymbolInformation('__destruct', SymbolKind::CONSTRUCTOR, $this->getDefinitionLocation('TestNamespace\\Example::__destruct'), 'TestNamespace\\Example'), + new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('TestNamespace\\whatever()'), 'TestNamespace'), // Global - new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_CONST'), ''), - new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestClass'), ''), - new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), 'TestClass'), - new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::staticTestProperty'), 'TestClass'), - new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::testProperty'), 'TestClass'), - new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), - new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass'), - new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestTrait'), ''), - new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestInterface'), ''), - new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('test_function()'), ''), - new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('ChildClass'), ''), - new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), + new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_CONST'), ''), + new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestClass'), ''), + new SymbolInformation('TEST_CLASS_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestClass::TEST_CLASS_CONST'), 'TestClass'), + new SymbolInformation('staticTestProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::staticTestProperty'), 'TestClass'), + new SymbolInformation('testProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('TestClass::testProperty'), 'TestClass'), + new SymbolInformation('staticTestMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::staticTestMethod()'), 'TestClass'), + new SymbolInformation('testMethod', SymbolKind::METHOD, $this->getDefinitionLocation('TestClass::testMethod()'), 'TestClass'), + new SymbolInformation('TestTrait', SymbolKind::CLASS_, $this->getDefinitionLocation('TestTrait'), ''), + new SymbolInformation('TestInterface', SymbolKind::INTERFACE, $this->getDefinitionLocation('TestInterface'), ''), + new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('test_function()'), ''), + new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('ChildClass'), ''), + new SymbolInformation('TEST_DEFINE_CONSTANT', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_DEFINE_CONSTANT'), ''), + new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), - new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') + new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') ], $result); // @codingStandardsIgnoreEnd } From 08cf1a3fd7b6dd97bf28a4fff5603effe313edc7 Mon Sep 17 00:00:00 2001 From: Stephan Unverwerth Date: Mon, 24 Apr 2017 11:11:40 +0200 Subject: [PATCH 008/117] Allow getting type from define() node (#363) * Allow getting type from define() node - fixes #364 * Add test case for DefinitionResolver --- src/DefinitionResolver.php | 26 ++++++++++-- src/Protocol/SymbolInformation.php | 3 +- tests/DefinitionResolverTest.php | 64 ++++++++++++++++++++++++++++++ tests/MockPhpDocument.php | 20 ++++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 tests/DefinitionResolverTest.php create mode 100644 tests/MockPhpDocument.php diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index bbf9076..5a6f88b 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -725,6 +725,19 @@ class DefinitionResolver */ public function getTypeFromNode(Node $node) { + if ( + $node instanceof Node\Expr\FuncCall + && $node->name instanceof Node\Name + && strtolower((string)$node->name) === 'define' + && isset($node->args[0]) + && $node->args[0]->value instanceof Node\Scalar\String_ + && isset($node->args[1]) + ) { + // constants with define() like + // define('TEST_DEFINE_CONSTANT', false); + return $this->resolveExpressionNodeToType($node->args[1]->value); + } + if ($node instanceof Node\Param) { // Parameters $docBlock = $node->getAttribute('parentNode')->getAttribute('docBlock'); @@ -882,11 +895,16 @@ class DefinitionResolver } return (string)$class->namespacedName . '::' . $node->name; } - } else if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && strtolower((string)$node->name) === 'define') { - if (!isset($node->args[0]) || !($node->args[0]->value instanceof Node\Scalar\String_)) { - return null; - } + } else if ( + $node instanceof Node\Expr\FuncCall + && $node->name instanceof Node\Name + && strtolower((string)$node->name) === 'define' + && isset($node->args[0]) + && $node->args[0]->value instanceof Node\Scalar\String_ + && isset($node->args[1]) + ) { return (string)$node->args[0]->value->value; } + return null; } } diff --git a/src/Protocol/SymbolInformation.php b/src/Protocol/SymbolInformation.php index 4fb1d3f..da04404 100644 --- a/src/Protocol/SymbolInformation.php +++ b/src/Protocol/SymbolInformation.php @@ -57,6 +57,7 @@ class SymbolInformation && strtolower((string)$node->name) === 'define' && isset($node->args[0]) && $node->args[0]->value instanceof Node\Scalar\String_ + && isset($node->args[1]) ) { // constants with define() like // define('TEST_DEFINE_CONSTANT', false); @@ -90,7 +91,7 @@ class SymbolInformation } else { return null; } - + if (!isset($symbol->name)) { if ($node instanceof Node\Name) { $symbol->name = (string)$node; diff --git a/tests/DefinitionResolverTest.php b/tests/DefinitionResolverTest.php new file mode 100644 index 0000000..0046ef0 --- /dev/null +++ b/tests/DefinitionResolverTest.php @@ -0,0 +1,64 @@ +parse("setAttribute('ownerDocument', new MockPhpDocument); + + $index = new Index; + $definitionResolver = new DefinitionResolver($index); + $def = $definitionResolver->createDefinitionFromNode($stmts[0], '\TEST_DEFINE'); + + $this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $def->type); + } + + public function testGetTypeFromNode() + { + $parser = new Parser; + $stmts = $parser->parse("setAttribute('ownerDocument', new MockPhpDocument); + + $index = new Index; + $definitionResolver = new DefinitionResolver($index); + $type = $definitionResolver->getTypeFromNode($stmts[0]); + + $this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $type); + } + + public function testGetDefinedFqnForIncompleteDefine() + { + // define('XXX') (only one argument) must not introduce a new symbol + $parser = new Parser; + $stmts = $parser->parse("setAttribute('ownerDocument', new MockPhpDocument); + + $index = new Index; + $definitionResolver = new DefinitionResolver($index); + $fqn = $definitionResolver->getDefinedFqn($stmts[0]); + + $this->assertNull($fqn); + } + + public function testGetDefinedFqnForDefine() + { + $parser = new Parser; + $stmts = $parser->parse("setAttribute('ownerDocument', new MockPhpDocument); + + $index = new Index; + $definitionResolver = new DefinitionResolver($index); + $fqn = $definitionResolver->getDefinedFqn($stmts[0]); + + $this->assertEquals('TEST_DEFINE', $fqn); + } +} diff --git a/tests/MockPhpDocument.php b/tests/MockPhpDocument.php new file mode 100644 index 0000000..48d4b70 --- /dev/null +++ b/tests/MockPhpDocument.php @@ -0,0 +1,20 @@ + Date: Fri, 9 Jun 2017 14:25:30 -0400 Subject: [PATCH 009/117] Adopt Microsoft/tolerant-php-parser (#357) --- .gitignore | 1 + .gitmodules | 27 + Performance.php | 65 + composer.json | 8 +- phpcs.xml.dist | 1 + phpunit.xml.dist | 2 +- src/CompletionProvider.php | 354 ++--- src/ComposerScripts.php | 3 +- src/Definition.php | 1 - src/DefinitionResolver.php | 1242 ++++++++++------- src/FqnUtilities.php | 31 + src/Index/Index.php | 11 + src/Indexer.php | 1 + src/LanguageServer.php | 7 +- src/NodeVisitor/ColumnCalculator.php | 41 - src/NodeVisitor/DefinitionCollector.php | 47 - src/NodeVisitor/DocBlockParser.php | 96 -- src/NodeVisitor/NodeAtPositionFinder.php | 45 - src/NodeVisitor/ReferencesAdder.php | 54 - src/NodeVisitor/ReferencesCollector.php | 75 - .../VariableReferencesCollector.php | 50 - src/Parser.php | 25 - src/ParserHelpers.php | 124 ++ src/PhpDocument.php | 123 +- src/PhpDocumentLoader.php | 13 +- src/Protocol/Diagnostic.php | 22 - src/Protocol/Location.php | 11 +- src/Protocol/Range.php | 27 +- src/Protocol/SymbolInformation.php | 83 +- src/Server/TextDocument.php | 82 +- src/TreeAnalyzer.php | 209 +++ src/utils.php | 18 - tests/DefinitionResolverTest.php | 35 +- tests/NodeVisitor/DefinitionCollectorTest.php | 94 +- tests/PhpDocumentLoaderTest.php | 20 +- tests/PhpDocumentTest.php | 29 +- tests/Server/ServerTestCase.php | 51 +- tests/Server/TextDocument/CompletionTest.php | 61 +- .../Definition/GlobalFallbackTest.php | 7 +- .../TextDocument/Definition/GlobalTest.php | 2 +- tests/Server/TextDocument/DidChangeTest.php | 9 +- tests/Server/TextDocument/DidCloseTest.php | 7 +- tests/Server/TextDocument/FormattingTest.php | 5 +- tests/Server/TextDocument/HoverTest.php | 15 +- tests/Server/TextDocument/ParseErrorsTest.php | 59 +- .../References/GlobalFallbackTest.php | 15 +- tests/Server/Workspace/SymbolTest.php | 2 +- tests/Validation/ValidationTest.php | 155 ++ .../Validation/cases/WithReturnTypehints.php | 18 + .../WithReturnTypehints.php.expected.json | 110 ++ ...nonymousClassMembersShouldNotBeSymbols.php | 11 + ...embersShouldNotBeSymbols.php.expected.json | 23 + .../cases/arrayValueShouldBeBoolean.php | 4 + ...rrayValueShouldBeBoolean.php.expected.json | 42 + tests/Validation/cases/caseStatement1.php | 7 + .../cases/caseStatement1.php.expected.json | 30 + tests/Validation/cases/classDefinition1.php | 11 + .../cases/classDefinition1.php.expected.json | 67 + tests/Validation/cases/classProperty1.php | 19 + .../cases/classProperty1.php.expected.json | 86 ++ tests/Validation/cases/constants.php | 13 + .../cases/constants.php.expected.json | 67 + tests/Validation/cases/constants2.php | 13 + .../cases/constants2.php.expected.json | 67 + tests/Validation/cases/constants3.php | 11 + .../cases/constants3.php.expected.json | 67 + tests/Validation/cases/constants4.php | 11 + .../cases/constants4.php.expected.json | 67 + tests/Validation/cases/constants5.php | 8 + .../cases/constants5.php.expected.json | 64 + .../cases/constantsInFunctionParamDefault.php | 5 + ...tsInFunctionParamDefault.php.expected.json | 46 + .../cases/docBlocksOnNamespaceDefinition.php | 6 + ...cksOnNamespaceDefinition.php.expected.json | 23 + tests/Validation/cases/exceptions1.php | 8 + .../cases/exceptions1.php.expected.json | 23 + tests/Validation/cases/forLoopReference1.php | 12 + .../cases/forLoopReference1.php.expected.json | 60 + tests/Validation/cases/functionUse.php | 7 + .../cases/functionUse.php.expected.json | 11 + tests/Validation/cases/functionUse2.php | 4 + .../cases/functionUse2.php.expected.json | 11 + tests/Validation/cases/ifStatement1.php | 6 + .../cases/ifStatement1.php.expected.json | 30 + tests/Validation/cases/interfaceProperty.php | 5 + .../cases/interfaceProperty.php.expected.json | 23 + .../cases/magicConstantsShouldBeGlobal.php | 5 + ...cConstantsShouldBeGlobal.php.expected.json | 23 + tests/Validation/cases/magicConsts.php | 7 + .../cases/magicConsts.php.expected.json | 42 + tests/Validation/cases/memberAccess1.php | 10 + .../cases/memberAccess1.php.expected.json | 67 + tests/Validation/cases/memberAccess2.php | 10 + .../cases/memberAccess2.php.expected.json | 67 + tests/Validation/cases/memberAccess3.php | 13 + .../cases/memberAccess3.php.expected.json | 82 ++ tests/Validation/cases/memberAccess4.php | 10 + .../cases/memberAccess4.php.expected.json | 73 + tests/Validation/cases/memberAccess5.php | 11 + .../cases/memberAccess5.php.expected.json | 60 + tests/Validation/cases/memberCall1.php | 16 + .../cases/memberCall1.php.expected.json | 70 + tests/Validation/cases/multipleNamespaces.php | 17 + .../multipleNamespaces.php.expected.json | 130 ++ .../cases/multiplePreceedingComments.php | 15 + ...ltiplePreceedingComments.php.expected.json | 42 + tests/Validation/cases/nameToken.php | 7 + .../cases/nameToken.php.expected.json | 42 + tests/Validation/cases/namespaces2.php | 5 + .../cases/namespaces2.php.expected.json | 36 + tests/Validation/cases/namespaces3.php | 3 + .../cases/namespaces3.php.expected.json | 14 + tests/Validation/cases/namespaces4.php | 2 + .../cases/namespaces4.php.expected.json | 11 + tests/Validation/cases/namespaces5.php | 3 + .../cases/namespaces5.php.expected.json | 45 + tests/Validation/cases/namespaces6.php | 2 + .../cases/namespaces6.php.expected.json | 23 + tests/Validation/cases/namespaces7.php | 4 + .../cases/namespaces7.php.expected.json | 4 + tests/Validation/cases/namespaces8.php | 6 + .../cases/namespaces8.php.expected.json | 27 + tests/Validation/cases/namespaces9.php | 6 + .../cases/namespaces9.php.expected.json | 4 + tests/Validation/cases/newStatic.php | 12 + .../cases/newStatic.php.expected.json | 63 + tests/Validation/cases/objectCreation.php | 9 + .../cases/objectCreation.php.expected.json | 64 + tests/Validation/cases/objectCreation2.php | 12 + .../cases/objectCreation2.php.expected.json | 85 ++ tests/Validation/cases/objectCreation3.php | 9 + .../cases/objectCreation3.php.expected.json | 46 + tests/Validation/cases/param1.php | 6 + .../Validation/cases/param1.php.expected.json | 46 + .../cases/parameterTypeResolution1.php | 12 + ...parameterTypeResolution1.php.expected.json | 66 + tests/Validation/cases/parent1.php | 15 + .../cases/parent1.php.expected.json | 106 ++ tests/Validation/cases/parent2.php | 15 + .../cases/parent2.php.expected.json | 102 ++ tests/Validation/cases/parent3.php | 15 + .../cases/parent3.php.expected.json | 109 ++ tests/Validation/cases/propertyName1.php | 12 + .../cases/propertyName1.php.expected.json | 42 + tests/Validation/cases/propertyName2.php | 10 + .../cases/propertyName2.php.expected.json | 42 + tests/Validation/cases/returnType.php | 13 + .../cases/returnType.php.expected.json | 49 + .../Validation/cases/scopedPropertyAccess.php | 9 + .../scopedPropertyAccess.php.expected.json | 67 + .../cases/scopedPropertyAccess2.php | 6 + .../scopedPropertyAccess2.php.expected.json | 27 + .../cases/scopedPropertyAccess3.php | 8 + .../scopedPropertyAccess3.php.expected.json | 49 + .../cases/scopedPropertyAccess4.php | 3 + .../scopedPropertyAccess4.php.expected.json | 8 + .../cases/scopedPropertyAccess5.php | 13 + .../scopedPropertyAccess5.php.expected.json | 55 + tests/Validation/cases/self1.php | 16 + .../Validation/cases/self1.php.expected.json | 109 ++ tests/Validation/cases/self2.php | 15 + .../Validation/cases/self2.php.expected.json | 109 ++ tests/Validation/cases/self3.php | 15 + .../Validation/cases/self3.php.expected.json | 109 ++ tests/Validation/cases/self4.php | 13 + .../Validation/cases/self4.php.expected.json | 73 + tests/Validation/cases/self5.php | 12 + .../Validation/cases/self5.php.expected.json | 64 + tests/Validation/cases/static1.php | 15 + .../cases/static1.php.expected.json | 109 ++ tests/Validation/cases/static2.php | 15 + .../cases/static2.php.expected.json | 109 ++ tests/Validation/cases/static3.php | 15 + .../cases/static3.php.expected.json | 109 ++ tests/Validation/cases/static4.php | 9 + .../cases/static4.php.expected.json | 69 + tests/Validation/cases/staticInArray.php | 5 + .../cases/staticInArray.php.expected.json | 4 + tests/Validation/cases/stringVariable.php | 9 + .../cases/stringVariable.php.expected.json | 61 + .../testQualifiedNameOutsideOfNamespace.php | 5 + ...edNameOutsideOfNamespace.php.expected.json | 27 + .../cases/verifyFqsenOnClassProperty.php | 9 + ...rifyFqsenOnClassProperty.php.expected.json | 68 + tests/Validation/disabled.json | 7 + validation/frameworks/cakephp | 1 + validation/frameworks/codeigniter | 1 + validation/frameworks/drupal | 1 + validation/frameworks/math-php | 1 + validation/frameworks/php-language-server | 1 + validation/frameworks/phpunit | 1 + validation/frameworks/symfony | 1 + validation/frameworks/tolerant-php-parser | 1 + validation/frameworks/wordpress | 1 + 194 files changed, 6468 insertions(+), 1501 deletions(-) create mode 100644 .gitmodules create mode 100644 Performance.php create mode 100644 src/FqnUtilities.php delete mode 100644 src/NodeVisitor/ColumnCalculator.php delete mode 100644 src/NodeVisitor/DefinitionCollector.php delete mode 100644 src/NodeVisitor/DocBlockParser.php delete mode 100644 src/NodeVisitor/NodeAtPositionFinder.php delete mode 100644 src/NodeVisitor/ReferencesAdder.php delete mode 100644 src/NodeVisitor/ReferencesCollector.php delete mode 100644 src/NodeVisitor/VariableReferencesCollector.php delete mode 100644 src/Parser.php create mode 100644 src/ParserHelpers.php create mode 100644 src/TreeAnalyzer.php create mode 100644 tests/Validation/ValidationTest.php create mode 100644 tests/Validation/cases/WithReturnTypehints.php create mode 100644 tests/Validation/cases/WithReturnTypehints.php.expected.json create mode 100644 tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php create mode 100644 tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json create mode 100644 tests/Validation/cases/arrayValueShouldBeBoolean.php create mode 100644 tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json create mode 100644 tests/Validation/cases/caseStatement1.php create mode 100644 tests/Validation/cases/caseStatement1.php.expected.json create mode 100644 tests/Validation/cases/classDefinition1.php create mode 100644 tests/Validation/cases/classDefinition1.php.expected.json create mode 100644 tests/Validation/cases/classProperty1.php create mode 100644 tests/Validation/cases/classProperty1.php.expected.json create mode 100644 tests/Validation/cases/constants.php create mode 100644 tests/Validation/cases/constants.php.expected.json create mode 100644 tests/Validation/cases/constants2.php create mode 100644 tests/Validation/cases/constants2.php.expected.json create mode 100644 tests/Validation/cases/constants3.php create mode 100644 tests/Validation/cases/constants3.php.expected.json create mode 100644 tests/Validation/cases/constants4.php create mode 100644 tests/Validation/cases/constants4.php.expected.json create mode 100644 tests/Validation/cases/constants5.php create mode 100644 tests/Validation/cases/constants5.php.expected.json create mode 100644 tests/Validation/cases/constantsInFunctionParamDefault.php create mode 100644 tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json create mode 100644 tests/Validation/cases/docBlocksOnNamespaceDefinition.php create mode 100644 tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json create mode 100644 tests/Validation/cases/exceptions1.php create mode 100644 tests/Validation/cases/exceptions1.php.expected.json create mode 100644 tests/Validation/cases/forLoopReference1.php create mode 100644 tests/Validation/cases/forLoopReference1.php.expected.json create mode 100644 tests/Validation/cases/functionUse.php create mode 100644 tests/Validation/cases/functionUse.php.expected.json create mode 100644 tests/Validation/cases/functionUse2.php create mode 100644 tests/Validation/cases/functionUse2.php.expected.json create mode 100644 tests/Validation/cases/ifStatement1.php create mode 100644 tests/Validation/cases/ifStatement1.php.expected.json create mode 100644 tests/Validation/cases/interfaceProperty.php create mode 100644 tests/Validation/cases/interfaceProperty.php.expected.json create mode 100644 tests/Validation/cases/magicConstantsShouldBeGlobal.php create mode 100644 tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json create mode 100644 tests/Validation/cases/magicConsts.php create mode 100644 tests/Validation/cases/magicConsts.php.expected.json create mode 100644 tests/Validation/cases/memberAccess1.php create mode 100644 tests/Validation/cases/memberAccess1.php.expected.json create mode 100644 tests/Validation/cases/memberAccess2.php create mode 100644 tests/Validation/cases/memberAccess2.php.expected.json create mode 100644 tests/Validation/cases/memberAccess3.php create mode 100644 tests/Validation/cases/memberAccess3.php.expected.json create mode 100644 tests/Validation/cases/memberAccess4.php create mode 100644 tests/Validation/cases/memberAccess4.php.expected.json create mode 100644 tests/Validation/cases/memberAccess5.php create mode 100644 tests/Validation/cases/memberAccess5.php.expected.json create mode 100644 tests/Validation/cases/memberCall1.php create mode 100644 tests/Validation/cases/memberCall1.php.expected.json create mode 100644 tests/Validation/cases/multipleNamespaces.php create mode 100644 tests/Validation/cases/multipleNamespaces.php.expected.json create mode 100644 tests/Validation/cases/multiplePreceedingComments.php create mode 100644 tests/Validation/cases/multiplePreceedingComments.php.expected.json create mode 100644 tests/Validation/cases/nameToken.php create mode 100644 tests/Validation/cases/nameToken.php.expected.json create mode 100644 tests/Validation/cases/namespaces2.php create mode 100644 tests/Validation/cases/namespaces2.php.expected.json create mode 100644 tests/Validation/cases/namespaces3.php create mode 100644 tests/Validation/cases/namespaces3.php.expected.json create mode 100644 tests/Validation/cases/namespaces4.php create mode 100644 tests/Validation/cases/namespaces4.php.expected.json create mode 100644 tests/Validation/cases/namespaces5.php create mode 100644 tests/Validation/cases/namespaces5.php.expected.json create mode 100644 tests/Validation/cases/namespaces6.php create mode 100644 tests/Validation/cases/namespaces6.php.expected.json create mode 100644 tests/Validation/cases/namespaces7.php create mode 100644 tests/Validation/cases/namespaces7.php.expected.json create mode 100644 tests/Validation/cases/namespaces8.php create mode 100644 tests/Validation/cases/namespaces8.php.expected.json create mode 100644 tests/Validation/cases/namespaces9.php create mode 100644 tests/Validation/cases/namespaces9.php.expected.json create mode 100644 tests/Validation/cases/newStatic.php create mode 100644 tests/Validation/cases/newStatic.php.expected.json create mode 100644 tests/Validation/cases/objectCreation.php create mode 100644 tests/Validation/cases/objectCreation.php.expected.json create mode 100644 tests/Validation/cases/objectCreation2.php create mode 100644 tests/Validation/cases/objectCreation2.php.expected.json create mode 100644 tests/Validation/cases/objectCreation3.php create mode 100644 tests/Validation/cases/objectCreation3.php.expected.json create mode 100644 tests/Validation/cases/param1.php create mode 100644 tests/Validation/cases/param1.php.expected.json create mode 100644 tests/Validation/cases/parameterTypeResolution1.php create mode 100644 tests/Validation/cases/parameterTypeResolution1.php.expected.json create mode 100644 tests/Validation/cases/parent1.php create mode 100644 tests/Validation/cases/parent1.php.expected.json create mode 100644 tests/Validation/cases/parent2.php create mode 100644 tests/Validation/cases/parent2.php.expected.json create mode 100644 tests/Validation/cases/parent3.php create mode 100644 tests/Validation/cases/parent3.php.expected.json create mode 100644 tests/Validation/cases/propertyName1.php create mode 100644 tests/Validation/cases/propertyName1.php.expected.json create mode 100644 tests/Validation/cases/propertyName2.php create mode 100644 tests/Validation/cases/propertyName2.php.expected.json create mode 100644 tests/Validation/cases/returnType.php create mode 100644 tests/Validation/cases/returnType.php.expected.json create mode 100644 tests/Validation/cases/scopedPropertyAccess.php create mode 100644 tests/Validation/cases/scopedPropertyAccess.php.expected.json create mode 100644 tests/Validation/cases/scopedPropertyAccess2.php create mode 100644 tests/Validation/cases/scopedPropertyAccess2.php.expected.json create mode 100644 tests/Validation/cases/scopedPropertyAccess3.php create mode 100644 tests/Validation/cases/scopedPropertyAccess3.php.expected.json create mode 100644 tests/Validation/cases/scopedPropertyAccess4.php create mode 100644 tests/Validation/cases/scopedPropertyAccess4.php.expected.json create mode 100644 tests/Validation/cases/scopedPropertyAccess5.php create mode 100644 tests/Validation/cases/scopedPropertyAccess5.php.expected.json create mode 100644 tests/Validation/cases/self1.php create mode 100644 tests/Validation/cases/self1.php.expected.json create mode 100644 tests/Validation/cases/self2.php create mode 100644 tests/Validation/cases/self2.php.expected.json create mode 100644 tests/Validation/cases/self3.php create mode 100644 tests/Validation/cases/self3.php.expected.json create mode 100644 tests/Validation/cases/self4.php create mode 100644 tests/Validation/cases/self4.php.expected.json create mode 100644 tests/Validation/cases/self5.php create mode 100644 tests/Validation/cases/self5.php.expected.json create mode 100644 tests/Validation/cases/static1.php create mode 100644 tests/Validation/cases/static1.php.expected.json create mode 100644 tests/Validation/cases/static2.php create mode 100644 tests/Validation/cases/static2.php.expected.json create mode 100644 tests/Validation/cases/static3.php create mode 100644 tests/Validation/cases/static3.php.expected.json create mode 100644 tests/Validation/cases/static4.php create mode 100644 tests/Validation/cases/static4.php.expected.json create mode 100644 tests/Validation/cases/staticInArray.php create mode 100644 tests/Validation/cases/staticInArray.php.expected.json create mode 100644 tests/Validation/cases/stringVariable.php create mode 100644 tests/Validation/cases/stringVariable.php.expected.json create mode 100644 tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php create mode 100644 tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json create mode 100644 tests/Validation/cases/verifyFqsenOnClassProperty.php create mode 100644 tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json create mode 100644 tests/Validation/disabled.json create mode 160000 validation/frameworks/cakephp create mode 160000 validation/frameworks/codeigniter create mode 160000 validation/frameworks/drupal create mode 160000 validation/frameworks/math-php create mode 160000 validation/frameworks/php-language-server create mode 160000 validation/frameworks/phpunit create mode 160000 validation/frameworks/symfony create mode 160000 validation/frameworks/tolerant-php-parser create mode 160000 validation/frameworks/wordpress diff --git a/.gitignore b/.gitignore index 4791ea2..e7997bf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ vendor/ .phpls/ composer.lock stubs +*.ast \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..06d05cb --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/Performance.php b/Performance.php new file mode 100644 index 0000000..5022f84 --- /dev/null +++ b/Performance.php @@ -0,0 +1,65 @@ +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; +} + diff --git a/composer.json b/composer.json index 1f23bca..93dec97 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,6 @@ }, "require": { "php": ">=7.0", - "nikic/php-parser": "^3.0.5", "phpdocumentor/reflection-docblock": "^3.0", "sabre/event": "^5.0", "felixfbecker/advanced-json-rpc": "^2.0", @@ -38,7 +37,8 @@ "webmozart/glob": "^4.1", "sabre/uri": "^2.0", "jetbrains/phpstorm-stubs": "dev-master", - "composer/composer": "^1.3" + "composer/composer": "^1.3", + "Microsoft/tolerant-php-parser": "^0.0.2" }, "minimum-stability": "dev", "prefer-stable": true, @@ -47,7 +47,9 @@ "LanguageServer\\": "src/" }, "files" : [ - "src/utils.php" + "src/utils.php", + "src/FqnUtilities.php", + "src/ParserHelpers.php" ] }, "autoload-dev": { diff --git a/phpcs.xml.dist b/phpcs.xml.dist index e67104b..f65f4fc 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -2,6 +2,7 @@ src tests + tests/Validation/cases diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6cd8787..38df71a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,6 +12,6 @@ - + diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 99a5d57..160798d 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -3,7 +3,6 @@ declare(strict_types = 1); namespace LanguageServer; -use PhpParser\Node; use LanguageServer\Index\ReadableIndex; use LanguageServer\Protocol\{ TextEdit, @@ -13,6 +12,8 @@ use LanguageServer\Protocol\{ CompletionItem, CompletionItemKind }; +use Microsoft\PhpParser; +use Microsoft\PhpParser\Node; class CompletionProvider { @@ -104,7 +105,7 @@ class CompletionProvider /** * @param DefinitionResolver $definitionResolver - * @param ReadableIndex $index + * @param ReadableIndex $index */ public function __construct(DefinitionResolver $definitionResolver, ReadableIndex $index) { @@ -121,45 +122,74 @@ class CompletionProvider */ 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); - if ($node instanceof Node\Expr\Error) { - $node = $node->getAttribute('parentNode'); + $offset = $node === null ? -1 : $pos->toOffset($node->getFileContents()); + if ( + $node !== null + && $offset > $node->getEndPosition() + && $node->parent !== null + && $node->parent->getLastChild() instanceof PhpParser\MissingToken + ) { + $node = $node->parent; } $list = new CompletionList; $list->isIncomplete = true; - // A non-free node means we do NOT suggest global symbols - if ( - $node instanceof Node\Expr\MethodCall - || $node instanceof Node\Expr\PropertyFetch - || $node instanceof Node\Expr\StaticCall - || $node instanceof Node\Expr\StaticPropertyFetch - || $node instanceof Node\Expr\ClassConstFetch + if ($node instanceof Node\Expression\Variable && + $node->parent instanceof Node\Expression\ObjectCreationExpression && + $node->name instanceof PhpParser\MissingToken ) { - // If the name is an Error node, just filter by the class - if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) { - // For instances, resolve the variable type - $prefixes = DefinitionResolver::getFqnsFromType( - $this->definitionResolver->resolveExpressionNodeToType($node->var) + $node = $node->parent; + } + + if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) { + $item = new CompletionItem('textEdit = new TextEdit( + new Range($pos, $pos), + stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), '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 { - // Static member reference - $prefixes = [$node->class instanceof Node\Name ? (string)$node->class : '']; + $list->items[] = $item; } + } /* + + MEMBER ACCESS EXPRESSIONS + $a->c# + $a-># */ + elseif ($node instanceof Node\Expression\MemberAccessExpression) { + $prefixes = FqnUtilities\getFqnsFromType( + $this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression) + ); $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) { - if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) { - $prefix .= '->'; - } else if ($node instanceof Node\Expr\StaticCall || $node instanceof Node\Expr\ClassConstFetch) { - $prefix .= '::'; - } else if ($node instanceof Node\Expr\StaticPropertyFetch) { - $prefix .= '::$'; - } + $prefix .= '->'; } + unset($prefix); 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) - || $node instanceof Node\Expr\New_ + } /* + + SCOPED PROPERTY ACCESS EXPRESSIONS + 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 = ''; - $prefixLen = 0; - if ($node instanceof Node\Name) { - $isFullyQualified = $node->isFullyQualified(); - $prefix = (string)$node; - $prefixLen = strlen($prefix); - $namespacedPrefix = (string)$node->getAttribute('namespacedName'); - $namespacedPrefixLen = strlen($prefix); + $prefixes = FqnUtilities\getFqnsFromType( + $classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier) + ); + + $prefixes = $this->expandParentFqns($prefixes); + + foreach ($prefixes as &$prefix) { + $prefix .= '::'; } - // Find closest namespace - $namespace = getClosestNode($node, Node\Stmt\Namespace_::class); - /** Map from alias to Definition */ - $aliasedDefs = []; - if ($namespace) { - foreach ($namespace->stmts as $stmt) { - 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) { + + unset($prefix); + + foreach ($this->index->getDefinitions() as $fqn => $def) { + foreach ($prefixes as $prefix) { + if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && !$def->isGlobal) { $list->items[] = CompletionItem::fromDefinition($def); } } } - // Additionally, suggest global symbols that either - // - start with the current namespace + prefix, if the Name node is not fully qualified - // - start with just the prefix, if the Name node is fully qualified + } elseif (ParserHelpers\isConstantFetch($node) || + ($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression || + (($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) { - if ( - $def->isGlobal // exclude methods, properties etc. - && ( - !$prefix - || ( - ((!$namespace || $isFullyQualified) && substr($fqn, 0, $prefixLen) === $prefix) - || ( - $namespace - && !$isFullyQualified - && substr($fqn, 0, $namespacedPrefixLen) === $namespacedPrefix - ) - ) - ) - // Only suggest classes for `new` - && (!($node instanceof Node\Expr\New_) || $def->canBeInstantiated) - ) { - $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 - $item->insertText = $alias; - } else if ($namespace && !($prefix && $isFullyQualified)) { - // Insert the global FQN with trailing backslash - $item->insertText = '\\' . $fqn; - } else { - // Insert the FQN without trailing backlash - $item->insertText = $fqn; + $fqnStartsWithPrefix = substr($fqn, 0, strlen($prefix)) === $prefix; + $fqnContainsPrefix = empty($prefix) || strpos($fqn, $prefix) !== false; + if (($def->canBeInstantiated || ($def->isGlobal && !isset($creation))) && $fqnContainsPrefix) { + if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) { + $namespacePrefix = (string)PhpParser\ResolvedName::buildName($namespaceDefinition->name->nameParts, $node->getFileContents()); + + $isAliased = false; + + $isNotFullyQualified = !($class instanceof Node\QualifiedName) || !$class->isFullyQualifiedName(); + if ($isNotFullyQualified) { + foreach ($namespaceImportTable as $alias => $name) { + if (substr($fqn, 0, strlen($name)) === $name) { + $fqn = $alias; + $isAliased = true; + break; + } + } + } + + $prefixWithNamespace = $namespacePrefix . "\\" . $prefix; + $fqnMatchesPrefixWithNamespace = substr($fqn, 0, strlen($prefixWithNamespace)) === $prefixWithNamespace; + $isFullyQualifiedAndPrefixMatches = !$isNotFullyQualified && ($fqnStartsWithPrefix || $fqnMatchesPrefixWithNamespace); + if (!$isFullyQualifiedAndPrefixMatches && !$isAliased) { + if (!array_search($fqn, array_values($namespaceImportTable))) { + if (empty($prefix)) { + $fqn = '\\' . $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; } } - // Suggest keywords - if ($node instanceof Node\Name && $node->getAttribute('parentNode') instanceof Node\Expr\ConstFetch) { + + if (!isset($creation)) { foreach (self::KEYWORDS as $keyword) { - if (substr($keyword, 0, $prefixLen) === $prefix) { - $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); - $item->insertText = $keyword . ' '; - $list->items[] = $item; - } + $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); + $item->insertText = $keyword . ' '; + $list->items[] = $item; } } - } else if ( - $node instanceof Node\Expr\Variable - || ($node && $node->getAttribute('parentNode') instanceof Node\Expr\Variable) - ) { - // Find variables, parameters and use statements in the scope - // 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) - ); + } elseif (ParserHelpers\isConstantFetch($node)) { + $prefix = (string) ($node->getResolvedName() ?? PhpParser\ResolvedName::buildName($node->nameParts, $node->getFileContents())); + foreach (self::KEYWORDS as $keyword) { + $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); + $item->insertText = $keyword . ' '; $list->items[] = $item; } - } else if ($node instanceof Node\Stmt\InlineHTML || $pos == new Position(0, 0)) { - $item = new CompletionItem('textEdit = new TextEdit( - new Range($pos, $pos), - stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), 'items[] = $item; } return $list; @@ -302,7 +324,7 @@ class CompletionProvider foreach ($fqns as $fqn) { $def = $this->index->getDefinition($fqn); if ($def) { - foreach ($this->expandParentFqns($def->extends) as $parent) { + foreach ($this->expandParentFqns($def->extends ?? []) as $parent) { $expanded[] = $parent; } } @@ -335,30 +357,34 @@ class CompletionProvider // Walk the AST upwards until a scope boundary is met $level = $node; - while ($level && !($level instanceof Node\FunctionLike)) { + while ($level && !ParserHelpers\isFunctionLike($level)) { // Walk siblings before the node $sibling = $level; - while ($sibling = $sibling->getAttribute('previousSibling')) { + while ($sibling = $sibling->getPreviousSibling()) { // Collect all variables inside the sibling node 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, // also add its parameters and closure uses to the result list - if ($level instanceof Node\FunctionLike) { - foreach ($level->params as $param) { - if (!isset($vars[$param->name]) && substr($param->name, 0, strlen($namePrefix)) === $namePrefix) { - $vars[$param->name] = $param; + if ($level && ParserHelpers\isFunctionLike($level) && $level->parameters !== null) { + foreach ($level->parameters->getValues() as $param) { + $paramName = $param->getName(); + if (empty($namePrefix) || strpos($paramName, $namePrefix) !== false) { + $vars[$paramName] = $param; } } - if ($level instanceof Node\Expr\Closure) { - foreach ($level->uses as $use) { - if (!isset($vars[$use->var]) && substr($use->var, 0, strlen($namePrefix)) === $namePrefix) { - $vars[$use->var] = $use; + + if ($level instanceof Node\Expression\AnonymousFunctionCreationExpression && $level->anonymousFunctionUseClause !== null && + $level->anonymousFunctionUseClause->useVariableNameList !== null) { + 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 string $namePrefix Prefix to filter - * @return Node\Expr\Variable[] + * @return Node\Expression\Variable[] */ private function findVariableDefinitionsInNode(Node $node, string $namePrefix = ''): array { $vars = []; // If the child node is a variable assignment, save it - $parent = $node->getAttribute('parentNode'); - if ( - $node instanceof Node\Expr\Variable - && ($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 - ) { - $vars[] = $node; - } - // Iterate over subnodes - foreach ($node->getSubNodeNames() as $attr) { - if (!isset($node->$attr)) { - continue; - } - $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; + + $isAssignmentToVariable = function ($node) { + return $node instanceof Node\Expression\AssignmentExpression; + }; + + if ($this->isAssignmentToVariableWithPrefix($node, $namePrefix)) { + $vars[] = $node->leftOperand; + } else { + // Get all descendent variables, then filter to ones that start with $namePrefix. + // Avoiding closure usage in tight loop + foreach ($node->getDescendantNodes($isAssignmentToVariable) as $descendantNode) { + if ($this->isAssignmentToVariableWithPrefix($descendantNode, $namePrefix)) { + $vars[] = $descendantNode->leftOperand; } } } + 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); + } } diff --git a/src/ComposerScripts.php b/src/ComposerScripts.php index c84a176..487c39d 100644 --- a/src/ComposerScripts.php +++ b/src/ComposerScripts.php @@ -10,6 +10,7 @@ use phpDocumentor\Reflection\DocBlockFactory; use Webmozart\PathUtil\Path; use Sabre\Uri; use function Sabre\Event\coroutine; +use Microsoft\PhpParser; foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) { if (file_exists($file)) { @@ -29,7 +30,7 @@ class ComposerScripts $finder = new FileSystemFilesFinder; $contentRetriever = new FileSystemContentRetriever; $docBlockFactory = DocBlockFactory::createInstance(); - $parser = new Parser; + $parser = new PhpParser\Parser(); $definitionResolver = new DefinitionResolver($index); $stubsLocation = null; diff --git a/src/Definition.php b/src/Definition.php index d4b59cb..e302f71 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -3,7 +3,6 @@ declare(strict_types = 1); namespace LanguageServer; -use PhpParser\Node; use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver}; use LanguageServer\Protocol\SymbolInformation; use Exception; diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 5a6f88b..0570ed1 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -3,28 +3,36 @@ declare(strict_types = 1); namespace LanguageServer; -use PhpParser\Node; -use PhpParser\PrettyPrinter\Standard as PrettyPrinter; -use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver}; -use LanguageServer\Protocol\SymbolInformation; use LanguageServer\Index\ReadableIndex; +use LanguageServer\Protocol\SymbolInformation; +use Microsoft\PhpParser; +use Microsoft\PhpParser\Node; +use phpDocumentor\Reflection\{ + DocBlock, DocBlockFactory, Fqsen, Type, TypeResolver, Types +}; class DefinitionResolver { /** + * The current project index (for retrieving existing definitions) + * * @var \LanguageServer\Index\ReadableIndex */ private $index; /** + * Resolves strings to a type object. + * * @var \phpDocumentor\Reflection\TypeResolver */ private $typeResolver; /** - * @var \PhpParser\PrettyPrinterAbstract + * Parses Doc Block comments given the DocBlock text and import tables at a position. + * + * @var DocBlockFactory */ - private $prettyPrinter; + private $docBlockFactory; /** * @param ReadableIndex $index @@ -33,35 +41,43 @@ class DefinitionResolver { $this->index = $index; $this->typeResolver = new TypeResolver; - $this->prettyPrinter = new PrettyPrinter; + $this->docBlockFactory = DocBlockFactory::createInstance(); } /** - * Builds the declaration line for a given node + * Builds the declaration line for a given node. Declarations with multiple lines are trimmed. * * @param Node $node * @return string */ - public function getDeclarationLineFromNode(Node $node): string + public function getDeclarationLineFromNode($node): string { - if ($node instanceof Node\Stmt\PropertyProperty || $node instanceof Node\Const_) { - // Properties and constants can have multiple declarations - // Use the parent node (that includes the modifiers), but only render the requested declaration - $child = $node; - /** @var Node */ - $node = $node->getAttribute('parentNode'); - $defLine = clone $node; - $defLine->props = [$child]; + // If node is part of a declaration list, build a declaration line that discludes other elements in the list + // - [PropertyDeclaration] // public $a, [$b = 3], $c; => public $b = 3; + // - [ConstDeclaration | ClassConstDeclaration] // "const A = 3, [B = 4];" => "const B = 4;" + if ( + ($declaration = ParserHelpers\tryGetPropertyDeclaration($node)) && ($elements = $declaration->propertyElements) || + ($declaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) && ($elements = $declaration->constElements) + ) { + $defLine = $declaration->getText(); + $defLineStart = $declaration->getStart(); + + $defLine = \substr_replace( + $defLine, + $node->getFullText(), + $elements->getFullStart() - $defLineStart, + $elements->getFullWidth() + ); } else { - $defLine = clone $node; + $defLine = $node->getText(); } - // Don't include the docblock in the declaration string - $defLine->setAttribute('comments', []); - if (isset($defLine->stmts)) { - $defLine->stmts = []; - } - $defText = $this->prettyPrinter->prettyPrint([$defLine]); - return strstr($defText, "\n", true) ?: $defText; + + // Trim string to only include first line + $defLine = \rtrim(\strtok($defLine, "\n"), "\r"); + + // TODO - pretty print rather than getting text + + return $defLine; } /** @@ -70,35 +86,79 @@ class DefinitionResolver * @param Node $node * @return string|null */ - public function getDocumentationFromNode(Node $node) + public function getDocumentationFromNode($node) { - if ($node instanceof Node\Stmt\PropertyProperty || $node instanceof Node\Const_) { - $node = $node->getAttribute('parentNode'); + // Any NamespaceDefinition comments likely apply to the file, not the declaration itself. + if ($node instanceof Node\Statement\NamespaceDefinition) { + return null; } - if ($node instanceof Node\Param) { - $fn = $node->getAttribute('parentNode'); - $docBlock = $fn->getAttribute('docBlock'); - if ($docBlock !== null) { - $tags = $docBlock->getTagsByName('param'); - foreach ($tags as $tag) { - if ($tag->getVariableName() === $node->name) { - return $tag->getDescription()->render(); - } - } - } - } else { - $docBlock = $node->getAttribute('docBlock'); - if ($docBlock !== null) { - // check wether we have a description, when true, add a new paragraph - // with the description - $description = $docBlock->getDescription()->render(); - if (empty($description)) { - return $docBlock->getSummary(); - } - return $docBlock->getSummary() . "\n\n" . $description; + // For properties and constants, set the node to the declaration node, rather than the individual property. + // This is because they get defined as part of a list. + $constOrPropertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node) ?? ParserHelpers\tryGetConstOrClassConstDeclaration($node); + if ($constOrPropertyDeclaration !== null) { + $node = $constOrPropertyDeclaration; + } + + // For parameters, parse the function-like declaration to get documentation for a parameter + if ($node instanceof Node\Parameter) { + $variableName = $node->getName(); + + $functionLikeDeclaration = ParserHelpers\getFunctionLikeDeclarationFromParameter($node); + $docBlock = $this->getDocBlock($functionLikeDeclaration); + + $parameterDocBlockTag = $this->tryGetDocBlockTagForParameter($docBlock, $variableName); + return $parameterDocBlockTag !== null ? $parameterDocBlockTag->getDescription()->render() : null; + } + + // For everything else, get the doc block summary corresponding to the current node. + $docBlock = $this->getDocBlock($node); + if ($docBlock !== null) { + // check wether we have a description, when true, add a new paragraph + // with the description + $description = $docBlock->getDescription()->render(); + + if (empty($description)) { + return $docBlock->getSummary(); + } + + return $docBlock->getSummary() . "\n\n" . $description; + } + return null; + } + + /** + * Gets Doc Block with resolved names for a Node + * + * @param Node $node + * @return DocBlock | null + */ + private function getDocBlock(Node $node) + { + // TODO make more efficient (caching, ensure import table is in right format to begin with) + $docCommentText = $node->getDocCommentText(); + if ($docCommentText !== null) { + list($namespaceImportTable,,) = $node->getImportTablesForCurrentScope(); + foreach ($namespaceImportTable as $alias => $name) { + $namespaceImportTable[$alias] = (string)$name; + } + $namespaceDefinition = $node->getNamespaceDefinition(); + if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) { + $namespaceName = (string)$namespaceDefinition->name->getNamespacedName(); + } else { + $namespaceName = 'global'; + } + $context = new Types\Context($namespaceName, $namespaceImportTable); + + try { + // create() throws when it thinks the doc comment has invalid fields. + // For example, a @see tag that is followed by something that doesn't look like a valid fqsen will throw. + return $this->docBlockFactory->create($docCommentText, $context); + } catch (\InvalidArgumentException $e) { + return null; } } + return null; } /** @@ -110,35 +170,56 @@ class DefinitionResolver */ public function createDefinitionFromNode(Node $node, string $fqn = null): Definition { - $parent = $node->getAttribute('parentNode'); $def = new Definition; - $def->canBeInstantiated = $node instanceof Node\Stmt\Class_; - $def->isGlobal = ( - $node instanceof Node\Stmt\ClassLike - || ($node instanceof Node\Name && $parent instanceof Node\Stmt\Namespace_) - || $node instanceof Node\Stmt\Function_ - || $parent instanceof Node\Stmt\Const_ - ); - $def->isStatic = ( - ($node instanceof Node\Stmt\ClassMethod && $node->isStatic()) - || ($node instanceof Node\Stmt\PropertyProperty && $parent->isStatic()) - ); $def->fqn = $fqn; - if ($node instanceof Node\Stmt\Class_) { + + // Determines whether the suggestion will show after "new" + $def->canBeInstantiated = $node instanceof Node\Statement\ClassDeclaration; + + // Interfaces, classes, traits, namespaces, functions, and global const elements + $def->isGlobal = ( + $node instanceof Node\Statement\InterfaceDeclaration || + $node instanceof Node\Statement\ClassDeclaration || + $node instanceof Node\Statement\TraitDeclaration || + + ($node instanceof Node\Statement\NamespaceDefinition && $node->name !== null) || + + $node instanceof Node\Statement\FunctionDeclaration || + + ($node instanceof Node\ConstElement && $node->parent->parent instanceof Node\Statement\ConstDeclaration) + ); + + // Static methods and static property declarations + $def->isStatic = ( + ($node instanceof Node\MethodDeclaration && $node->isStatic()) || + + (($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null + && $propertyDeclaration->isStatic()) + ); + + if ($node instanceof Node\Statement\ClassDeclaration && + // TODO - this should be better represented in the parser API + $node->classBaseClause !== null && $node->classBaseClause->baseClass !== null) { + $def->extends = [(string)$node->classBaseClause->baseClass->getResolvedName()]; + } elseif ( + $node instanceof Node\Statement\InterfaceDeclaration && + // TODO - this should be better represented in the parser API + $node->interfaceBaseClause !== null && $node->interfaceBaseClause->interfaceNameList !== null + ) { $def->extends = []; - if ($node->extends) { - $def->extends[] = (string)$node->extends; - } - } else if ($node instanceof Node\Stmt\Interface_) { - $def->extends = []; - foreach ($node->extends as $n) { - $def->extends[] = (string)$n; + foreach ($node->interfaceBaseClause->interfaceNameList->getValues() as $n) { + $def->extends[] = (string)$n->getResolvedName(); } } + $def->symbolInformation = SymbolInformation::fromNode($node, $fqn); - $def->type = $this->getTypeFromNode($node); - $def->declarationLine = $this->getDeclarationLineFromNode($node); - $def->documentation = $this->getDocumentationFromNode($node); + + if ($def->symbolInformation !== null) { + $def->type = $this->getTypeFromNode($node); + $def->declarationLine = $this->getDeclarationLineFromNode($node); + $def->documentation = $this->getDocumentationFromNode($node); + } + return $def; } @@ -150,14 +231,19 @@ class DefinitionResolver */ public function resolveReferenceNodeToDefinition(Node $node) { - // Variables are not indexed globally, as they stay in the file scope anyway - if ($node instanceof Node\Expr\Variable) { - // Resolve $this - if ($node->name === 'this' && $fqn = $this->getContainingClassFqn($node)) { + $parent = $node->parent; + // Variables are not indexed globally, as they stay in the file scope anyway. + // Ignore variable nodes that are part of ScopedPropertyAccessExpression, + // as the scoped property access expression node is handled separately. + if ($node instanceof Node\Expression\Variable && + !($parent instanceof Node\Expression\ScopedPropertyAccessExpression)) { + // Resolve $this to the containing class definition. + if ($node->getName() === 'this' && $fqn = $this->getContainingClassFqn($node)) { return $this->index->getDefinition($fqn, false); } + // Resolve the variable to a definition node (assignment, param or closure use) - $defNode = self::resolveVariableToNode($node); + $defNode = $this->resolveVariableToNode($node); if ($defNode === null) { return null; } @@ -171,37 +257,12 @@ class DefinitionResolver } // If the node is a function or constant, it could be namespaced, but PHP falls back to global // http://php.net/manual/en/language.namespaces.fallback.php - $parent = $node->getAttribute('parentNode'); - $globalFallback = $parent instanceof Node\Expr\ConstFetch || $parent instanceof Node\Expr\FuncCall; + // TODO - verify that this is not a method + $globalFallback = ParserHelpers\isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression; // Return the Definition object from the index index return $this->index->getDefinition($fqn, $globalFallback); } - /** - * Returns all possible FQNs in a type - * - * @param Type $type - * @return string[] - */ - public static function getFqnsFromType(Type $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 (self::getFqnsFromType($type) as $fqn) { - $fqns[] = $fqn; - } - } - } - return $fqns; - } - /** * Given any node, returns the FQN of the symbol that is referenced * Returns null if the FQN could not be resolved or the reference node references a variable @@ -211,141 +272,188 @@ class DefinitionResolver */ public function resolveReferenceNodeToFqn(Node $node) { - $parent = $node->getAttribute('parentNode'); - - if ( - $node instanceof Node\Name && ( - $parent instanceof Node\Stmt\ClassLike - || $parent instanceof Node\Param - || $parent instanceof Node\FunctionLike - || $parent instanceof Node\Stmt\GroupUse - || $parent instanceof Node\Expr\New_ - || $parent instanceof Node\Expr\StaticCall - || $parent instanceof Node\Expr\ClassConstFetch - || $parent instanceof Node\Expr\StaticPropertyFetch - || $parent instanceof Node\Expr\Instanceof_ - ) - ) { - // For extends, implements, type hints and classes of classes of static calls use the name directly - $name = (string)$node; - // Only the name node should be considered a reference, not the UseUse node itself - } else if ($parent instanceof Node\Stmt\UseUse) { - $name = (string)$parent->name; - $grandParent = $parent->getAttribute('parentNode'); - if ($grandParent instanceof Node\Stmt\GroupUse) { - $name = $grandParent->prefix . '\\' . $name; - } else if ($grandParent instanceof Node\Stmt\Use_ && $grandParent->type === Node\Stmt\Use_::TYPE_FUNCTION) { - $name .= '()'; - } - } else if ($node instanceof Node\Expr\MethodCall || $node instanceof Node\Expr\PropertyFetch) { - if ($node->name instanceof Node\Expr) { - // Cannot get definition if right-hand side is expression - return null; - } - // Get the type of the left-hand expression - $varType = $this->resolveExpressionNodeToType($node->var); - if ($varType instanceof Types\Compound) { - // For compound types, use the first FQN we find - // (popular use case is ClassName|null) - for ($i = 0; $t = $varType->get($i); $i++) { - if ( - $t instanceof Types\This - || $t instanceof Types\Object_ - || $t instanceof Types\Static_ - || $t instanceof Types\Self_ - ) { - $varType = $t; - break; - } - } - } - if ( - $varType instanceof Types\This - || $varType instanceof Types\Static_ - || $varType instanceof Types\Self_ - ) { - // $this/static/self is resolved to the containing class - $classFqn = self::getContainingClassFqn($node); - } else if (!($varType instanceof Types\Object_) || $varType->getFqsen() === null) { - // Left-hand expression could not be resolved to a class - return null; - } else { - $classFqn = substr((string)$varType->getFqsen(), 1); - } - $memberSuffix = '->' . (string)$node->name; - if ($node instanceof Node\Expr\MethodCall) { - $memberSuffix .= '()'; - } - // Find the right class that implements the member - $implementorFqns = [$classFqn]; - while ($implementorFqn = array_shift($implementorFqns)) { - // If the member FQN exists, return it - if ($this->index->getDefinition($implementorFqn . $memberSuffix)) { - return $implementorFqn . $memberSuffix; - } - // Get Definition of implementor class - $implementorDef = $this->index->getDefinition($implementorFqn); - // If it doesn't exist, return the initial guess - if ($implementorDef === null) { - break; - } - // Repeat for parent class - if ($implementorDef->extends) { - foreach ($implementorDef->extends as $extends) { - $implementorFqns[] = $extends; - } - } - } - return $classFqn . $memberSuffix; - } else if ($parent instanceof Node\Expr\FuncCall && $node instanceof Node\Name) { - if ($parent->name instanceof Node\Expr) { - return null; - } - $name = (string)($node->getAttribute('namespacedName') ?? $parent->name); - } else if ($parent instanceof Node\Expr\ConstFetch && $node instanceof Node\Name) { - $name = (string)($node->getAttribute('namespacedName') ?? $parent->name); + // TODO all name tokens should be a part of a node + if ($node instanceof Node\QualifiedName) { + return $this->resolveQualifiedNameNodeToFqn($node); + } else if ($node instanceof Node\Expression\MemberAccessExpression) { + return $this->resolveMemberAccessExpressionNodeToFqn($node); + } else if (ParserHelpers\isConstantFetch($node)) { + return (string)($node->getNamespacedName()); } else if ( - $node instanceof Node\Expr\ClassConstFetch - || $node instanceof Node\Expr\StaticPropertyFetch - || $node instanceof Node\Expr\StaticCall + // A\B::C - constant access expression + $node instanceof Node\Expression\ScopedPropertyAccessExpression + && !($node->memberName instanceof Node\Expression\Variable) ) { - if ($node->class instanceof Node\Expr || $node->name instanceof Node\Expr) { - // Cannot get definition of dynamic names - return null; - } - $className = (string)$node->class; - if ($className === 'self' || $className === 'static' || $className === 'parent') { - // self and static are resolved to the containing class - $classNode = getClosestNode($node, Node\Stmt\Class_::class); - if ($classNode === null) { + return $this->resolveScopedPropertyAccessExpressionNodeToFqn($node); + } else if ( + // A\B::$c - static property access expression + $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression + ) { + return $this->resolveScopedPropertyAccessExpressionNodeToFqn($node->parent); + } + + return null; + } + + private function resolveQualifiedNameNodeToFqn(Node\QualifiedName $node) + { + $parent = $node->parent; + + if ($parent instanceof Node\TraitSelectOrAliasClause) { + return null; + } + // Add use clause references + if (($useClause = $parent) instanceof Node\NamespaceUseGroupClause + || $useClause instanceof Node\NamespaceUseClause + ) { + $contents = $node->getFileContents(); + if ($useClause instanceof Node\NamespaceUseGroupClause) { + $prefix = $useClause->parent->parent->namespaceName; + if ($prefix === null) { return null; } - if ($className === 'parent') { - // parent is resolved to the parent class - if (!isset($n->extends)) { - return null; + $name = PhpParser\ResolvedName::buildName($prefix->nameParts, $contents); + $name->addNameParts($node->nameParts, $contents); + $name = (string)$name; + + if ($useClause->functionOrConst === null) { + $useClause = $node->getFirstAncestor(Node\Statement\NamespaceUseDeclaration::class); + if ($useClause->functionOrConst !== null && $useClause->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword) { + $name .= '()'; } - $className = (string)$classNode->extends; - } else { - $className = (string)$classNode->namespacedName; + } + return $name; + } else { + $name = (string) PhpParser\ResolvedName::buildName($node->nameParts, $contents); + if ($useClause->groupClauses === null && $useClause->parent->parent->functionOrConst !== null && $useClause->parent->parent->functionOrConst->kind === PhpParser\TokenKind::FunctionKeyword) { + $name .= '()'; } } - if ($node instanceof Node\Expr\StaticPropertyFetch) { - $name = (string)$className . '::$' . $node->name; - } else { - $name = (string)$className . '::' . $node->name; + + return $name; + } + + // For extends, implements, type hints and classes of classes of static calls use the name directly + $name = (string) ($node->getResolvedName() ?? $node->getNamespacedName()); + + if ($node->parent instanceof Node\Expression\CallExpression) { + $name .= '()'; + } + return $name; + } + + private function resolveMemberAccessExpressionNodeToFqn(Node\Expression\MemberAccessExpression $access) + { + if ($access->memberName instanceof Node\Expression) { + // Cannot get definition if right-hand side is expression + return null; + } + // Get the type of the left-hand expression + $varType = $this->resolveExpressionNodeToType($access->dereferencableExpression); + + if ($varType instanceof Types\Compound) { + // For compound types, use the first FQN we find + // (popular use case is ClassName|null) + for ($i = 0; $t = $varType->get($i); $i++) { + if ( + $t instanceof Types\This + || $t instanceof Types\Object_ + || $t instanceof Types\Static_ + || $t instanceof Types\Self_ + ) { + $varType = $t; + break; + } } + } + if ( + $varType instanceof Types\This + || $varType instanceof Types\Static_ + || $varType instanceof Types\Self_ + ) { + // $this/static/self is resolved to the containing class + $classFqn = self::getContainingClassFqn($access); + } else if (!($varType instanceof Types\Object_) || $varType->getFqsen() === null) { + // Left-hand expression could not be resolved to a class + return null; + } else { + $classFqn = substr((string)$varType->getFqsen(), 1); + } + $memberSuffix = '->' . (string)($access->memberName->getText() ?? $access->memberName->getText($access->getFileContents())); + if ($access->parent instanceof Node\Expression\CallExpression) { + $memberSuffix .= '()'; + } + + // Find the right class that implements the member + $implementorFqns = [$classFqn]; + + while ($implementorFqn = array_shift($implementorFqns)) { + // If the member FQN exists, return it + if ($this->index->getDefinition($implementorFqn . $memberSuffix)) { + return $implementorFqn . $memberSuffix; + } + // Get Definition of implementor class + $implementorDef = $this->index->getDefinition($implementorFqn); + // If it doesn't exist, return the initial guess + if ($implementorDef === null) { + break; + } + // Repeat for parent class + if ($implementorDef->extends) { + foreach ($implementorDef->extends as $extends) { + $implementorFqns[] = $extends; + } + } + } + + return $classFqn . $memberSuffix; + } + + private function resolveScopedPropertyAccessExpressionNodeToFqn(Node\Expression\ScopedPropertyAccessExpression $scoped) + { + if ($scoped->scopeResolutionQualifier instanceof Node\Expression\Variable) { + $varType = $this->getTypeFromNode($scoped->scopeResolutionQualifier); + if ($varType === null) { + return null; + } + $className = substr((string)$varType->getFqsen(), 1); + } elseif ($scoped->scopeResolutionQualifier instanceof Node\QualifiedName) { + $className = (string)$scoped->scopeResolutionQualifier->getResolvedName(); } else { return null; } - if (!isset($name)) { - return null; + + if ($className === 'self' || $className === 'static' || $className === 'parent') { + // self and static are resolved to the containing class + $classNode = $scoped->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if ($classNode === null) { + return null; + } + if ($className === 'parent') { + // parent is resolved to the parent class + if (!isset($classNode->extends)) { + return null; + } + $className = (string)$classNode->extends->getResolvedName(); + } else { + $className = (string)$classNode->getNamespacedName(); + } + } elseif ($scoped->scopeResolutionQualifier instanceof Node\QualifiedName) { + $className = $scoped->scopeResolutionQualifier->getResolvedName(); } - if ( - $node instanceof Node\Expr\MethodCall - || $node instanceof Node\Expr\StaticCall - || $parent instanceof Node\Expr\FuncCall - ) { + if ($scoped->memberName instanceof Node\Expression\Variable) { + if ($scoped->parent instanceof Node\Expression\CallExpression) { + return null; + } + $memberName = $scoped->memberName->getName(); + if (empty($memberName)) { + return null; + } + $name = (string)$className . '::$' . $memberName; + } else { + $name = (string)$className . '::' . $scoped->memberName->getText($scoped->getFileContents()); + } + if ($scoped->parent instanceof Node\Expression\CallExpression) { $name .= '()'; } return $name; @@ -360,44 +468,49 @@ class DefinitionResolver */ private static function getContainingClassFqn(Node $node) { - $classNode = getClosestNode($node, Node\Stmt\Class_::class); - if ($classNode === null || $classNode->isAnonymous()) { + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if ($classNode === null) { return null; } - return (string)$classNode->namespacedName; + return (string)$classNode->getNamespacedName(); } /** * Returns the assignment or parameter node where a variable was defined * - * @param Node\Expr\Variable|Node\Expr\ClosureUse $var The variable access - * @return Node\Expr\Assign|Node\Expr\AssignOp|Node\Param|Node\Expr\ClosureUse|null + * @param Node\Expression\Variable | Node\Expression\ClosureUse $var The variable access + * @return Node\Expression\Assign | Node\Expression\AssignOp|Node\Param | Node\Expression\ClosureUse|null */ - public static function resolveVariableToNode(Node\Expr $var) + public function resolveVariableToNode($var) { $n = $var; - // When a use is passed, start outside the closure to not return immediatly - if ($var instanceof Node\Expr\ClosureUse) { - $n = $var->getAttribute('parentNode')->getAttribute('parentNode'); - $name = $var->var; - } else if ($var instanceof Node\Expr\Variable || $var instanceof Node\Param) { - $name = $var->name; + // When a use is passed, start outside the closure to not return immediately + // Use variable vs variable parsing? + if ($var instanceof Node\UseVariableName) { + $n = $var->getFirstAncestor(Node\Expression\AnonymousFunctionCreationExpression::class)->parent; + $name = $var->getName(); + } else if ($var instanceof Node\Expression\Variable || $var instanceof Node\Parameter) { + $name = $var->getName(); } else { throw new \InvalidArgumentException('$var must be Variable, Param or ClosureUse, not ' . get_class($var)); } // Traverse the AST up do { // If a function is met, check the parameters and use statements - if ($n instanceof Node\FunctionLike) { - foreach ($n->getParams() as $param) { - if ($param->name === $name) { - return $param; + if (ParserHelpers\isFunctionLike($n)) { + if ($n->parameters !== null) { + foreach ($n->parameters->getElements() as $param) { + if ($param->getName() === $name) { + return $param; + } } } // If it is a closure, also check use statements - if ($n instanceof Node\Expr\Closure) { - foreach ($n->uses as $use) { - if ($use->var === $name) { + if ($n instanceof Node\Expression\AnonymousFunctionCreationExpression && + $n->anonymousFunctionUseClause !== null && + $n->anonymousFunctionUseClause->useVariableNameList !== null) { + foreach ($n->anonymousFunctionUseClause->useVariableNameList->getElements() as $use) { + if ($use->getName() === $name) { return $use; } } @@ -405,15 +518,19 @@ class DefinitionResolver break; } // Check each previous sibling node for a variable assignment to that variable - while ($n->getAttribute('previousSibling') && $n = $n->getAttribute('previousSibling')) { + while (($prevSibling = $n->getPreviousSibling()) !== null && $n = $prevSibling) { + if ($n instanceof Node\Statement\ExpressionStatement) { + $n = $n->expression; + } if ( - ($n instanceof Node\Expr\Assign || $n instanceof Node\Expr\AssignOp) - && $n->var instanceof Node\Expr\Variable && $n->var->name === $name + // TODO - clean this up + ($n instanceof Node\Expression\AssignmentExpression && $n->operator->kind === PhpParser\TokenKind::EqualsToken) + && $n->leftOperand instanceof Node\Expression\Variable && $n->leftOperand->getName() === $name ) { return $n; } } - } while (isset($n) && $n = $n->getAttribute('parentNode')); + } while (isset($n) && $n = $n->parent); // Return null if nothing was found return null; } @@ -422,56 +539,95 @@ class DefinitionResolver * Given an expression node, resolves that expression recursively to a type. * If the type could not be resolved, returns Types\Mixed. * - * @param \PhpParser\Node\Expr $expr - * @return \phpDocumentor\Reflection\Type + * @param Node\Expression $expr + * @return \phpDocumentor\Reflection\Type|null */ - public function resolveExpressionNodeToType(Node\Expr $expr): Type + public function resolveExpressionNodeToType($expr) { - if ($expr instanceof Node\Expr\Variable || $expr instanceof Node\Expr\ClosureUse) { - if ($expr instanceof Node\Expr\Variable && $expr->name === 'this') { + // PARENTHESIZED EXPRESSION + // Retrieve inner expression from parenthesized expression + while ($expr instanceof Node\Expression\ParenthesizedExpression) { + $expr = $expr->expression; + } + + if ($expr == null || $expr instanceof PhpParser\MissingToken || $expr instanceof PhpParser\SkippedToken) { + // TODO some members are null or Missing/SkippedToken + // How do we handle this more generally? + return new Types\Mixed; + } + + // VARIABLE + // $this -> Type\this + // $myVariable -> type of corresponding assignment expression + if ($expr instanceof Node\Expression\Variable || $expr instanceof Node\UseVariableName) { + if ($expr->getName() === 'this') { return new Types\This; } - // Find variable definition + // Find variable definition (parameter or assignment expression) $defNode = $this->resolveVariableToNode($expr); - if ($defNode instanceof Node\Expr) { + if ($defNode instanceof Node\Expression\AssignmentExpression || $defNode instanceof Node\UseVariableName) { return $this->resolveExpressionNodeToType($defNode); } - if ($defNode instanceof Node\Param) { + if ($defNode instanceof Node\Parameter) { return $this->getTypeFromNode($defNode); } } - if ($expr instanceof Node\Expr\FuncCall) { + + // FUNCTION CALL + // Function calls are resolved to type corresponding to their FQN + if ($expr instanceof Node\Expression\CallExpression && + !( + $expr->callableExpression instanceof Node\Expression\ScopedPropertyAccessExpression || + $expr->callableExpression instanceof Node\Expression\MemberAccessExpression) + ) { // Find the function definition - if ($expr->name instanceof Node\Expr) { + if ($expr->callableExpression instanceof Node\Expression) { // Cannot get type for dynamic function call return new Types\Mixed; } - $fqn = (string)($expr->getAttribute('namespacedName') ?? $expr->name) . '()'; - $def = $this->index->getDefinition($fqn, true); - if ($def !== null) { - return $def->type; + + if ($expr->callableExpression instanceof Node\QualifiedName) { + $fqn = $expr->callableExpression->getResolvedName() ?? $expr->callableExpression->getNamespacedName(); + $fqn .= '()'; + $def = $this->index->getDefinition($fqn, true); + if ($def !== null) { + return $def->type; + } } } - if ($expr instanceof Node\Expr\ConstFetch) { - if (strtolower((string)$expr->name) === 'true' || strtolower((string)$expr->name) === 'false') { + + // TRUE / FALSE / NULL + // Resolve true and false reserved words to Types\Boolean + if ($expr instanceof Node\ReservedWord) { + $token = $expr->children->kind; + if ($token === PhpParser\TokenKind::TrueReservedWord || $token === PhpParser\TokenKind::FalseReservedWord) { return new Types\Boolean; } - if (strtolower((string)$expr->name) === 'null') { + + if ($token === PhpParser\TokenKind::NullReservedWord) { return new Types\Null_; } - // Resolve constant - $fqn = (string)($expr->getAttribute('namespacedName') ?? $expr->name); + } + + // CONSTANT FETCH + // Resolve constants by retrieving corresponding definition type from FQN + if (ParserHelpers\isConstantFetch($expr)) { + $fqn = (string)$expr->getNamespacedName(); $def = $this->index->getDefinition($fqn, true); if ($def !== null) { return $def->type; } } - if ($expr instanceof Node\Expr\MethodCall || $expr instanceof Node\Expr\PropertyFetch) { - if ($expr->name instanceof Node\Expr) { + + // MEMBER ACCESS EXPRESSION + if ($expr instanceof Node\Expression\MemberAccessExpression) { + if ($expr->memberName instanceof Node\Expression) { return new Types\Mixed; } - // Resolve object - $objType = $this->resolveExpressionNodeToType($expr->var); + $var = $expr->dereferencableExpression; + + // Resolve object + $objType = $this->resolveExpressionNodeToType($var); if (!($objType instanceof Types\Compound)) { $objType = new Types\Compound([$objType]); } @@ -486,8 +642,8 @@ class DefinitionResolver } else { $classFqn = substr((string)$t->getFqsen(), 1); } - $fqn = $classFqn . '->' . $expr->name; - if ($expr instanceof Node\Expr\MethodCall) { + $fqn = $classFqn . '->' . $expr->memberName->getText($expr->getFileContents()); + if ($expr->parent instanceof Node\Expression\CallExpression) { $fqn .= '()'; } $def = $this->index->getDefinition($fqn); @@ -496,146 +652,184 @@ class DefinitionResolver } } } - if ( - $expr instanceof Node\Expr\StaticCall - || $expr instanceof Node\Expr\StaticPropertyFetch - || $expr instanceof Node\Expr\ClassConstFetch - ) { - $classType = self::resolveClassNameToType($expr->class); - if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null || $expr->name instanceof Node\Expr) { + + // SCOPED PROPERTY ACCESS EXPRESSION + if ($expr instanceof Node\Expression\ScopedPropertyAccessExpression) { + $classType = $this->resolveClassNameToType($expr->scopeResolutionQualifier); + if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null) { return new Types\Mixed; } $fqn = substr((string)$classType->getFqsen(), 1) . '::'; - if ($expr instanceof Node\Expr\StaticPropertyFetch) { - $fqn .= '$'; - } - $fqn .= $expr->name; - if ($expr instanceof Node\Expr\StaticCall) { + + // TODO is there a cleaner way to do this? + $fqn .= $expr->memberName->getText() ?? $expr->memberName->getText($expr->getFileContents()); + if ($expr->parent instanceof Node\Expression\CallExpression) { $fqn .= '()'; } + $def = $this->index->getDefinition($fqn); if ($def === null) { return new Types\Mixed; } return $def->type; } - if ($expr instanceof Node\Expr\New_) { - return self::resolveClassNameToType($expr->class); + + // OBJECT CREATION EXPRESSION + // new A() => resolves to the type of the class type designator (A) + // TODO: new $this->a => resolves to the string represented by "a" + if ($expr instanceof Node\Expression\ObjectCreationExpression) { + return $this->resolveClassNameToType($expr->classTypeDesignator); } - if ($expr instanceof Node\Expr\Clone_ || $expr instanceof Node\Expr\Assign) { - return $this->resolveExpressionNodeToType($expr->expr); + + // CLONE EXPRESSION + // clone($a) => resolves to the type of $a + if ($expr instanceof Node\Expression\CloneExpression) { + return $this->resolveExpressionNodeToType($expr->expression); } - if ($expr instanceof Node\Expr\Ternary) { + + // ASSIGNMENT EXPRESSION + // $a = $myExpression => resolves to the type of the right-hand operand + if ($expr instanceof Node\Expression\AssignmentExpression) { + return $this->resolveExpressionNodeToType($expr->rightOperand); + } + + // TERNARY EXPRESSION + // $condition ? $ifExpression : $elseExpression => reslves to type of $ifCondition or $elseExpression + // $condition ?: $elseExpression => resolves to type of $condition or $elseExpression + if ($expr instanceof Node\Expression\TernaryExpression) { // ?: - if ($expr->if === null) { + if ($expr->ifExpression === null) { return new Types\Compound([ - $this->resolveExpressionNodeToType($expr->cond), - $this->resolveExpressionNodeToType($expr->else) + $this->resolveExpressionNodeToType($expr->condition), // TODO: why? + $this->resolveExpressionNodeToType($expr->elseExpression) ]); } // Ternary is a compound of the two possible values return new Types\Compound([ - $this->resolveExpressionNodeToType($expr->if), - $this->resolveExpressionNodeToType($expr->else) + $this->resolveExpressionNodeToType($expr->ifExpression), + $this->resolveExpressionNodeToType($expr->elseExpression) ]); } - if ($expr instanceof Node\Expr\BinaryOp\Coalesce) { + + // NULL COALLESCE + // $rightOperand ?? $leftOperand => resolves to type of $rightOperand or $leftOperand + if ($expr instanceof Node\Expression\BinaryExpression && $expr->operator->kind === PhpParser\TokenKind::QuestionQuestionToken) { // ?? operator return new Types\Compound([ - $this->resolveExpressionNodeToType($expr->left), - $this->resolveExpressionNodeToType($expr->right) + $this->resolveExpressionNodeToType($expr->leftOperand), + $this->resolveExpressionNodeToType($expr->rightOperand) ]); } + + // BOOLEAN EXPRESSIONS: resolve to Types\Boolean + // (bool) $expression + // !$expression + // empty($var) + // isset($var) + // >, >=, <, <=, &&, ||, AND, OR, XOR, ==, ===, !=, !== if ( - $expr instanceof Node\Expr\Instanceof_ - || $expr instanceof Node\Expr\Cast\Bool_ - || $expr instanceof Node\Expr\BooleanNot - || $expr instanceof Node\Expr\Empty_ - || $expr instanceof Node\Expr\Isset_ - || $expr instanceof Node\Expr\BinaryOp\Greater - || $expr instanceof Node\Expr\BinaryOp\GreaterOrEqual - || $expr instanceof Node\Expr\BinaryOp\Smaller - || $expr instanceof Node\Expr\BinaryOp\SmallerOrEqual - || $expr instanceof Node\Expr\BinaryOp\BooleanAnd - || $expr instanceof Node\Expr\BinaryOp\BooleanOr - || $expr instanceof Node\Expr\BinaryOp\LogicalAnd - || $expr instanceof Node\Expr\BinaryOp\LogicalOr - || $expr instanceof Node\Expr\BinaryOp\LogicalXor - || $expr instanceof Node\Expr\BinaryOp\NotEqual - || $expr instanceof Node\Expr\BinaryOp\NotIdentical + ParserHelpers\isBooleanExpression($expr) + + || ($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::BoolCastToken) + || ($expr instanceof Node\Expression\UnaryOpExpression && $expr->operator->kind === PhpParser\TokenKind::ExclamationToken) + || $expr instanceof Node\Expression\EmptyIntrinsicExpression + || $expr instanceof Node\Expression\IssetIntrinsicExpression ) { return new Types\Boolean; } + + // STRING EXPRESSIONS: resolve to Types\String + // [concatenation] .=, . + // [literals] "hello", \b"hello", \B"hello", 'hello', \b'hello', HEREDOC, NOWDOC + // [cast] (string) "hello" + // + // TODO: Magic constants (__CLASS__, __DIR__, __FUNCTION__, __METHOD__, __NAMESPACE__, __TRAIT__, __FILE__) if ( - $expr instanceof Node\Expr\Cast\String_ - || $expr instanceof Node\Expr\BinaryOp\Concat - || $expr instanceof Node\Expr\AssignOp\Concat - || $expr instanceof Node\Scalar\String_ - || $expr instanceof Node\Scalar\Encapsed - || $expr instanceof Node\Scalar\EncapsedStringPart - || $expr instanceof Node\Scalar\MagicConst\Class_ - || $expr instanceof Node\Scalar\MagicConst\Dir - || $expr instanceof Node\Scalar\MagicConst\Function_ - || $expr instanceof Node\Scalar\MagicConst\Method - || $expr instanceof Node\Scalar\MagicConst\Namespace_ - || $expr instanceof Node\Scalar\MagicConst\Trait_ + ($expr instanceof Node\Expression\BinaryExpression && + ($expr->operator->kind === PhpParser\TokenKind::DotToken || $expr->operator->kind === PhpParser\TokenKind::DotEqualsToken)) || + $expr instanceof Node\StringLiteral || + ($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::StringCastToken) ) { return new Types\String_; } + + // BINARY EXPRESSIONS: + // Resolve to Types\Integer if both left and right operands are integer types, otherwise Types\Float + // [operator] +, -, *, ** + // [assignment] *=, **=, -=, += + // Resolve to Types\Float + // [assignment] /= if ( - $expr instanceof Node\Expr\BinaryOp\Minus - || $expr instanceof Node\Expr\BinaryOp\Plus - || $expr instanceof Node\Expr\BinaryOp\Pow - || $expr instanceof Node\Expr\BinaryOp\Mul + $expr instanceof Node\Expression\BinaryExpression && + ($operator = $expr->operator->kind) + && ($operator === PhpParser\TokenKind::PlusToken || + $operator === PhpParser\TokenKind::AsteriskAsteriskToken || + $operator === PhpParser\TokenKind::AsteriskToken || + $operator === PhpParser\TokenKind::MinusToken || + + // Assignment expressions (TODO: consider making this a type of AssignmentExpression rather than kind of BinaryExpression) + $operator === PhpParser\TokenKind::AsteriskEqualsToken|| + $operator === PhpParser\TokenKind::AsteriskAsteriskEqualsToken || + $operator === PhpParser\TokenKind::MinusEqualsToken || + $operator === PhpParser\TokenKind::PlusEqualsToken + ) ) { if ( - $this->resolveExpressionNodeToType($expr->left) instanceof Types\Integer - && $this->resolveExpressionNodeToType($expr->right) instanceof Types\Integer + $this->resolveExpressionNodeToType($expr->leftOperand) instanceof Types\Integer + && $this->resolveExpressionNodeToType($expr->rightOperand) instanceof Types\Integer ) { return new Types\Integer; } return new Types\Float_; - } - - if ( - $expr instanceof Node\Expr\AssignOp\Minus - || $expr instanceof Node\Expr\AssignOp\Plus - || $expr instanceof Node\Expr\AssignOp\Pow - || $expr instanceof Node\Expr\AssignOp\Mul + } else if ( + $expr instanceof Node\Expression\BinaryExpression && + $expr->operator->kind === PhpParser\TokenKind::SlashEqualsToken ) { - if ( - $this->resolveExpressionNodeToType($expr->var) instanceof Types\Integer - && $this->resolveExpressionNodeToType($expr->expr) instanceof Types\Integer - ) { - return new Types\Integer; - } return new Types\Float_; } + // INTEGER EXPRESSIONS: resolve to Types\Integer + // [literal] 1 + // [operator] <=>, &, ^, | + // TODO: Magic constants (__LINE__) if ( - $expr instanceof Node\Scalar\LNumber - || $expr instanceof Node\Expr\Cast\Int_ - || $expr instanceof Node\Scalar\MagicConst\Line - || $expr instanceof Node\Expr\BinaryOp\Spaceship - || $expr instanceof Node\Expr\BinaryOp\BitwiseAnd - || $expr instanceof Node\Expr\BinaryOp\BitwiseOr - || $expr instanceof Node\Expr\BinaryOp\BitwiseXor + // TODO: consider different Node types of float/int, also better property name (not "children") + ($expr instanceof Node\NumericLiteral && $expr->children->kind === PhpParser\TokenKind::IntegerLiteralToken) || + $expr instanceof Node\Expression\BinaryExpression && ( + ($operator = $expr->operator->kind) + && ($operator === PhpParser\TokenKind::LessThanEqualsGreaterThanToken || + $operator === PhpParser\TokenKind::AmpersandToken || + $operator === PhpParser\TokenKind::CaretToken || + $operator === PhpParser\TokenKind::BarToken) + ) ) { return new Types\Integer; } + + // FLOAT EXPRESSIONS: resolve to Types\Float + // [literal] 1.5 + // [operator] / + // [cast] (double) if ( - $expr instanceof Node\Expr\BinaryOp\Div - || $expr instanceof Node\Scalar\DNumber - || $expr instanceof Node\Expr\Cast\Double + $expr instanceof Node\NumericLiteral && $expr->children->kind === PhpParser\TokenKind::FloatingLiteralToken || + ($expr instanceof Node\Expression\CastExpression && $expr->castType->kind === PhpParser\TokenKind::DoubleCastToken) || + ($expr instanceof Node\Expression\BinaryExpression && $expr->operator->kind === PhpParser\TokenKind::SlashToken) ) { return new Types\Float_; } - if ($expr instanceof Node\Expr\Array_) { + + // ARRAY CREATION EXPRESSION: + // Resolve to Types\Array (Types\Compound of value and key types) + // [a, b, c] + // [1=>"hello", "hi"=>1, 4=>[]]s + if ($expr instanceof Node\Expression\ArrayCreationExpression) { $valueTypes = []; $keyTypes = []; - foreach ($expr->items as $item) { - $valueTypes[] = $this->resolveExpressionNodeToType($item->value); - $keyTypes[] = $item->key ? $this->resolveExpressionNodeToType($item->key) : new Types\Integer; + if ($expr->arrayElements !== null) { + foreach ($expr->arrayElements->getElements() as $item) { + $valueTypes[] = $this->resolveExpressionNodeToType($item->elementValue); + $keyTypes[] = $item->elementKey ? $this->resolveExpressionNodeToType($item->elementKey) : new Types\Integer; + } } $valueTypes = array_unique($keyTypes); $keyTypes = array_unique($keyTypes); @@ -655,54 +849,68 @@ class DefinitionResolver } return new Types\Array_($valueType, $keyType); } - if ($expr instanceof Node\Expr\ArrayDimFetch) { - $varType = $this->resolveExpressionNodeToType($expr->var); + + // SUBSCRIPT EXPRESSION + // $myArray[3] + // $myArray{"hello"} + if ($expr instanceof Node\Expression\SubscriptExpression) { + $varType = $this->resolveExpressionNodeToType($expr->postfixExpression); if (!($varType instanceof Types\Array_)) { return new Types\Mixed; } return $varType->getValueType(); } - if ($expr instanceof Node\Expr\Include_) { + + // SCRIPT INCLUSION EXPRESSION + // include, require, include_once, require_once + if ($expr instanceof Node\Expression\ScriptInclusionExpression) { // TODO: resolve path to PhpDocument and find return statement return new Types\Mixed; } + + if ($expr instanceof Node\QualifiedName) { + return $this->resolveClassNameToType($expr); + } + return new Types\Mixed; } + /** * Takes any class name node (from a static method call, or new node) and returns a Type object * Resolves keywords like self, static and parent * - * @param Node $class + * @param Node | PhpParser\Token $class * @return Type */ - private static function resolveClassNameToType(Node $class): Type + public function resolveClassNameToType($class): Type { - if ($class instanceof Node\Expr) { + if ($class instanceof Node\Expression) { return new Types\Mixed; } - if ($class instanceof Node\Stmt\Class_) { + if ($class instanceof PhpParser\Token && $class->kind === PhpParser\TokenKind::ClassKeyword) { // Anonymous class return new Types\Object_; } - $className = (string)$class; + $className = (string)$class->getResolvedName(); + if ($className === 'static') { return new Types\Static_; } if ($className === 'self' || $className === 'parent') { - $classNode = getClosestNode($class, Node\Stmt\Class_::class); + $classNode = $class->getFirstAncestor(Node\Statement\ClassDeclaration::class); if ($className === 'parent') { - if ($classNode === null || $classNode->extends === null) { + if ($classNode === null || $classNode->classBaseClause === null) { return new Types\Object_; } // parent is resolved to the parent class - $classFqn = (string)$classNode->extends; + $classFqn = (string)$classNode->classBaseClause->baseClass->getResolvedName(); } else { if ($classNode === null) { return new Types\Self_; } // self is resolved to the containing class - $classFqn = (string)$classNode->namespacedName; + $classFqn = (string)$classNode->getNamespacedName(); } return new Types\Object_(new Fqsen('\\' . $classFqn)); } @@ -723,58 +931,65 @@ class DefinitionResolver * @param Node $node * @return \phpDocumentor\Reflection\Type|null */ - public function getTypeFromNode(Node $node) + public function getTypeFromNode($node) { - if ( - $node instanceof Node\Expr\FuncCall - && $node->name instanceof Node\Name - && strtolower((string)$node->name) === 'define' - && isset($node->args[0]) - && $node->args[0]->value instanceof Node\Scalar\String_ - && isset($node->args[1]) - ) { + if (ParserHelpers\isConstDefineExpression($node)) { // constants with define() like // define('TEST_DEFINE_CONSTANT', false); - return $this->resolveExpressionNodeToType($node->args[1]->value); + return $this->resolveExpressionNodeToType($node->argumentExpressionList->children[2]->expression); } - if ($node instanceof Node\Param) { + // PARAMETERS + // Get the type of the parameter: + // 1. Doc block + // 2. Parameter type and default + if ($node instanceof Node\Parameter) { // Parameters - $docBlock = $node->getAttribute('parentNode')->getAttribute('docBlock'); - if ($docBlock !== null) { - // Use @param tag - foreach ($docBlock->getTagsByName('param') as $paramTag) { - if ($paramTag->getVariableName() === $node->name) { - if ($paramTag->getType() === null) { - break; - } - return $paramTag->getType(); - } - } + // Get the doc block for the the function call + // /** + // * @param MyClass $myParam + // */ + // function foo($a) + $functionLikeDeclaration = ParserHelpers\getFunctionLikeDeclarationFromParameter($node); + $variableName = $node->getName(); + $docBlock = $this->getDocBlock($functionLikeDeclaration); + + $parameterDocBlockTag = $this->tryGetDocBlockTagForParameter($docBlock, $variableName); + if ($parameterDocBlockTag !== null && ($type = $parameterDocBlockTag->getType())) { + // Doc block comments supercede all other forms of type inference + return $type; } - $type = null; - if ($node->type !== null) { + + // function foo(MyClass $a) + if ($node->typeDeclaration !== null) { // Use PHP7 return type hint - if (is_string($node->type)) { + if ($node->typeDeclaration instanceof PhpParser\Token) { // Resolve a string like "bool" to a type object - $type = $this->typeResolver->resolve($node->type); + $type = $this->typeResolver->resolve($node->typeDeclaration->getText($node->getFileContents())); } else { - $type = new Types\Object_(new Fqsen('\\' . (string)$node->type)); + $type = new Types\Object_(new Fqsen('\\' . (string)$node->typeDeclaration->getResolvedName())); } } + // function foo($a = 3) if ($node->default !== null) { $defaultType = $this->resolveExpressionNodeToType($node->default); if (isset($type) && !is_a($type, get_class($defaultType))) { - $type = new Types\Compound([$type, $defaultType]); - } else { - $type = $defaultType; + // TODO - verify it is worth creating a compound type + return new Types\Compound([$type, $defaultType]); } + $type = $defaultType; } return $type ?? new Types\Mixed; } - if ($node instanceof Node\FunctionLike) { + + // FUNCTIONS AND METHODS + // Get the return type + // 1. doc block + // 2. return type hint + // 3. TODO: infer from return statements + if (ParserHelpers\isFunctionLike($node)) { // Functions/methods - $docBlock = $node->getAttribute('docBlock'); + $docBlock = $this->getDocBlock($node); if ( $docBlock !== null && !empty($returnTags = $docBlock->getTagsByName('return')) @@ -783,56 +998,56 @@ class DefinitionResolver // Use @return tag return $returnTags[0]->getType(); } - if ($node->returnType !== null) { + if ($node->returnType !== null && !($node->returnType instanceof PhpParser\MissingToken)) { // Use PHP7 return type hint - if (is_string($node->returnType)) { + if ($node->returnType instanceof PhpParser\Token) { // Resolve a string like "bool" to a type object - return $this->typeResolver->resolve($node->returnType); + return $this->typeResolver->resolve($node->returnType->getText($node->getFileContents())); } - return new Types\Object_(new Fqsen('\\' . (string)$node->returnType)); + return new Types\Object_(new Fqsen('\\' . (string)$node->returnType->getResolvedName())); } // Unknown return type return new Types\Mixed; } - if ($node instanceof Node\Expr\Variable) { - $node = $node->getAttribute('parentNode'); - } + + // PROPERTIES, CONSTS, CLASS CONSTS, ASSIGNMENT EXPRESSIONS + // Get the documented type the assignment resolves to. if ( - $node instanceof Node\Stmt\PropertyProperty - || $node instanceof Node\Const_ - || $node instanceof Node\Expr\Assign - || $node instanceof Node\Expr\AssignOp - ) { - if ($node instanceof Node\Stmt\PropertyProperty || $node instanceof Node\Const_) { - $docBlockHolder = $node->getAttribute('parentNode'); - } else { - $docBlockHolder = $node; - } + ($declarationNode = + ParserHelpers\tryGetPropertyDeclaration($node) ?? + ParserHelpers\tryGetConstOrClassConstDeclaration($node) + ) !== null || + ($node = $node->parent) instanceof Node\Expression\AssignmentExpression) { + $declarationNode = $declarationNode ?? $node; + // Property, constant or variable // Use @var tag if ( - isset($docBlockHolder) - && ($docBlock = $docBlockHolder->getAttribute('docBlock')) + ($docBlock = $this->getDocBlock($declarationNode)) && !empty($varTags = $docBlock->getTagsByName('var')) && ($type = $varTags[0]->getType()) ) { return $type; } + // Resolve the expression - if ($node instanceof Node\Stmt\PropertyProperty) { - if ($node->default) { - return $this->resolveExpressionNodeToType($node->default); + if ($declarationNode instanceof Node\PropertyDeclaration) { + // TODO should have default + if (isset($node->parent->rightOperand)) { + return $this->resolveExpressionNodeToType($node->parent->rightOperand); } - } else if ($node instanceof Node\Const_) { - return $this->resolveExpressionNodeToType($node->value); - } else { - return $this->resolveExpressionNodeToType($node); + } else if ($node instanceof Node\ConstElement) { + return $this->resolveExpressionNodeToType($node->assignment); + } else if ($node instanceof Node\Expression\AssignmentExpression) { + return $this->resolveExpressionNodeToType($node->rightOperand); } // TODO: read @property tags of class // TODO: Try to infer the type from default value / constant value // Unknown return new Types\Mixed; } + + // The node does not have a type return null; } @@ -843,67 +1058,138 @@ class DefinitionResolver * @param Node $node * @return string|null */ - public static function getDefinedFqn(Node $node) + public static function getDefinedFqn($node) { - $parent = $node->getAttribute('parentNode'); + $parent = $node->parent; // Anonymous classes don't count as a definition - if ($node instanceof Node\Stmt\ClassLike && isset($node->name)) { - // Class, interface or trait declaration - return (string)$node->namespacedName; - } else if ($node instanceof Node\Name && $parent instanceof Node\Stmt\Namespace_) { - return (string)$node; - } else if ($node instanceof Node\Stmt\Function_) { + // INPUT OUTPUT: + // namespace A\B; + // class C { } A\B\C + // interface C { } A\B\C + // trait C { } A\B\C + if ( + $node instanceof Node\Statement\ClassDeclaration || + $node instanceof Node\Statement\InterfaceDeclaration || + $node instanceof Node\Statement\TraitDeclaration + ) { + return (string) $node->getNamespacedName(); + } + + // INPUT OUTPUT: + // namespace A\B; A\B + if ($node instanceof Node\Statement\NamespaceDefinition && $node->name instanceof Node\QualifiedName) { + $name = (string) PhpParser\ResolvedName::buildName($node->name->nameParts, $node->getFileContents()); + return $name === "" ? null : $name; + } + + // INPUT OUTPUT: + // namespace A\B; + // function a(); A\B\a(); + if ($node instanceof Node\Statement\FunctionDeclaration) { // Function: use functionName() as the name - return (string)$node->namespacedName . '()'; - } else if ($node instanceof Node\Stmt\ClassMethod) { + $name = (string)$node->getNamespacedName(); + return $name === "" ? null : $name . '()'; + } + + // INPUT OUTPUT + // namespace A\B; + // class C { + // function a () {} A\B\C->a() + // static function b() {} A\B\C::b() + // } + if ($node instanceof Node\MethodDeclaration) { // Class method: use ClassName->methodName() as name - $class = $node->getAttribute('parentNode'); + $class = $node->getFirstAncestor( + Node\Expression\ObjectCreationExpression::class, + Node\Statement\ClassDeclaration::class, + Node\Statement\InterfaceDeclaration::class, + Node\Statement\TraitDeclaration::class + ); if (!isset($class->name)) { // Ignore anonymous classes return null; } if ($node->isStatic()) { - return (string)$class->namespacedName . '::' . (string)$node->name . '()'; + return (string)$class->getNamespacedName() . '::' . $node->getName() . '()'; } else { - return (string)$class->namespacedName . '->' . (string)$node->name . '()'; + return (string)$class->getNamespacedName() . '->' . $node->getName() . '()'; } - } else if ($node instanceof Node\Stmt\PropertyProperty) { - $property = $node->getAttribute('parentNode'); - $class = $property->getAttribute('parentNode'); - if (!isset($class->name)) { - // Ignore anonymous classes + } + + // INPUT OUTPUT + // namespace A\B; + // class C { + // static $a = 4, $b = 4 A\B\C::$a, A\B\C::$b + // $a = 4, $b = 4 A\B\C->$a, A\B\C->$b // TODO verify variable name + // } + if ( + ($propertyDeclaration = ParserHelpers\tryGetPropertyDeclaration($node)) !== null && + ($classDeclaration = + $node->getFirstAncestor( + Node\Expression\ObjectCreationExpression::class, + Node\Statement\ClassDeclaration::class, + Node\Statement\InterfaceDeclaration::class, + Node\Statement\TraitDeclaration::class + ) + ) !== null && isset($classDeclaration->name)) { + $name = $node->getName(); + if ($propertyDeclaration->isStatic()) { + // Static Property: use ClassName::$propertyName as name + return (string)$classDeclaration->getNamespacedName() . '::$' . $name; + } + + // Instance Property: use ClassName->propertyName as name + return (string)$classDeclaration->getNamespacedName() . '->' . $name; + } + + // INPUT OUTPUT + // namespace A\B; + // const FOO = 5; A\B\FOO + // class C { + // const $a, $b = 4 A\B\C::$a(), A\B\C::$b + // } + if (($constDeclaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) !== null) { + if ($constDeclaration instanceof Node\Statement\ConstDeclaration) { + // Basic constant: use CONSTANT_NAME as name + return (string)$node->getNamespacedName(); + } + + // Class constant: use ClassName::CONSTANT_NAME as name + $classDeclaration = $constDeclaration->getFirstAncestor( + Node\Expression\ObjectCreationExpression::class, + Node\Statement\ClassDeclaration::class, + Node\Statement\InterfaceDeclaration::class, + Node\Statement\TraitDeclaration::class + ); + + if (!isset($classDeclaration->name)) { return null; } - if ($property->isStatic()) { - // Static Property: use ClassName::$propertyName as name - return (string)$class->namespacedName . '::$' . (string)$node->name; - } else { - // Instance Property: use ClassName->propertyName as name - return (string)$class->namespacedName . '->' . (string)$node->name; + return (string)$classDeclaration->getNamespacedName() . '::' . $node->getName(); + } + + if (ParserHelpers\isConstDefineExpression($node)) { + return $node->argumentExpressionList->children[0]->expression->getStringContentsText(); + } + + return null; + } + + /** + * @param DocBlock | null $docBlock + * @param string | null $variableName + * @return DocBlock\Tags\Param | null + */ + private function tryGetDocBlockTagForParameter($docBlock, $variableName) + { + if ($docBlock === null || $variableName === null) { + return null; + } + $tags = $docBlock->getTagsByName('param'); + foreach ($tags as $tag) { + if ($tag->getVariableName() === \ltrim($variableName, "$")) { + return $tag; } - } else if ($node instanceof Node\Const_) { - $parent = $node->getAttribute('parentNode'); - if ($parent instanceof Node\Stmt\Const_) { - // Basic constant: use CONSTANT_NAME as name - return (string)$node->namespacedName; - } - if ($parent instanceof Node\Stmt\ClassConst) { - // Class constant: use ClassName::CONSTANT_NAME as name - $class = $parent->getAttribute('parentNode'); - if (!isset($class->name) || $class->name instanceof Node\Expr) { - return null; - } - return (string)$class->namespacedName . '::' . $node->name; - } - } else if ( - $node instanceof Node\Expr\FuncCall - && $node->name instanceof Node\Name - && strtolower((string)$node->name) === 'define' - && isset($node->args[0]) - && $node->args[0]->value instanceof Node\Scalar\String_ - && isset($node->args[1]) - ) { - return (string)$node->args[0]->value->value; } return null; } diff --git a/src/FqnUtilities.php b/src/FqnUtilities.php new file mode 100644 index 0000000..a3aab05 --- /dev/null +++ b/src/FqnUtilities.php @@ -0,0 +1,31 @@ +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; +} diff --git a/src/Index/Index.php b/src/Index/Index.php index b753476..9cb975e 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -150,6 +150,17 @@ class Index implements ReadableIndex, \Serializable 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 * diff --git a/src/Indexer.php b/src/Indexer.php index 34ad618..9f8749d 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -6,6 +6,7 @@ namespace LanguageServer; use LanguageServer\Cache\Cache; use LanguageServer\FilesFinder\FilesFinder; use LanguageServer\Index\{DependenciesIndex, Index}; +use LanguageServer\Protocol\Message; use LanguageServer\Protocol\MessageType; use Webmozart\PathUtil\Path; use Composer\Semver\VersionParser; diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 8fe9ec1..173abfe 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -8,10 +8,7 @@ use LanguageServer\Protocol\{ ClientCapabilities, TextDocumentSyncKind, Message, - MessageType, InitializeResult, - SymbolInformation, - TextDocumentIdentifier, CompletionOptions }; 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\Cache\{FileSystemCache, ClientCache}; use AdvancedJsonRpc; -use Sabre\Event\{Loop, Promise}; +use Sabre\Event\Promise; use function Sabre\Event\coroutine; -use Exception; use Throwable; use Webmozart\PathUtil\Path; -use Sabre\Uri; class LanguageServer extends AdvancedJsonRpc\Dispatcher { diff --git a/src/NodeVisitor/ColumnCalculator.php b/src/NodeVisitor/ColumnCalculator.php deleted file mode 100644 index 6f6b3bf..0000000 --- a/src/NodeVisitor/ColumnCalculator.php +++ /dev/null @@ -1,41 +0,0 @@ -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); - } -} diff --git a/src/NodeVisitor/DefinitionCollector.php b/src/NodeVisitor/DefinitionCollector.php deleted file mode 100644 index 5198139..0000000 --- a/src/NodeVisitor/DefinitionCollector.php +++ /dev/null @@ -1,47 +0,0 @@ -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); - } -} diff --git a/src/NodeVisitor/DocBlockParser.php b/src/NodeVisitor/DocBlockParser.php deleted file mode 100644 index 78f928e..0000000 --- a/src/NodeVisitor/DocBlockParser.php +++ /dev/null @@ -1,96 +0,0 @@ -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 = ''; - } - } -} diff --git a/src/NodeVisitor/NodeAtPositionFinder.php b/src/NodeVisitor/NodeAtPositionFinder.php deleted file mode 100644 index cb17801..0000000 --- a/src/NodeVisitor/NodeAtPositionFinder.php +++ /dev/null @@ -1,45 +0,0 @@ -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; - } - } - } -} diff --git a/src/NodeVisitor/ReferencesAdder.php b/src/NodeVisitor/ReferencesAdder.php deleted file mode 100644 index 9c6ac0f..0000000 --- a/src/NodeVisitor/ReferencesAdder.php +++ /dev/null @@ -1,54 +0,0 @@ -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); - } -} diff --git a/src/NodeVisitor/ReferencesCollector.php b/src/NodeVisitor/ReferencesCollector.php deleted file mode 100644 index e7b9f06..0000000 --- a/src/NodeVisitor/ReferencesCollector.php +++ /dev/null @@ -1,75 +0,0 @@ -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; - } -} diff --git a/src/NodeVisitor/VariableReferencesCollector.php b/src/NodeVisitor/VariableReferencesCollector.php deleted file mode 100644 index bb44bdc..0000000 --- a/src/NodeVisitor/VariableReferencesCollector.php +++ /dev/null @@ -1,50 +0,0 @@ -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; - } - } -} diff --git a/src/Parser.php b/src/Parser.php deleted file mode 100644 index d6d6d13..0000000 --- a/src/Parser.php +++ /dev/null @@ -1,25 +0,0 @@ - [ - 'comments', - 'startLine', - 'endLine', - 'startFilePos', - 'endFilePos' - ] - ]); - parent::__construct($lexer); - } -} diff --git a/src/ParserHelpers.php b/src/ParserHelpers.php new file mode 100644 index 0000000..5025ae3 --- /dev/null +++ b/src/ParserHelpers.php @@ -0,0 +1,124 @@ +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]); +} diff --git a/src/PhpDocument.php b/src/PhpDocument.php index 3a25c23..f7b813e 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -3,27 +3,20 @@ declare(strict_types = 1); namespace LanguageServer; -use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, TextEdit}; -use LanguageServer\NodeVisitor\{ - NodeAtPositionFinder, - ReferencesAdder, - DocBlockParser, - DefinitionCollector, - ColumnCalculator, - ReferencesCollector -}; use LanguageServer\Index\Index; -use PhpParser\{Error, ErrorHandler, Node, NodeTraverser}; -use PhpParser\NodeVisitor\NameResolver; +use LanguageServer\Protocol\{ + Diagnostic, Position, Range +}; +use Microsoft\PhpParser; +use Microsoft\PhpParser\Node; use phpDocumentor\Reflection\DocBlockFactory; -use Sabre\Uri; class PhpDocument { /** * The PHPParser instance * - * @var Parser + * @var PhpParser\Parser */ private $parser; @@ -63,7 +56,7 @@ class PhpDocument /** * The AST of the document * - * @var Node[] + * @var Node */ private $stmts; @@ -77,7 +70,7 @@ class PhpDocument /** * Map from fully qualified name (FQN) to Node * - * @var Node[] + * @var Node */ private $definitionNodes; @@ -96,18 +89,18 @@ class PhpDocument private $diagnostics; /** - * @param string $uri The URI of the document - * @param string $content The content of the document - * @param Index $index The Index to register definitions and references to - * @param Parser $parser The PHPParser instance - * @param DocBlockFactory $docBlockFactory The DocBlockFactory instance to parse docblocks + * @param string $uri The URI of the document + * @param string $content The content of the document + * @param Index $index The Index to register definitions and references to + * @param PhpParser\Parser $parser The PhpParser instance + * @param DocBlockFactory $docBlockFactory The DocBlockFactory instance to parse docblocks * @param DefinitionResolver $definitionResolver The DefinitionResolver to resolve definitions to symbols in the workspace */ public function __construct( string $uri, string $content, Index $index, - Parser $parser, + $parser, DocBlockFactory $docBlockFactory, DefinitionResolver $definitionResolver ) { @@ -133,7 +126,7 @@ class PhpDocument /** * Updates the content on this document. * 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 * @return void @@ -160,64 +153,26 @@ class PhpDocument $this->definitions = null; $this->definitionNodes = null; - $errorHandler = new ErrorHandler\Collecting; - $stmts = $this->parser->parse($content, $errorHandler); + $treeAnalyzer = new TreeAnalyzer($this->parser, $content, $this->docBlockFactory, $this->definitionResolver, $this->uri); - $this->diagnostics = []; - foreach ($errorHandler->getErrors() as $error) { - $this->diagnostics[] = Diagnostic::fromError($error, $this->content, DiagnosticSeverity::ERROR, 'php'); + $this->diagnostics = $treeAnalyzer->getDiagnostics(); + + $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 - if ($stmts) { - $traverser = new NodeTraverser; - - // 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; + // Register this document on the project for references + foreach ($this->referenceNodes as $fqn => $nodes) { + $this->index->addReferenceUri($fqn, $this->uri); } + + $this->stmts = $treeAnalyzer->getStmts(); } /** @@ -266,9 +221,9 @@ class PhpDocument /** * Returns the AST of the document * - * @return Node[] + * @return Node | null */ - public function getStmts(): array + public function getStmts() { return $this->stmts; } @@ -284,11 +239,13 @@ class PhpDocument if ($this->stmts === null) { return null; } - $traverser = new NodeTraverser; - $finder = new NodeAtPositionFinder($position); - $traverser->addVisitor($finder); - $traverser->traverse($this->stmts); - return $finder->node; + + $offset = $position->toOffset($this->stmts->getFileContents()); + $node = $this->stmts->getDescendantNodeAtPosition($offset); + if ($node !== null && $node->getStart() > $offset) { + return null; + } + return $node; } /** diff --git a/src/PhpDocumentLoader.php b/src/PhpDocumentLoader.php index 728225d..57a7e9c 100644 --- a/src/PhpDocumentLoader.php +++ b/src/PhpDocumentLoader.php @@ -8,6 +8,7 @@ use LanguageServer\Index\ProjectIndex; use phpDocumentor\Reflection\DocBlockFactory; use Sabre\Event\Promise; use function Sabre\Event\coroutine; +use Microsoft\PhpParser; /** * Takes care of loading documents and managing "open" documents @@ -36,6 +37,11 @@ class PhpDocumentLoader */ private $parser; + /** + * @var PhpParser\Parser + */ + private $tolerantParser; + /** * @var DocBlockFactory */ @@ -47,9 +53,10 @@ class PhpDocumentLoader private $definitionResolver; /** - * @param ContentRetriever $contentRetriever - * @param ProjectIndex $project + * @param ContentRetriever $contentRetriever + * @param ProjectIndex $projectIndex * @param DefinitionResolver $definitionResolver + * @internal param ProjectIndex $project */ public function __construct( ContentRetriever $contentRetriever, @@ -59,7 +66,7 @@ class PhpDocumentLoader $this->contentRetriever = $contentRetriever; $this->projectIndex = $projectIndex; $this->definitionResolver = $definitionResolver; - $this->parser = new Parser; + $this->parser = new PhpParser\Parser(); $this->docBlockFactory = DocBlockFactory::createInstance(); } diff --git a/src/Protocol/Diagnostic.php b/src/Protocol/Diagnostic.php index 44a24c1..7bf9895 100644 --- a/src/Protocol/Diagnostic.php +++ b/src/Protocol/Diagnostic.php @@ -2,8 +2,6 @@ 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 * resource. @@ -47,26 +45,6 @@ class Diagnostic */ 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 Range $range The range at which the message applies diff --git a/src/Protocol/Location.php b/src/Protocol/Location.php index a1d9861..50fedfa 100644 --- a/src/Protocol/Location.php +++ b/src/Protocol/Location.php @@ -2,7 +2,8 @@ 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. @@ -25,9 +26,13 @@ class Location * @param Node $node * @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) diff --git a/src/Protocol/Range.php b/src/Protocol/Range.php index e4fe527..1e19a5a 100644 --- a/src/Protocol/Range.php +++ b/src/Protocol/Range.php @@ -2,7 +2,8 @@ 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. @@ -31,26 +32,12 @@ class Range */ public static function fromNode(Node $node) { - return new self( - new Position($node->getAttribute('startLine') - 1, $node->getAttribute('startColumn') - 1), - new Position($node->getAttribute('endLine') - 1, $node->getAttribute('endColumn')) - ); - } + $range = PhpParser\PositionUtilities::getRangeFromPosition($node->getStart(), $node->getWidth(), $node->getFileContents()); - /** - * Returns the range where an error occured - * - * @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)); + return new self( + new Position($range->start->line, $range->start->character), + new Position($range->end->line, $range->end->character) + ); } public function __construct(Position $start = null, Position $end = null) diff --git a/src/Protocol/SymbolInformation.php b/src/Protocol/SymbolInformation.php index da04404..6b4d39e 100644 --- a/src/Protocol/SymbolInformation.php +++ b/src/Protocol/SymbolInformation.php @@ -2,7 +2,8 @@ namespace LanguageServer\Protocol; -use PhpParser\Node; +use Microsoft\PhpParser; +use Microsoft\PhpParser\Node; use Exception; /** @@ -44,66 +45,68 @@ class SymbolInformation * * @param Node $node * @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; - - if ( - $node instanceof Node\Expr\FuncCall - && $node->name instanceof Node\Name - && strtolower((string)$node->name) === 'define' - && isset($node->args[0]) - && $node->args[0]->value instanceof Node\Scalar\String_ - && isset($node->args[1]) - ) { + if ($node instanceof Node\Statement\ClassDeclaration) { + $symbol->kind = SymbolKind::CLASS_; + } else if ($node instanceof Node\Statement\TraitDeclaration) { + $symbol->kind = SymbolKind::CLASS_; + } else if (\LanguageServer\ParserHelpers\isConstDefineExpression($node)) { // constants with define() like // define('TEST_DEFINE_CONSTANT', false); $symbol->kind = SymbolKind::CONSTANT; - $symbol->name = (string)$node->args[0]->value->value; - } else if ($node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_) { - $symbol->kind = SymbolKind::CLASS_; - } else if ($node instanceof Node\Stmt\Interface_) { + $symbol->name = $node->argumentExpressionList->children[0]->expression->getStringContentsText(); + } else if ($node instanceof Node\Statement\InterfaceDeclaration) { $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; - } else if ($node instanceof Node\Stmt\Function_) { + } else if ($node instanceof Node\Statement\FunctionDeclaration) { $symbol->kind = SymbolKind::FUNCTION; - } else if ($node instanceof Node\Stmt\ClassMethod && ($node->name === '__construct' || $node->name === '__destruct')) { - $symbol->kind = SymbolKind::CONSTRUCTOR; - } else if ($node instanceof Node\Stmt\ClassMethod) { - $symbol->kind = SymbolKind::METHOD; - } else if ($node instanceof Node\Stmt\PropertyProperty) { + } else if ($node instanceof Node\MethodDeclaration) { + $nameText = $node->getName(); + if ($nameText === '__construct' || $nameText === '__destruct') { + $symbol->kind = SymbolKind::CONSTRUCTOR; + } else { + $symbol->kind = SymbolKind::METHOD; + } + } else if ($node instanceof Node\Expression\Variable && $node->getFirstAncestor(Node\PropertyDeclaration::class) !== null) { $symbol->kind = SymbolKind::PROPERTY; - } else if ($node instanceof Node\Const_) { + } else if ($node instanceof Node\ConstElement) { $symbol->kind = SymbolKind::CONSTANT; } else if ( ( - ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) - && $node->var instanceof Node\Expr\Variable + ($node instanceof Node\Expression\AssignmentExpression) + && $node->leftOperand instanceof Node\Expression\Variable ) - || $node instanceof Node\Expr\ClosureUse - || $node instanceof Node\Param + || $node instanceof Node\UseVariableName + || $node instanceof Node\Parameter ) { $symbol->kind = SymbolKind::VARIABLE; } else { return null; } - if (!isset($symbol->name)) { - if ($node instanceof Node\Name) { - $symbol->name = (string)$node; - } else if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp) { - $symbol->name = $node->var->name; - } else if ($node instanceof Node\Expr\ClosureUse) { - $symbol->name = $node->var; - } else if (isset($node->name)) { - $symbol->name = (string)$node->name; - } else { - return null; + if ($node instanceof Node\Expression\AssignmentExpression) { + if ($node->leftOperand instanceof Node\Expression\Variable) { + $symbol->name = $node->leftOperand->getName(); + } elseif ($node->leftOperand instanceof PhpParser\Token) { + $symbol->name = trim($node->leftOperand->getText($node->getFileContents()), "$"); } + } 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); diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index aa76ec2..2e8e4bf 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -3,34 +3,21 @@ declare(strict_types = 1); namespace LanguageServer\Server; -use PhpParser\PrettyPrinter\Standard as PrettyPrinter; -use PhpParser\{Node, NodeTraverser}; -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\{ + CompletionProvider, LanguageClient, PhpDocument, PhpDocumentLoader, DefinitionResolver }; 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\Uri; +use function LanguageServer\{ + isVendored, waitForEvent +}; use function Sabre\Event\coroutine; -use function LanguageServer\{waitForEvent, isVendored}; /** * Provides method handlers for all textDocument/* methods @@ -49,11 +36,6 @@ class TextDocument */ protected $project; - /** - * @var PrettyPrinter - */ - protected $prettyPrinter; - /** * @var DefinitionResolver */ @@ -80,12 +62,12 @@ class TextDocument protected $composerLock; /** - * @param PhpDocumentLoader $documentLoader + * @param PhpDocumentLoader $documentLoader * @param DefinitionResolver $definitionResolver - * @param LanguageClient $client - * @param ReadableIndex $index - * @param \stdClass $composerJson - * @param \stdClass $composerLock + * @param LanguageClient $client + * @param ReadableIndex $index + * @param \stdClass $composerJson + * @param \stdClass $composerLock */ public function __construct( PhpDocumentLoader $documentLoader, @@ -97,7 +79,6 @@ class TextDocument ) { $this->documentLoader = $documentLoader; $this->client = $client; - $this->prettyPrinter = new PrettyPrinter(); $this->definitionResolver = $definitionResolver; $this->completionProvider = new CompletionProvider($this->definitionResolver, $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 // by traversing the AST if ( - $node instanceof Node\Expr\Variable - || $node instanceof Node\Param - || $node instanceof Node\Expr\ClosureUse + + ($node instanceof Node\Expression\Variable && !($node->getParent()->getParent() instanceof Node\PropertyDeclaration)) + || $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; } // Find function/method/closure scope $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'); - } - $traverser = new NodeTraverser; - $refCollector = new VariableReferencesCollector($node->name); - $traverser->addVisitor($refCollector); - $traverser->traverse($n->getStmts()); - foreach ($refCollector->nodes as $ref) { - $locations[] = Location::fromNode($ref); + + foreach ($n->getDescendantNodes() as $descendantNode) { + if ($descendantNode instanceof Node\Expression\Variable && + $descendantNode->getName() === $node->getName() + ) { + $locations[] = Location::fromNode($descendantNode); + } } } else { // Definition with a global FQN $fqn = DefinitionResolver::getDefinedFqn($node); + // Wait until indexing finished if (!$this->index->isComplete()) { yield waitForEvent($this->index, 'complete'); diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php new file mode 100644 index 0000000..08c60b0 --- /dev/null +++ b/src/TreeAnalyzer.php @@ -0,0 +1,209 @@ +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; + } +} diff --git a/src/utils.php b/src/utils.php index c0c5bf7..636b41e 100644 --- a/src/utils.php +++ b/src/utils.php @@ -5,7 +5,6 @@ namespace LanguageServer; use Throwable; use InvalidArgumentException; -use PhpParser\Node; use Sabre\Event\{Loop, Promise, EmitterInterface}; use Sabre\Uri; @@ -94,23 +93,6 @@ function waitForEvent(EmitterInterface $emitter, string $event): Promise 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 * Example: diff --git a/tests/DefinitionResolverTest.php b/tests/DefinitionResolverTest.php index 0046ef0..e7d8b78 100644 --- a/tests/DefinitionResolverTest.php +++ b/tests/DefinitionResolverTest.php @@ -5,32 +5,33 @@ namespace LanguageServer\Tests; use PHPUnit\Framework\TestCase; use LanguageServer\Index\Index; -use LanguageServer\{DefinitionResolver, Parser}; +use LanguageServer\DefinitionResolver; +use Microsoft\PhpParser; class DefinitionResolverTest extends TestCase { public function testCreateDefinitionFromNode() { - $parser = new Parser; - $stmts = $parser->parse("setAttribute('ownerDocument', new MockPhpDocument); + $parser = new PhpParser\Parser; + $doc = new MockPhpDocument; + $stmts = $parser->parseSourceFile("getUri()); $index = new 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); } public function testGetTypeFromNode() { - $parser = new Parser; - $stmts = $parser->parse("setAttribute('ownerDocument', new MockPhpDocument); + $parser = new PhpParser\Parser; + $doc = new MockPhpDocument; + $stmts = $parser->parseSourceFile("getUri()); $index = new 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); } @@ -38,26 +39,26 @@ class DefinitionResolverTest extends TestCase public function testGetDefinedFqnForIncompleteDefine() { // define('XXX') (only one argument) must not introduce a new symbol - $parser = new Parser; - $stmts = $parser->parse("setAttribute('ownerDocument', new MockPhpDocument); + $parser = new PhpParser\Parser; + $doc = new MockPhpDocument; + $stmts = $parser->parseSourceFile("getUri()); $index = new Index; $definitionResolver = new DefinitionResolver($index); - $fqn = $definitionResolver->getDefinedFqn($stmts[0]); + $fqn = $definitionResolver->getDefinedFqn($stmts->statementList[1]->expression); $this->assertNull($fqn); } public function testGetDefinedFqnForDefine() { - $parser = new Parser; - $stmts = $parser->parse("setAttribute('ownerDocument', new MockPhpDocument); + $parser = new PhpParser\Parser; + $doc = new MockPhpDocument; + $stmts = $parser->parseSourceFile("getUri()); $index = new Index; $definitionResolver = new DefinitionResolver($index); - $fqn = $definitionResolver->getDefinedFqn($stmts[0]); + $fqn = $definitionResolver->getDefinedFqn($stmts->statementList[1]->expression); $this->assertEquals('TEST_DEFINE', $fqn); } diff --git a/tests/NodeVisitor/DefinitionCollectorTest.php b/tests/NodeVisitor/DefinitionCollectorTest.php index 7345f2c..a092b62 100644 --- a/tests/NodeVisitor/DefinitionCollectorTest.php +++ b/tests/NodeVisitor/DefinitionCollectorTest.php @@ -4,39 +4,21 @@ declare(strict_types = 1); namespace LanguageServer\Tests\Server\TextDocument; use PHPUnit\Framework\TestCase; -use PhpParser\{NodeTraverser, Node}; -use PhpParser\NodeVisitor\NameResolver; use phpDocumentor\Reflection\DocBlockFactory; -use LanguageServer\{LanguageClient, PhpDocument, PhpDocumentLoader, Parser, DefinitionResolver}; -use LanguageServer\ContentRetriever\FileSystemContentRetriever; -use LanguageServer\Protocol\ClientCapabilities; -use LanguageServer\Index\{ProjectIndex, Index, DependenciesIndex}; -use LanguageServer\Tests\MockProtocolStream; -use LanguageServer\NodeVisitor\{ReferencesAdder, DefinitionCollector}; +use LanguageServer\{ + DefinitionResolver, TreeAnalyzer +}; +use LanguageServer\Index\{Index}; use function LanguageServer\pathToUri; +use Microsoft\PhpParser; +use Microsoft\PhpParser\Node; class DefinitionCollectorTest extends TestCase { public function testCollectsSymbols() { $path = realpath(__DIR__ . '/../../fixtures/symbols.php'); - $uri = pathToUri($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; + $defNodes = $this->collectDefinitions($path); $this->assertEquals([ 'TestNamespace', @@ -55,46 +37,48 @@ class DefinitionCollectorTest extends TestCase 'TestNamespace\\Example->__construct()', 'TestNamespace\\Example->__destruct()' ], array_keys($defNodes)); - $this->assertInstanceOf(Node\Const_::class, $defNodes['TestNamespace\\TEST_CONST']); - $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\TestClass']); - $this->assertInstanceOf(Node\Const_::class, $defNodes['TestNamespace\\TestClass::TEST_CLASS_CONST']); - $this->assertInstanceOf(Node\Stmt\PropertyProperty::class, $defNodes['TestNamespace\\TestClass::$staticTestProperty']); - $this->assertInstanceOf(Node\Stmt\PropertyProperty::class, $defNodes['TestNamespace\\TestClass->testProperty']); - $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\TestClass::staticTestMethod()']); - $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\TestClass->testMethod()']); - $this->assertInstanceOf(Node\Stmt\Trait_::class, $defNodes['TestNamespace\\TestTrait']); - $this->assertInstanceOf(Node\Stmt\Interface_::class, $defNodes['TestNamespace\\TestInterface']); - $this->assertInstanceOf(Node\Stmt\Function_::class, $defNodes['TestNamespace\\test_function()']); - $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\ChildClass']); - $this->assertInstanceOf(Node\Stmt\Class_::class, $defNodes['TestNamespace\\Example']); - $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__construct()']); - $this->assertInstanceOf(Node\Stmt\ClassMethod::class, $defNodes['TestNamespace\\Example->__destruct()']); + + $this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TEST_CONST']); + $this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\TestClass']); + $this->assertInstanceOf(Node\ConstElement::class, $defNodes['TestNamespace\\TestClass::TEST_CLASS_CONST']); + // TODO - should we parse properties more strictly? + $this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass::$staticTestProperty']); + $this->assertInstanceOf(Node\Expression\Variable::class, $defNodes['TestNamespace\\TestClass->testProperty']); + $this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass::staticTestMethod()']); + $this->assertInstanceOf(Node\MethodDeclaration::class, $defNodes['TestNamespace\\TestClass->testMethod()']); + $this->assertInstanceOf(Node\Statement\TraitDeclaration::class, $defNodes['TestNamespace\\TestTrait']); + $this->assertInstanceOf(Node\Statement\InterfaceDeclaration::class, $defNodes['TestNamespace\\TestInterface']); + $this->assertInstanceOf(Node\Statement\FunctionDeclaration::class, $defNodes['TestNamespace\\test_function()']); + $this->assertInstanceOf(Node\Statement\ClassDeclaration::class, $defNodes['TestNamespace\\ChildClass']); + $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() { $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); - $parser = new Parser; + $parser = new PhpParser\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(['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()']); + $treeAnalyzer = new TreeAnalyzer($parser, $content, $docBlockFactory, $definitionResolver, $uri); + return $treeAnalyzer->getDefinitionNodes(); } } diff --git a/tests/PhpDocumentLoaderTest.php b/tests/PhpDocumentLoaderTest.php index 7be062d..348f23f 100644 --- a/tests/PhpDocumentLoaderTest.php +++ b/tests/PhpDocumentLoaderTest.php @@ -3,20 +3,14 @@ declare(strict_types = 1); namespace LanguageServer\Tests\Server; -use PHPUnit\Framework\TestCase; -use LanguageServer\Tests\MockProtocolStream; -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 LanguageServer\{ + PhpDocument, PhpDocumentLoader, Project, DefinitionResolver }; -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; class PhpDocumentLoaderTest extends TestCase diff --git a/tests/PhpDocumentTest.php b/tests/PhpDocumentTest.php index 011a231..ae1b5cd 100644 --- a/tests/PhpDocumentTest.php +++ b/tests/PhpDocumentTest.php @@ -3,22 +3,26 @@ declare(strict_types = 1); 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 LanguageServer\Tests\MockProtocolStream; -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 PHPUnit\Framework\TestCase; use function LanguageServer\isVendored; class PhpDocumentTest extends TestCase { public function createDocument(string $uri, string $content) { - $parser = new Parser; + $parser = new PhpParser\Parser(); $docBlockFactory = DocBlockFactory::createInstance(); $index = new Index; $definitionResolver = new DefinitionResolver($index); @@ -36,10 +40,15 @@ class PhpDocumentTest extends TestCase { $document = $this->createDocument('whatever', "getNodeAtPosition(new Position(1, 13)); - $this->assertInstanceOf(Node\Name\FullyQualified::class, $node); + $this->assertQualifiedName($node); $this->assertEquals('SomeClass', (string)$node); } + private function assertQualifiedName($node) + { + $this->assertInstanceOf(Node\QualifiedName::class, $node); + } + public function testIsVendored() { $document = $this->createDocument('file:///dir/vendor/x.php', " new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))), // Namespaced - 'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 10), new Position( 2, 23))), - 'SecondTestNamespace' => new Location($useUri, new Range(new Position( 2, 10), new Position( 2, 29))), - '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\\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\\TestInterface' => new Location($symbolsUri, new Range(new Position(68, 0), new Position(71, 1))), + 'TestNamespace' => new Location($symbolsUri, new Range(new Position( 2, 0), new Position( 2, 24))), + '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\\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\\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\\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::staticTestProperty' => new Location($symbolsUri, new Range(new Position(34, 18), new Position(34, 37))), @@ -112,7 +113,7 @@ abstract class ServerTestCase extends TestCase 'TestNamespace' => [ 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; - 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' => [ 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; ], 'TestNamespace\\TestClass::staticTestProperty' => [ - 0 => new Location($referencesUri, new Range(new Position( 8, 5), 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; + 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, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty; ], '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()' => [ - 0 => new Location($referencesUri, new Range(new Position( 5, 0), new Position( 5, 18))), // $obj->testMethod(); - 1 => new Location($referencesUri, new Range(new Position(38, 0), new Position(38, 32))), // $obj->testProperty->testMethod(); - 2 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))) // $child->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, 30))), // $obj->testProperty->testMethod(); + 2 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod(); ], 'TestNamespace\\test_function()' => [ 0 => new Location($referencesUri, new Range(new Position(10, 0), new Position(10, 13))), @@ -186,7 +187,7 @@ abstract class ServerTestCase extends TestCase ], '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) ], '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; ], 'TestClass::staticTestProperty' => [ - 0 => new Location($globalReferencesUri, new Range(new Position( 8, 5), 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; + 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, 11), new Position(39, 30))) // TestClass::$staticTestProperty[123]->testProperty; ], '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()' => [ - 0 => new Location($globalReferencesUri, new Range(new Position( 5, 0), new Position( 5, 18))), // $obj->testMethod(); - 1 => new Location($globalReferencesUri, new Range(new Position(38, 0), new Position(38, 32))), // $obj->testProperty->testMethod(); - 2 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))) // $child->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, 30))), // $obj->testProperty->testMethod(); + 2 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 23))) // $child->testMethod(); ], 'test_function()' => [ 0 => new Location($globalReferencesUri, new Range(new Position(10, 0), new Position(10, 13))), diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 29851e0..03ba3d8 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -5,15 +5,16 @@ namespace LanguageServer\Tests\Server\TextDocument; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; -use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, CompletionProvider, DefinitionResolver}; -use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex, GlobalIndex, StubsIndex}; +use LanguageServer\{ + Server, LanguageClient, PhpDocumentLoader, DefinitionResolver +}; +use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex}; use LanguageServer\ContentRetriever\FileSystemContentRetriever; use LanguageServer\Protocol\{ TextDocumentIdentifier, TextEdit, Range, Position, - ClientCapabilities, CompletionList, CompletionItem, CompletionItemKind @@ -52,7 +53,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(3, 7) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'testProperty', CompletionItemKind::PROPERTY, @@ -76,7 +77,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(3, 6) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'testProperty', CompletionItemKind::PROPERTY, @@ -100,7 +101,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(8, 5) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( '$var', CompletionItemKind::VARIABLE, @@ -132,7 +133,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(8, 6) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( '$param', CompletionItemKind::VARIABLE, @@ -154,7 +155,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(6, 10) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ // Global TestClass definition (inserted as \TestClass) new CompletionItem( 'TestClass', @@ -223,17 +224,20 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(6, 5) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'TestClass', CompletionItemKind::CLASS_, 'TestNamespace', 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' . "\n\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" . - '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" . - 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.' + '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" . + '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" . + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', + null, + null, + 'TestClass' ) ], true), $items); } @@ -246,7 +250,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 14) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'staticTestProperty', CompletionItemKind::PROPERTY, @@ -267,7 +271,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 11) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'TEST_CLASS_CONST', CompletionItemKind::VARIABLE, @@ -300,7 +304,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 13) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'TEST_CLASS_CONST', CompletionItemKind::VARIABLE, @@ -333,7 +337,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 13) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'TEST_CLASS_CONST', CompletionItemKind::VARIABLE, @@ -366,7 +370,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(6, 6) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'TestClass', CompletionItemKind::CLASS_, @@ -392,7 +396,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 1) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class '), new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone ') ], true), $items); @@ -406,7 +410,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(0, 0) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( 'SomeNamespace', CompletionItemKind::MODULE, @@ -471,7 +475,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(4, 8) )->wait(); - $this->assertEquals(new CompletionList([ + $this->assertCompletionsListSubset(new CompletionList([ new CompletionItem( '$abc2', CompletionItemKind::VARIABLE, @@ -494,4 +498,13 @@ class CompletionTest extends TestCase ) ], 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); + } } diff --git a/tests/Server/TextDocument/Definition/GlobalFallbackTest.php b/tests/Server/TextDocument/Definition/GlobalFallbackTest.php index f3c0771..4e45f9e 100644 --- a/tests/Server/TextDocument/Definition/GlobalFallbackTest.php +++ b/tests/Server/TextDocument/Definition/GlobalFallbackTest.php @@ -5,11 +5,12 @@ namespace LanguageServer\Tests\Server\TextDocument\Definition; use LanguageServer\Tests\MockProtocolStream; 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\ContentRetriever\FileSystemContentRetriever; -use LanguageServer\Protocol\{TextDocumentIdentifier, Position, Range, Location, ClientCapabilities}; -use Sabre\Event\Promise; +use LanguageServer\Protocol\{TextDocumentIdentifier, Position, Range, Location}; class GlobalFallbackTest extends ServerTestCase { diff --git a/tests/Server/TextDocument/Definition/GlobalTest.php b/tests/Server/TextDocument/Definition/GlobalTest.php index b5d7425..0988e2a 100644 --- a/tests/Server/TextDocument/Definition/GlobalTest.php +++ b/tests/Server/TextDocument/Definition/GlobalTest.php @@ -24,7 +24,7 @@ class GlobalTest extends ServerTestCase // namespace keyword $result = $this->textDocument->definition( new TextDocumentIdentifier(pathToUri(realpath(__DIR__ . '/../../../../fixtures/references.php'))), - new Position(2, 4) + new Position(1, 0) )->wait(); $this->assertEquals([], $result); } diff --git a/tests/Server/TextDocument/DidChangeTest.php b/tests/Server/TextDocument/DidChangeTest.php index bdd3b22..4d26ed8 100644 --- a/tests/Server/TextDocument/DidChangeTest.php +++ b/tests/Server/TextDocument/DidChangeTest.php @@ -5,17 +5,16 @@ namespace LanguageServer\Tests\Server\TextDocument; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; -use LanguageServer\{Server, Client, LanguageClient, PhpDocumentLoader, DefinitionResolver}; +use LanguageServer\{ + Server, LanguageClient, PhpDocumentLoader, DefinitionResolver +}; use LanguageServer\ContentRetriever\FileSystemContentRetriever; use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex}; use LanguageServer\Protocol\{ - TextDocumentIdentifier, - TextDocumentItem, VersionedTextDocumentIdentifier, TextDocumentContentChangeEvent, Range, - Position, - ClientCapabilities + Position }; class DidChangeTest extends TestCase diff --git a/tests/Server/TextDocument/DidCloseTest.php b/tests/Server/TextDocument/DidCloseTest.php index b31a58d..42daec5 100644 --- a/tests/Server/TextDocument/DidCloseTest.php +++ b/tests/Server/TextDocument/DidCloseTest.php @@ -5,11 +5,12 @@ namespace LanguageServer\Tests\Server\TextDocument; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; -use LanguageServer\{Server, Client, LanguageClient, PhpDocumentLoader, DefinitionResolver}; +use LanguageServer\{ + Server, LanguageClient, PhpDocumentLoader, DefinitionResolver +}; use LanguageServer\ContentRetriever\FileSystemContentRetriever; use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex}; -use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier, ClientCapabilities}; -use Exception; +use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier}; class DidCloseTest extends TestCase { diff --git a/tests/Server/TextDocument/FormattingTest.php b/tests/Server/TextDocument/FormattingTest.php index abb3d6d..1844891 100644 --- a/tests/Server/TextDocument/FormattingTest.php +++ b/tests/Server/TextDocument/FormattingTest.php @@ -5,14 +5,15 @@ namespace LanguageServer\Tests\Server\TextDocument; use PHPUnit\Framework\TestCase; 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\ContentRetriever\FileSystemContentRetriever; use LanguageServer\Protocol\{ TextDocumentIdentifier, TextDocumentItem, FormattingOptions, - ClientCapabilities, TextEdit, Range, Position diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 4da707a..5010360 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -21,7 +21,7 @@ class HoverTest extends ServerTestCase $reference->range->start )->wait(); $this->assertEquals(new Hover([ - new MarkedString('php', "range->start )->wait(); $this->assertEquals(new Hover([ - new MarkedString('php', "range->end )->wait(); $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); } @@ -165,8 +165,9 @@ class HoverTest extends ServerTestCase new TextDocumentIdentifier($reference->uri), $reference->range->end )->wait(); + // TODO - should pretty print with fqns, like \define, \false. Not yet supported by tolerant-php-parser $this->assertEquals(new Hover([ - new MarkedString('php', "range), $result); } @@ -178,7 +179,7 @@ class HoverTest extends ServerTestCase $uri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); $result = $this->textDocument->hover(new TextDocumentIdentifier($uri), new Position(13, 7))->wait(); $this->assertEquals(new Hover( - [new MarkedString('php', "textDocument->hover(new TextDocumentIdentifier($uri), new Position(22, 11))->wait(); $this->assertEquals(new Hover( [ - new MarkedString('php', "textDocument->hover(new TextDocumentIdentifier($uri), new Position(59, 11))->wait(); $this->assertEquals(new Hover([ - new MarkedString('php', " [ 'start' => [ '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' => [ 'line' => 2, @@ -72,13 +122,14 @@ class ParseErrorsTest extends TestCase 'severity' => DiagnosticSeverity::ERROR, 'code' => null, 'source' => 'php', - 'message' => "Syntax error, unexpected T_CLASS, expecting T_STRING" + 'message' => "'Name' expected." ]] ], json_decode(json_encode($this->args), true)); } public function testParseErrorsWithOnlyStartLine() { + $this->markTestIncomplete('This diagnostic not yet implemented in tolerant-php-parser'); $this->openFile(__DIR__ . '/../../../fixtures/namespace_not_first.php'); $this->assertEquals([ 'whatever', diff --git a/tests/Server/TextDocument/References/GlobalFallbackTest.php b/tests/Server/TextDocument/References/GlobalFallbackTest.php index ac7b355..abfefce 100644 --- a/tests/Server/TextDocument/References/GlobalFallbackTest.php +++ b/tests/Server/TextDocument/References/GlobalFallbackTest.php @@ -3,12 +3,17 @@ declare(strict_types = 1); namespace LanguageServer\Tests\Server\TextDocument\References; -use PHPUnit\Framework\TestCase; -use LanguageServer\Tests\MockProtocolStream; -use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver}; -use LanguageServer\Index\{Index, ProjectIndex, DependenciesIndex}; +use LanguageServer\{ + LanguageClient, PhpDocumentLoader, Server, DefinitionResolver +}; 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; class GlobalFallbackTest extends ServerTestCase diff --git a/tests/Server/Workspace/SymbolTest.php b/tests/Server/Workspace/SymbolTest.php index 65ce804..8f2680d 100644 --- a/tests/Server/Workspace/SymbolTest.php +++ b/tests/Server/Workspace/SymbolTest.php @@ -29,7 +29,7 @@ class SymbolTest extends ServerTestCase $referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); // @codingStandardsIgnoreStart $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 new SymbolInformation('TEST_CONST', SymbolKind::CONSTANT, $this->getDefinitionLocation('TestNamespace\\TEST_CONST'), 'TestNamespace'), new SymbolInformation('TestClass', SymbolKind::CLASS_, $this->getDefinitionLocation('TestNamespace\\TestClass'), 'TestNamespace'), diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php new file mode 100644 index 0000000..649e947 --- /dev/null +++ b/tests/Validation/ValidationTest.php @@ -0,0 +1,155 @@ +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'; +} diff --git a/tests/Validation/cases/WithReturnTypehints.php b/tests/Validation/cases/WithReturnTypehints.php new file mode 100644 index 0000000..045051f --- /dev/null +++ b/tests/Validation/cases/WithReturnTypehints.php @@ -0,0 +1,18 @@ +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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php new file mode 100644 index 0000000..260a8cc --- /dev/null +++ b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php @@ -0,0 +1,11 @@ + TRUE]; +} \ No newline at end of file diff --git a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json new file mode 100644 index 0000000..c9b02d0 --- /dev/null +++ b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/caseStatement1.php b/tests/Validation/cases/caseStatement1.php new file mode 100644 index 0000000..69851aa --- /dev/null +++ b/tests/Validation/cases/caseStatement1.php @@ -0,0 +1,7 @@ +a; + +class A { + public $a = 3; +} \ No newline at end of file diff --git a/tests/Validation/cases/classDefinition1.php.expected.json b/tests/Validation/cases/classDefinition1.php.expected.json new file mode 100644 index 0000000..aad36bb --- /dev/null +++ b/tests/Validation/cases/classDefinition1.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/classProperty1.php b/tests/Validation/cases/classProperty1.php new file mode 100644 index 0000000..000e788 --- /dev/null +++ b/tests/Validation/cases/classProperty1.php @@ -0,0 +1,19 @@ +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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/constants.php b/tests/Validation/cases/constants.php new file mode 100644 index 0000000..23423cf --- /dev/null +++ b/tests/Validation/cases/constants.php @@ -0,0 +1,13 @@ + BYE + ]; + } +} \ No newline at end of file diff --git a/tests/Validation/cases/constants.php.expected.json b/tests/Validation/cases/constants.php.expected.json new file mode 100644 index 0000000..eef5cdd --- /dev/null +++ b/tests/Validation/cases/constants.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/constants2.php b/tests/Validation/cases/constants2.php new file mode 100644 index 0000000..fda345e --- /dev/null +++ b/tests/Validation/cases/constants2.php @@ -0,0 +1,13 @@ + "hi" + ]; + } +} \ No newline at end of file diff --git a/tests/Validation/cases/constants2.php.expected.json b/tests/Validation/cases/constants2.php.expected.json new file mode 100644 index 0000000..6ecb2a6 --- /dev/null +++ b/tests/Validation/cases/constants2.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/constants3.php b/tests/Validation/cases/constants3.php new file mode 100644 index 0000000..7ee1dde --- /dev/null +++ b/tests/Validation/cases/constants3.php @@ -0,0 +1,11 @@ +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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/constants5.php b/tests/Validation/cases/constants5.php new file mode 100644 index 0000000..82f833f --- /dev/null +++ b/tests/Validation/cases/constants5.php @@ -0,0 +1,8 @@ +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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php new file mode 100644 index 0000000..4871946 --- /dev/null +++ b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php @@ -0,0 +1,6 @@ +foo()) { + } + } + + public function foo() { + return $this; + } +} diff --git a/tests/Validation/cases/forLoopReference1.php.expected.json b/tests/Validation/cases/forLoopReference1.php.expected.json new file mode 100644 index 0000000..e861248 --- /dev/null +++ b/tests/Validation/cases/forLoopReference1.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/functionUse.php b/tests/Validation/cases/functionUse.php new file mode 100644 index 0000000..ff3fe9e --- /dev/null +++ b/tests/Validation/cases/functionUse.php @@ -0,0 +1,7 @@ +b(); +}; \ No newline at end of file diff --git a/tests/Validation/cases/functionUse.php.expected.json b/tests/Validation/cases/functionUse.php.expected.json new file mode 100644 index 0000000..5c9af19 --- /dev/null +++ b/tests/Validation/cases/functionUse.php.expected.json @@ -0,0 +1,11 @@ +{ + "references": { + "A": [ + "./functionUse.php" + ], + "A->b()": [ + "./functionUse.php" + ] + }, + "definitions": [] +} \ No newline at end of file diff --git a/tests/Validation/cases/functionUse2.php b/tests/Validation/cases/functionUse2.php new file mode 100644 index 0000000..593e6ac --- /dev/null +++ b/tests/Validation/cases/functionUse2.php @@ -0,0 +1,4 @@ + true + ); +} \ No newline at end of file diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json new file mode 100644 index 0000000..c017d49 --- /dev/null +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess1.php b/tests/Validation/cases/memberAccess1.php new file mode 100644 index 0000000..e1bb231 --- /dev/null +++ b/tests/Validation/cases/memberAccess1.php @@ -0,0 +1,10 @@ +a(); + } +} diff --git a/tests/Validation/cases/memberAccess1.php.expected.json b/tests/Validation/cases/memberAccess1.php.expected.json new file mode 100644 index 0000000..ff4868b --- /dev/null +++ b/tests/Validation/cases/memberAccess1.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess2.php b/tests/Validation/cases/memberAccess2.php new file mode 100644 index 0000000..e1bb231 --- /dev/null +++ b/tests/Validation/cases/memberAccess2.php @@ -0,0 +1,10 @@ +a(); + } +} diff --git a/tests/Validation/cases/memberAccess2.php.expected.json b/tests/Validation/cases/memberAccess2.php.expected.json new file mode 100644 index 0000000..a6707d0 --- /dev/null +++ b/tests/Validation/cases/memberAccess2.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess3.php b/tests/Validation/cases/memberAccess3.php new file mode 100644 index 0000000..b2078d7 --- /dev/null +++ b/tests/Validation/cases/memberAccess3.php @@ -0,0 +1,13 @@ +prefixesPsr0 = ComposerStaticInitIncludePath::$prefixesPsr0; + + }, null, ClassLoader::class); + } +} diff --git a/tests/Validation/cases/memberAccess3.php.expected.json b/tests/Validation/cases/memberAccess3.php.expected.json new file mode 100644 index 0000000..df58d1e --- /dev/null +++ b/tests/Validation/cases/memberAccess3.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess4.php b/tests/Validation/cases/memberAccess4.php new file mode 100644 index 0000000..dd1b2fb --- /dev/null +++ b/tests/Validation/cases/memberAccess4.php @@ -0,0 +1,10 @@ +toString()); + } +} diff --git a/tests/Validation/cases/memberAccess4.php.expected.json b/tests/Validation/cases/memberAccess4.php.expected.json new file mode 100644 index 0000000..1cfabb3 --- /dev/null +++ b/tests/Validation/cases/memberAccess4.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess5.php b/tests/Validation/cases/memberAccess5.php new file mode 100644 index 0000000..7061acb --- /dev/null +++ b/tests/Validation/cases/memberAccess5.php @@ -0,0 +1,11 @@ +args) { }; + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess5.php.expected.json b/tests/Validation/cases/memberAccess5.php.expected.json new file mode 100644 index 0000000..ae2b4ac --- /dev/null +++ b/tests/Validation/cases/memberAccess5.php.expected.json @@ -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 + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberCall1.php b/tests/Validation/cases/memberCall1.php new file mode 100644 index 0000000..4f7ebed --- /dev/null +++ b/tests/Validation/cases/memberCall1.php @@ -0,0 +1,16 @@ +getAccount(); + } + + } +} \ No newline at end of file diff --git a/tests/Validation/cases/memberCall1.php.expected.json b/tests/Validation/cases/memberCall1.php.expected.json new file mode 100644 index 0000000..4cf2cd8 --- /dev/null +++ b/tests/Validation/cases/memberCall1.php.expected.json @@ -0,0 +1,70 @@ +{ + "references": { + "MyNamespace\\AccountInterface": [ + "./memberCall1.php" + ], + "MyNamespace\\A": [ + "./memberCall1.php" + ], + "MyNamespace\\AccountInterface->getAccount()": [ + "./memberCall1.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./memberCall1.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": "./memberCall1.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class ParseErrorsTest", + "documentation": null + }, + "MyNamespace\\ParseErrorsTest->setAccount()": { + "fqn": "MyNamespace\\ParseErrorsTest->setAccount()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "setAccount", + "kind": 6, + "location": { + "uri": "./memberCall1.php" + }, + "containerName": "MyNamespace\\ParseErrorsTest" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "public function setAccount(AccountInterface $account)", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/multipleNamespaces.php b/tests/Validation/cases/multipleNamespaces.php new file mode 100644 index 0000000..0743635 --- /dev/null +++ b/tests/Validation/cases/multipleNamespaces.php @@ -0,0 +1,17 @@ +b(); + } +} \ No newline at end of file diff --git a/tests/Validation/cases/multipleNamespaces.php.expected.json b/tests/Validation/cases/multipleNamespaces.php.expected.json new file mode 100644 index 0000000..2ed59de --- /dev/null +++ b/tests/Validation/cases/multipleNamespaces.php.expected.json @@ -0,0 +1,130 @@ +{ + "references": { + "MyNamespace2\\MyNamespace1\\B": [ + "./multipleNamespaces.php" + ], + "MyNamespace2\\MyNamespace1": [ + "./multipleNamespaces.php" + ], + "MyNamespace2": [ + "./multipleNamespaces.php" + ], + "MyNamespace2\\A->b()": [ + "./multipleNamespaces.php" + ] + }, + "definitions": { + "MyNamespace1": { + "fqn": "MyNamespace1", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace1", + "kind": 3, + "location": { + "uri": "./multipleNamespaces.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "namespace MyNamespace1;", + "documentation": null + }, + "MyNamespace1\\B": { + "fqn": "MyNamespace1\\B", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "B", + "kind": 5, + "location": { + "uri": "./multipleNamespaces.php" + }, + "containerName": "MyNamespace1" + }, + "type": null, + "declarationLine": "class B {", + "documentation": null + }, + "MyNamespace1\\B->b()": { + "fqn": "MyNamespace1\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./multipleNamespaces.php" + }, + "containerName": "MyNamespace1\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace2": { + "fqn": "MyNamespace2", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace2", + "kind": 3, + "location": { + "uri": "./multipleNamespaces.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "namespace MyNamespace2;", + "documentation": null + }, + "MyNamespace2\\A": { + "fqn": "MyNamespace2\\A", + "extends": [ + "MyNamespace2\\MyNamespace1\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./multipleNamespaces.php" + }, + "containerName": "MyNamespace2" + }, + "type": null, + "declarationLine": "class A extends MyNamespace1\\B {", + "documentation": null + }, + "MyNamespace2\\A->a()": { + "fqn": "MyNamespace2\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./multipleNamespaces.php" + }, + "containerName": "MyNamespace2\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/multiplePreceedingComments.php b/tests/Validation/cases/multiplePreceedingComments.php new file mode 100644 index 0000000..1490385 --- /dev/null +++ b/tests/Validation/cases/multiplePreceedingComments.php @@ -0,0 +1,15 @@ +fn()": { + "fqn": "Foo->fn()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "fn", + "kind": 6, + "location": { + "uri": "./multiplePreceedingComments.php" + }, + "containerName": "Foo" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type": {}, + "declarationLine": "public function fn()", + "documentation": "Foo" + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/nameToken.php b/tests/Validation/cases/nameToken.php new file mode 100644 index 0000000..770aef2 --- /dev/null +++ b/tests/Validation/cases/nameToken.php @@ -0,0 +1,7 @@ +b()": { + "fqn": "A->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./nameToken.php" + }, + "containerName": "A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/namespaces2.php b/tests/Validation/cases/namespaces2.php new file mode 100644 index 0000000..a49478d --- /dev/null +++ b/tests/Validation/cases/namespaces2.php @@ -0,0 +1,5 @@ +foo(); + } + + private function foo() { + } +} \ No newline at end of file diff --git a/tests/Validation/cases/newStatic.php.expected.json b/tests/Validation/cases/newStatic.php.expected.json new file mode 100644 index 0000000..cafe114 --- /dev/null +++ b/tests/Validation/cases/newStatic.php.expected.json @@ -0,0 +1,63 @@ +{ + "references": { + "static": [ + "\/Users\/roblou\/code\/php-language-server\/tests\/Validation\/..\/..\/validation\/frameworks\/_cases\/newStatic.php" + ], + "NewStatic->foo()": [ + "\/Users\/roblou\/code\/php-language-server\/tests\/Validation\/..\/..\/validation\/frameworks\/_cases\/newStatic.php" + ] + }, + "definitions": { + "NewStatic": { + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "NewStatic", + "kind": 5, + "location": { + "uri": "\/Users\/roblou\/code\/php-language-server\/tests\/Validation\/..\/..\/validation\/frameworks\/_cases\/newStatic.php" + }, + "containerName": "" + }, + "type__class": "LanguageServer\\Tests\\ValidationTest", + "type": null, + "documentation": null + }, + "NewStatic::main()": { + "extends": [], + "isGlobal": false, + "isStatic": true, + "canBeInstantiated": false, + "symbolInformation": { + "name": "main", + "kind": 6, + "location": { + "uri": "\/Users\/roblou\/code\/php-language-server\/tests\/Validation\/..\/..\/validation\/frameworks\/_cases\/newStatic.php" + }, + "containerName": "NewStatic" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "documentation": null + }, + "NewStatic->foo()": { + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "foo", + "kind": 6, + "location": { + "uri": "\/Users\/roblou\/code\/php-language-server\/tests\/Validation\/..\/..\/validation\/frameworks\/_cases\/newStatic.php" + }, + "containerName": "NewStatic" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/objectCreation.php b/tests/Validation/cases/objectCreation.php new file mode 100644 index 0000000..01437d3 --- /dev/null +++ b/tests/Validation/cases/objectCreation.php @@ -0,0 +1,9 @@ +inline_diff_renderer; + } +} diff --git a/tests/Validation/cases/objectCreation.php.expected.json b/tests/Validation/cases/objectCreation.php.expected.json new file mode 100644 index 0000000..cea0343 --- /dev/null +++ b/tests/Validation/cases/objectCreation.php.expected.json @@ -0,0 +1,64 @@ +{ + "references": { + "MyNamespace\\A->inline_diff_renderer": [ + "./objectCreation.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./objectCreation.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": "./objectCreation.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./objectCreation.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/objectCreation2.php b/tests/Validation/cases/objectCreation2.php new file mode 100644 index 0000000..494cd7f --- /dev/null +++ b/tests/Validation/cases/objectCreation2.php @@ -0,0 +1,12 @@ +hi(); + } +} diff --git a/tests/Validation/cases/objectCreation2.php.expected.json b/tests/Validation/cases/objectCreation2.php.expected.json new file mode 100644 index 0000000..7f856b1 --- /dev/null +++ b/tests/Validation/cases/objectCreation2.php.expected.json @@ -0,0 +1,85 @@ +{ + "references": { + "MyNamespace\\B->hi()": [ + "./objectCreation2.php" + ], + "MyNamespace\\B": [ + "./objectCreation2.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./objectCreation2.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "namespace MyNamespace;", + "documentation": null + }, + "MyNamespace\\B": { + "fqn": "MyNamespace\\B", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "B", + "kind": 5, + "location": { + "uri": "./objectCreation2.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class B {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./objectCreation2.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./objectCreation2.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/objectCreation3.php b/tests/Validation/cases/objectCreation3.php new file mode 100644 index 0000000..dc91dcd --- /dev/null +++ b/tests/Validation/cases/objectCreation3.php @@ -0,0 +1,9 @@ +textDocument = new class($this->args) + { + }; + } +} diff --git a/tests/Validation/cases/objectCreation3.php.expected.json b/tests/Validation/cases/objectCreation3.php.expected.json new file mode 100644 index 0000000..c6dcab9 --- /dev/null +++ b/tests/Validation/cases/objectCreation3.php.expected.json @@ -0,0 +1,46 @@ +{ + "references": { + "A->args": [ + "./objectCreation3.php" + ] + }, + "definitions": { + "A": { + "fqn": "A", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./objectCreation3.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "class A {", + "documentation": null + }, + "A->a()": { + "fqn": "A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./objectCreation3.php" + }, + "containerName": "A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/param1.php b/tests/Validation/cases/param1.php new file mode 100644 index 0000000..a7128fe --- /dev/null +++ b/tests/Validation/cases/param1.php @@ -0,0 +1,6 @@ +getAccount(); + } + } +} diff --git a/tests/Validation/cases/parameterTypeResolution1.php.expected.json b/tests/Validation/cases/parameterTypeResolution1.php.expected.json new file mode 100644 index 0000000..2910eaf --- /dev/null +++ b/tests/Validation/cases/parameterTypeResolution1.php.expected.json @@ -0,0 +1,66 @@ +{ + "references": { + "ParamType": [ + "./_cases/parameterTypeResolution1.php" + ], + "static": [ + "./_cases/parameterTypeResolution1.php" + ], + "ParamType->getAccount()": [ + "./_cases/parameterTypeResolution1.php" + ] + }, + "definitions": { + "ParamType": { + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "ParamType", + "kind": 5, + "location": { + "uri": "./_cases/parameterTypeResolution1.php" + }, + "containerName": "" + }, + "type__class": "LanguageServer\\Tests\\ValidationTest", + "type": null, + "documentation": null + }, + "ParamType->setAccount()": { + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "setAccount", + "kind": 6, + "location": { + "uri": "./_cases/parameterTypeResolution1.php" + }, + "containerName": "ParamType" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "documentation": null + }, + "ParamType->getAccount()": { + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "getAccount", + "kind": 6, + "location": { + "uri": "./_cases/parameterTypeResolution1.php" + }, + "containerName": "ParamType" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/parent1.php b/tests/Validation/cases/parent1.php new file mode 100644 index 0000000..6708e7b --- /dev/null +++ b/tests/Validation/cases/parent1.php @@ -0,0 +1,15 @@ +b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./parent1.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./parent1.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./parent1.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/parent2.php b/tests/Validation/cases/parent2.php new file mode 100644 index 0000000..bb2955e --- /dev/null +++ b/tests/Validation/cases/parent2.php @@ -0,0 +1,15 @@ +b()": { + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./_cases/parent2.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "documentation": null + }, + "MyNamespace\\A": { + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./_cases/parent2.php" + }, + "containerName": "MyNamespace" + }, + "type__class": "LanguageServer\\Tests\\ValidationTest", + "type": null, + "documentation": null + }, + "MyNamespace\\A->a()": { + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./_cases/parent2.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/parent3.php b/tests/Validation/cases/parent3.php new file mode 100644 index 0000000..813194d --- /dev/null +++ b/tests/Validation/cases/parent3.php @@ -0,0 +1,15 @@ +b(); + } +} diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json new file mode 100644 index 0000000..2c4915d --- /dev/null +++ b/tests/Validation/cases/parent3.php.expected.json @@ -0,0 +1,109 @@ +{ + "references": { + "MyNamespace\\B": [ + "./parent3.php" + ], + "MyNamespace\\B->b()": [ + "./parent3.php" + ], + "parent": [ + "./parent3.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./parent3.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "namespace MyNamespace;", + "documentation": null + }, + "MyNamespace\\B": { + "fqn": "MyNamespace\\B", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "B", + "kind": 5, + "location": { + "uri": "./parent3.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class B {", + "documentation": null + }, + "MyNamespace\\B->b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./parent3.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./parent3.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./parent3.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/propertyName1.php b/tests/Validation/cases/propertyName1.php new file mode 100644 index 0000000..c53cf54 --- /dev/null +++ b/tests/Validation/cases/propertyName1.php @@ -0,0 +1,12 @@ +mainPropertyName": { + "fqn": "MyClass->mainPropertyName", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "mainPropertyName", + "kind": 7, + "location": { + "uri": "./propertyName1.php" + }, + "containerName": "MyClass" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\String_", + "type": {}, + "declarationLine": "protected $mainPropertyName;", + "documentation": "The name of the main property, or NULL if there is none." + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/propertyName2.php b/tests/Validation/cases/propertyName2.php new file mode 100644 index 0000000..d79f758 --- /dev/null +++ b/tests/Validation/cases/propertyName2.php @@ -0,0 +1,10 @@ +mainPropertyName": { + "fqn": "MyClass->mainPropertyName", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "mainPropertyName", + "kind": 7, + "location": { + "uri": "./propertyName2.php" + }, + "containerName": "MyClass" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\String_", + "type": {}, + "declarationLine": "protected $mainPropertyName;", + "documentation": "The name of the main property, or NULL if there is none." + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/returnType.php b/tests/Validation/cases/returnType.php new file mode 100644 index 0000000..5aad940 --- /dev/null +++ b/tests/Validation/cases/returnType.php @@ -0,0 +1,13 @@ +testProperty; \ No newline at end of file diff --git a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json new file mode 100644 index 0000000..ab1e214 --- /dev/null +++ b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json @@ -0,0 +1,55 @@ +{ + "references": { + "TestInterface": [ + "./scopedPropertyAccess5.php" + ], + "TestClass": [ + "./scopedPropertyAccess5.php" + ], + "TestClass::$testProperty": [ + "./scopedPropertyAccess5.php" + ], + "TestClass::$staticTestProperty": [ + "./scopedPropertyAccess5.php" + ] + }, + "definitions": { + "TestClass": { + "fqn": "TestClass", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "TestClass", + "kind": 5, + "location": { + "uri": "./scopedPropertyAccess5.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "class TestClass implements TestInterface {", + "documentation": null + }, + "TestClass::$testProperty": { + "fqn": "TestClass::$testProperty", + "extends": [], + "isGlobal": false, + "isStatic": true, + "canBeInstantiated": false, + "symbolInformation": { + "name": "testProperty", + "kind": 7, + "location": { + "uri": "./scopedPropertyAccess5.php" + }, + "containerName": "TestClass" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Array_", + "type": {}, + "declarationLine": "public static $testProperty;", + "documentation": "Lorem excepteur officia sit anim velit veniam enim." + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/self1.php b/tests/Validation/cases/self1.php new file mode 100644 index 0000000..e286381 --- /dev/null +++ b/tests/Validation/cases/self1.php @@ -0,0 +1,16 @@ +b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./self1.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./self1.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./self1.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/self2.php b/tests/Validation/cases/self2.php new file mode 100644 index 0000000..50a2c6e --- /dev/null +++ b/tests/Validation/cases/self2.php @@ -0,0 +1,15 @@ +b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./self2.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./self2.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./self2.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/self3.php b/tests/Validation/cases/self3.php new file mode 100644 index 0000000..5034502 --- /dev/null +++ b/tests/Validation/cases/self3.php @@ -0,0 +1,15 @@ +b(); + } +} diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json new file mode 100644 index 0000000..3d5d4ad --- /dev/null +++ b/tests/Validation/cases/self3.php.expected.json @@ -0,0 +1,109 @@ +{ + "references": { + "MyNamespace\\B": [ + "./self3.php" + ], + "MyNamespace\\A->b()": [ + "./self3.php" + ], + "self": [ + "./self3.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./self3.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "namespace MyNamespace;", + "documentation": null + }, + "MyNamespace\\B": { + "fqn": "MyNamespace\\B", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "B", + "kind": 5, + "location": { + "uri": "./self3.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class B {", + "documentation": null + }, + "MyNamespace\\B->b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./self3.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./self3.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./self3.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/self4.php b/tests/Validation/cases/self4.php new file mode 100644 index 0000000..4f0ffe1 --- /dev/null +++ b/tests/Validation/cases/self4.php @@ -0,0 +1,13 @@ +addTestFile(__DIR__ . DS . 'Database' . DS . 'ConnectionTest.php'); + + } +} \ No newline at end of file diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json new file mode 100644 index 0000000..5fac53f --- /dev/null +++ b/tests/Validation/cases/self4.php.expected.json @@ -0,0 +1,73 @@ +{ + "references": { + "self": [ + "./self4.php" + ], + "MyNamespace\\A->addTestFile()": [ + "./self4.php" + ], + "MyNamespace\\DS": [ + "./self4.php" + ], + "DS": [ + "./self4.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./self4.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": "./self4.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": "./self4.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "public static function suite()", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/self5.php b/tests/Validation/cases/self5.php new file mode 100644 index 0000000..0fcd08f --- /dev/null +++ b/tests/Validation/cases/self5.php @@ -0,0 +1,12 @@ +assertTrue("HI"); + } +} \ No newline at end of file diff --git a/tests/Validation/cases/self5.php.expected.json b/tests/Validation/cases/self5.php.expected.json new file mode 100644 index 0000000..781cd24 --- /dev/null +++ b/tests/Validation/cases/self5.php.expected.json @@ -0,0 +1,64 @@ +{ + "references": { + "MyNamespace\\A->assertTrue()": [ + "./self5.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./self5.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": "./self5.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A", + "documentation": null + }, + "MyNamespace\\A->typesProvider()": { + "fqn": "MyNamespace\\A->typesProvider()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "typesProvider", + "kind": 6, + "location": { + "uri": "./self5.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "public function typesProvider()", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/static1.php b/tests/Validation/cases/static1.php new file mode 100644 index 0000000..f512e00 --- /dev/null +++ b/tests/Validation/cases/static1.php @@ -0,0 +1,15 @@ +b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./static1.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./static1.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./static1.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/static2.php b/tests/Validation/cases/static2.php new file mode 100644 index 0000000..088682a --- /dev/null +++ b/tests/Validation/cases/static2.php @@ -0,0 +1,15 @@ +b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./static2.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./static2.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./static2.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/static3.php b/tests/Validation/cases/static3.php new file mode 100644 index 0000000..62a3025 --- /dev/null +++ b/tests/Validation/cases/static3.php @@ -0,0 +1,15 @@ +b(); + } +} diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json new file mode 100644 index 0000000..bb556bf --- /dev/null +++ b/tests/Validation/cases/static3.php.expected.json @@ -0,0 +1,109 @@ +{ + "references": { + "MyNamespace\\B": [ + "./static3.php" + ], + "MyNamespace\\b()": [ + "./static3.php" + ], + "b()": [ + "./static3.php" + ] + }, + "definitions": { + "MyNamespace": { + "fqn": "MyNamespace", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "MyNamespace", + "kind": 3, + "location": { + "uri": "./static3.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "namespace MyNamespace;", + "documentation": null + }, + "MyNamespace\\B": { + "fqn": "MyNamespace\\B", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "B", + "kind": 5, + "location": { + "uri": "./static3.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class B {", + "documentation": null + }, + "MyNamespace\\B->b()": { + "fqn": "MyNamespace\\B->b()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "b", + "kind": 6, + "location": { + "uri": "./static3.php" + }, + "containerName": "MyNamespace\\B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function b() {", + "documentation": null + }, + "MyNamespace\\A": { + "fqn": "MyNamespace\\A", + "extends": [ + "MyNamespace\\B" + ], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "A", + "kind": 5, + "location": { + "uri": "./static3.php" + }, + "containerName": "MyNamespace" + }, + "type": null, + "declarationLine": "class A extends B {", + "documentation": null + }, + "MyNamespace\\A->a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./static3.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/static4.php b/tests/Validation/cases/static4.php new file mode 100644 index 0000000..bada62f --- /dev/null +++ b/tests/Validation/cases/static4.php @@ -0,0 +1,9 @@ +a()": { + "fqn": "MyNamespace\\A->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./static4.php" + }, + "containerName": "MyNamespace\\A" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/staticInArray.php b/tests/Validation/cases/staticInArray.php new file mode 100644 index 0000000..fbfaba9 --- /dev/null +++ b/tests/Validation/cases/staticInArray.php @@ -0,0 +1,5 @@ +hi"; + } +} \ No newline at end of file diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json new file mode 100644 index 0000000..5ac910b --- /dev/null +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -0,0 +1,61 @@ +{ + "references": [], + "definitions": { + "B": { + "fqn": "B", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "B", + "kind": 5, + "location": { + "uri": "./stringVariable.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "class B", + "documentation": null + }, + "B->hi": { + "fqn": "B->hi", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "hi", + "kind": 7, + "location": { + "uri": "./stringVariable.php" + }, + "containerName": "B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Integer", + "type": {}, + "declarationLine": "public $hi;", + "documentation": null + }, + "B->a()": { + "fqn": "B->a()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "a", + "kind": 6, + "location": { + "uri": "./stringVariable.php" + }, + "containerName": "B" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "function a () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php new file mode 100644 index 0000000..61fe57e --- /dev/null +++ b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php @@ -0,0 +1,5 @@ +bar = 'hello'; + } +} diff --git a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json new file mode 100644 index 0000000..f072722 --- /dev/null +++ b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json @@ -0,0 +1,68 @@ +{ + "references": { + "CURLAUTH_BASIC": [ + "./verifyFqsenOnClassProperty.php" + ], + "Foo->bar": [ + "./verifyFqsenOnClassProperty.php" + ] + }, + "definitions": { + "Foo": { + "fqn": "Foo", + "extends": [], + "isGlobal": true, + "isStatic": false, + "canBeInstantiated": true, + "symbolInformation": { + "name": "Foo", + "kind": 5, + "location": { + "uri": "./verifyFqsenOnClassProperty.php" + }, + "containerName": "" + }, + "type": null, + "declarationLine": "class Foo {", + "documentation": null + }, + "Foo->bar": { + "fqn": "Foo->bar", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "bar", + "kind": 7, + "location": { + "uri": "./verifyFqsenOnClassProperty.php" + }, + "containerName": "Foo" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type": {}, + "declarationLine": "protected $bar;", + "documentation": null + }, + "Foo->foo()": { + "fqn": "Foo->foo()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "foo", + "kind": 6, + "location": { + "uri": "./verifyFqsenOnClassProperty.php" + }, + "containerName": "Foo" + }, + "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type": {}, + "declarationLine": "public function foo () {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/disabled.json b/tests/Validation/disabled.json new file mode 100644 index 0000000..5e223ef --- /dev/null +++ b/tests/Validation/disabled.json @@ -0,0 +1,7 @@ +[ + "forLoopReference1.php", + "namespaces3.php", + "parameterTypeResolution1.php", + "parent2.php", + "newStatic.php" +] \ No newline at end of file diff --git a/validation/frameworks/cakephp b/validation/frameworks/cakephp new file mode 160000 index 0000000..0450ecc --- /dev/null +++ b/validation/frameworks/cakephp @@ -0,0 +1 @@ +Subproject commit 0450ecc030ae37ca0a3f8c0e4e56ce9ceec8402d diff --git a/validation/frameworks/codeigniter b/validation/frameworks/codeigniter new file mode 160000 index 0000000..c06bc67 --- /dev/null +++ b/validation/frameworks/codeigniter @@ -0,0 +1 @@ +Subproject commit c06bc67d6c4059b3d1050221d5b1624ac4e2f1f8 diff --git a/validation/frameworks/drupal b/validation/frameworks/drupal new file mode 160000 index 0000000..ac7313d --- /dev/null +++ b/validation/frameworks/drupal @@ -0,0 +1 @@ +Subproject commit ac7313dda0644f35031428ff954b6d9e6f90e857 diff --git a/validation/frameworks/math-php b/validation/frameworks/math-php new file mode 160000 index 0000000..601baa6 --- /dev/null +++ b/validation/frameworks/math-php @@ -0,0 +1 @@ +Subproject commit 601baa6267cc4a357c8032d9407a0206975aa26e diff --git a/validation/frameworks/php-language-server b/validation/frameworks/php-language-server new file mode 160000 index 0000000..546660f --- /dev/null +++ b/validation/frameworks/php-language-server @@ -0,0 +1 @@ +Subproject commit 546660f957623b2cdc179fe107b28581d60ba190 diff --git a/validation/frameworks/phpunit b/validation/frameworks/phpunit new file mode 160000 index 0000000..bb74d4e --- /dev/null +++ b/validation/frameworks/phpunit @@ -0,0 +1 @@ +Subproject commit bb74d4eac541cf63f3454ca5fa31c4a20391032b diff --git a/validation/frameworks/symfony b/validation/frameworks/symfony new file mode 160000 index 0000000..9d9f628 --- /dev/null +++ b/validation/frameworks/symfony @@ -0,0 +1 @@ +Subproject commit 9d9f628d926aaf34e020ddccb4454ff7c4ce5ceb diff --git a/validation/frameworks/tolerant-php-parser b/validation/frameworks/tolerant-php-parser new file mode 160000 index 0000000..6ce1e1f --- /dev/null +++ b/validation/frameworks/tolerant-php-parser @@ -0,0 +1 @@ +Subproject commit 6ce1e1f978f1c17d555b83660b97899f0d9dbeec diff --git a/validation/frameworks/wordpress b/validation/frameworks/wordpress new file mode 160000 index 0000000..afdef17 --- /dev/null +++ b/validation/frameworks/wordpress @@ -0,0 +1 @@ +Subproject commit afdef17903c52c15642be01169495e08a08b04a7 From 42d0c7b714f91d272207380737eb075ee2d46b8a Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Fri, 9 Jun 2017 22:12:32 +0200 Subject: [PATCH 010/117] Improve handling of abstract classes (#391) --- src/DefinitionResolver.php | 8 ++++++-- src/LanguageServer.php | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 0570ed1..cf2f8a6 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -114,7 +114,7 @@ class DefinitionResolver // For everything else, get the doc block summary corresponding to the current node. $docBlock = $this->getDocBlock($node); if ($docBlock !== null) { - // check wether we have a description, when true, add a new paragraph + // check whether we have a description, when true, add a new paragraph // with the description $description = $docBlock->getDescription()->render(); @@ -174,7 +174,11 @@ class DefinitionResolver $def->fqn = $fqn; // Determines whether the suggestion will show after "new" - $def->canBeInstantiated = $node instanceof Node\Statement\ClassDeclaration; + $def->canBeInstantiated = ( + $node instanceof Node\Statement\ClassDeclaration && + // check whether it is not an abstract class + ($node->abstractOrFinalModifier === null || $node->abstractOrFinalModifier->kind !== PhpParser\TokenKind::AbstractKeyword) + ); // Interfaces, classes, traits, namespaces, functions, and global const elements $def->isGlobal = ( diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 173abfe..118dd93 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -106,7 +106,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher protected $definitionResolver; /** - * @param PotocolReader $reader + * @param ProtocolReader $reader * @param ProtocolWriter $writer */ public function __construct(ProtocolReader $reader, ProtocolWriter $writer) @@ -132,7 +132,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher // If a ResponseError is thrown, send it back in the Response $error = $e; } catch (Throwable $e) { - // If an unexpected error occured, send back an INTERNAL_ERROR error response + // If an unexpected error occurred, send back an INTERNAL_ERROR error response $error = new AdvancedJsonRpc\Error( (string)$e, AdvancedJsonRpc\ErrorCode::INTERNAL_ERROR, From 7b72b38fd9533b3daedd5af17eeab3a6b14a3e5e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 10 Jun 2017 01:55:41 -0700 Subject: [PATCH 011/117] Assert that references array is equal, not a subset, and update expected.json files (#395) --- tests/Validation/ValidationTest.php | 7 +------ .../cases/WithReturnTypehints.php.expected.json | 6 ++++++ tests/Validation/cases/exceptions1.php.expected.json | 6 +++++- tests/Validation/cases/functionUse2.php.expected.json | 3 +++ .../cases/magicConstantsShouldBeGlobal.php.expected.json | 9 ++++++++- tests/Validation/cases/magicConsts.php.expected.json | 6 +++++- tests/Validation/cases/memberAccess5.php.expected.json | 6 +++++- tests/Validation/cases/namespaces8.php.expected.json | 6 ++++++ tests/Validation/cases/self4.php.expected.json | 6 ++++++ 9 files changed, 45 insertions(+), 10 deletions(-) diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index 649e947..8fa7442 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -64,12 +64,7 @@ class ValidationTest extends TestCase 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.'); - } + $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)); diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index 50801d9..166786a 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -6,6 +6,12 @@ "self": [ "./WithReturnTypehints.php" ], + "Fixtures\\Prophecy\\__CLASS__": [ + "./WithReturnTypehints.php" + ], + "__CLASS__": [ + "./WithReturnTypehints.php" + ], "parent": [ "./WithReturnTypehints.php" ] diff --git a/tests/Validation/cases/exceptions1.php.expected.json b/tests/Validation/cases/exceptions1.php.expected.json index d74deff..4a13354 100644 --- a/tests/Validation/cases/exceptions1.php.expected.json +++ b/tests/Validation/cases/exceptions1.php.expected.json @@ -1,5 +1,9 @@ { - "references": [], + "references": { + "MyNamespace\\Exception": [ + "./exceptions1.php" + ] + }, "definitions": { "MyNamespace": { "fqn": "MyNamespace", diff --git a/tests/Validation/cases/functionUse2.php.expected.json b/tests/Validation/cases/functionUse2.php.expected.json index f3609bd..320cc41 100644 --- a/tests/Validation/cases/functionUse2.php.expected.json +++ b/tests/Validation/cases/functionUse2.php.expected.json @@ -3,6 +3,9 @@ "LanguageServer": [ "./functionUse2.php" ], + "LanguageServer\\pathToUri()": [ + "./functionUse2.php" + ], "LanguageServer\\timeout()": [ "./functionUse2.php" ] diff --git a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json index 36a2942..b61333f 100644 --- a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json +++ b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json @@ -1,5 +1,12 @@ { - "references": [], + "references": { + "B\\__FILE__": [ + "./magicConstantsShouldBeGlobal.php" + ], + "__FILE__": [ + "./magicConstantsShouldBeGlobal.php" + ] + }, "definitions": { "B": { "fqn": "B", diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json index c017d49..6641af3 100644 --- a/tests/Validation/cases/magicConsts.php.expected.json +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -1,5 +1,9 @@ { - "references": [], + "references": { + "__CLASS__": [ + "./magicConsts.php" + ] + }, "definitions": { "A": { "fqn": "A", diff --git a/tests/Validation/cases/memberAccess5.php.expected.json b/tests/Validation/cases/memberAccess5.php.expected.json index ae2b4ac..5023cd6 100644 --- a/tests/Validation/cases/memberAccess5.php.expected.json +++ b/tests/Validation/cases/memberAccess5.php.expected.json @@ -1,5 +1,9 @@ { - "references": [], + "references": { + "MyNamespace\\ParseErrorsTest->args": [ + "./memberAccess5.php" + ] + }, "definitions": { "MyNamespace": { "fqn": "MyNamespace", diff --git a/tests/Validation/cases/namespaces8.php.expected.json b/tests/Validation/cases/namespaces8.php.expected.json index 2fbd1fb..7a77f7c 100644 --- a/tests/Validation/cases/namespaces8.php.expected.json +++ b/tests/Validation/cases/namespaces8.php.expected.json @@ -2,6 +2,12 @@ "references": { "LanguageServer": [ "./namespaces8.php" + ], + "LanguageServer\\pathToUri()": [ + "./namespaces8.php" + ], + "LanguageServer\\uriToPath()": [ + "./namespaces8.php" ] }, "definitions": { diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index 5fac53f..9a01912 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -6,6 +6,12 @@ "MyNamespace\\A->addTestFile()": [ "./self4.php" ], + "MyNamespace\\__DIR__": [ + "./self4.php" + ], + "__DIR__": [ + "./self4.php" + ], "MyNamespace\\DS": [ "./self4.php" ], From f10680e4419758e8986393c57cc78a7d9f2cbbbe Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 10 Jun 2017 02:10:15 -0700 Subject: [PATCH 012/117] Fix variable type from method return value, add tests (#393) --- fixtures/completion/method_return_type.php | 11 ++++ .../completion/static_method_return_type.php | 12 ++++ src/DefinitionResolver.php | 10 +++ tests/LanguageServerTest.php | 4 +- tests/Server/TextDocument/CompletionTest.php | 44 +++++++++++++ tests/Validation/ValidationTest.php | 3 +- .../WithReturnTypehints.php.expected.json | 6 +- ...rrayValueShouldBeBoolean.php.expected.json | 2 +- .../cases/classDefinition1.php.expected.json | 2 +- .../cases/classProperty1.php.expected.json | 4 +- .../cases/constants.php.expected.json | 2 +- .../cases/constants2.php.expected.json | 2 +- .../cases/constants3.php.expected.json | 2 +- .../cases/constants4.php.expected.json | 2 +- .../cases/constants5.php.expected.json | 2 +- ...tsInFunctionParamDefault.php.expected.json | 2 +- .../cases/magicConsts.php.expected.json | 2 +- .../cases/memberAccess1.php.expected.json | 2 +- .../cases/memberAccess2.php.expected.json | 2 +- .../cases/memberAccess3.php.expected.json | 2 +- .../cases/memberAccess4.php.expected.json | 2 +- .../cases/memberAccess5.php.expected.json | 2 +- .../cases/memberCall1.php.expected.json | 2 +- tests/Validation/cases/methodReturnType.php | 7 ++ .../cases/methodReturnType.php.expected.json | 46 +++++++++++++ .../multipleNamespaces.php.expected.json | 4 +- ...ltiplePreceedingComments.php.expected.json | 2 +- .../cases/nameToken.php.expected.json | 2 +- .../cases/objectCreation.php.expected.json | 2 +- .../cases/objectCreation2.php.expected.json | 2 +- .../cases/objectCreation3.php.expected.json | 2 +- .../Validation/cases/param1.php.expected.json | 2 +- .../cases/parent1.php.expected.json | 4 +- .../cases/parent3.php.expected.json | 4 +- .../cases/propertyName1.php.expected.json | 2 +- .../cases/propertyName2.php.expected.json | 2 +- .../cases/returnType.php.expected.json | 2 +- .../scopedPropertyAccess.php.expected.json | 2 +- .../scopedPropertyAccess3.php.expected.json | 2 +- .../scopedPropertyAccess5.php.expected.json | 2 +- .../Validation/cases/self1.php.expected.json | 4 +- .../Validation/cases/self2.php.expected.json | 4 +- .../Validation/cases/self3.php.expected.json | 4 +- .../Validation/cases/self4.php.expected.json | 2 +- .../Validation/cases/self5.php.expected.json | 2 +- .../cases/static1.php.expected.json | 4 +- .../cases/static2.php.expected.json | 4 +- .../cases/static3.php.expected.json | 4 +- .../cases/static4.php.expected.json | 2 +- .../cases/staticMethodReturnType.php | 9 +++ .../staticMethodReturnType.php.expected.json | 65 +++++++++++++++++++ .../cases/stringVariable.php.expected.json | 4 +- ...rifyFqsenOnClassProperty.php.expected.json | 4 +- 53 files changed, 264 insertions(+), 61 deletions(-) create mode 100644 fixtures/completion/method_return_type.php create mode 100644 fixtures/completion/static_method_return_type.php create mode 100644 tests/Validation/cases/methodReturnType.php create mode 100644 tests/Validation/cases/methodReturnType.php.expected.json create mode 100644 tests/Validation/cases/staticMethodReturnType.php create mode 100644 tests/Validation/cases/staticMethodReturnType.php.expected.json diff --git a/fixtures/completion/method_return_type.php b/fixtures/completion/method_return_type.php new file mode 100644 index 0000000..b168f65 --- /dev/null +++ b/fixtures/completion/method_return_type.php @@ -0,0 +1,11 @@ +foo(); +$foo-> \ No newline at end of file diff --git a/fixtures/completion/static_method_return_type.php b/fixtures/completion/static_method_return_type.php new file mode 100644 index 0000000..06cafdd --- /dev/null +++ b/fixtures/completion/static_method_return_type.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index cf2f8a6..32defab 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -623,6 +623,16 @@ class DefinitionResolver } } + // MEMBER CALL EXPRESSION/SCOPED PROPERTY CALL EXPRESSION + // The type of the member/scoped property call expression is the type of the method, so resolve the + // type of the callable expression. + if ($expr instanceof Node\Expression\CallExpression && ( + $expr->callableExpression instanceof Node\Expression\MemberAccessExpression || + $expr->callableExpression instanceof Node\Expression\ScopedPropertyAccessExpression) + ) { + return $this->resolveExpressionNodeToType($expr->callableExpression); + } + // MEMBER ACCESS EXPRESSION if ($expr instanceof Node\Expression\MemberAccessExpression) { if ($expr->memberName instanceof Node\Expression) { diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index fb52ef6..5d03451 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -57,7 +57,7 @@ class LanguageServerTest extends TestCase if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { if ($msg->body->params->type === MessageType::ERROR) { $promise->reject(new Exception($msg->body->params->message)); - } else if (strpos($msg->body->params->message, 'All 27 PHP files parsed') !== false) { + } else if (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { $promise->fulfill(); } } @@ -103,7 +103,7 @@ class LanguageServerTest extends TestCase if ($promise->state === Promise::PENDING) { $promise->reject(new Exception($msg->body->params->message)); } - } else if (strpos($msg->body->params->message, 'All 27 PHP files parsed') !== false) { + } else if (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { $promise->fulfill(); } } diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 03ba3d8..923acc1 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -499,6 +499,50 @@ class CompletionTest extends TestCase ], true), $items); } + public function testMethodReturnType() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/method_return_type.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(10, 6) + )->wait(); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'foo', + CompletionItemKind::METHOD, + '\FooClass', + null, + null, + null, + null, + null + ) + ], true), $items); + } + + public function testStaticMethodReturnType() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/static_method_return_type.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(11, 6) + )->wait(); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'bar', + CompletionItemKind::METHOD, + 'mixed', + null, + null, + null, + null, + null + ) + ], true), $items); + } + private function assertCompletionsListSubset(CompletionList $subsetList, CompletionList $list) { foreach ($subsetList->items as $expectedItem) { diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index 8fa7442..3fc7429 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -132,8 +132,7 @@ class ValidationTest extends TestCase } 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]['type__tostring'] = (string)$definition->type; } $defsForAssert[$fqn][$propertyName] = $definition->$propertyName; diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index 166786a..271c203 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -69,7 +69,7 @@ }, "containerName": "Fixtures\\Prophecy\\WithReturnTypehints" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type__tostring": "\\self", "type": {}, "declarationLine": "public function getSelf(): self {", "documentation": null @@ -88,7 +88,7 @@ }, "containerName": "Fixtures\\Prophecy\\WithReturnTypehints" }, - "type__class": "phpDocumentor\\Reflection\\Types\\String_", + "type__tostring": "string", "type": {}, "declarationLine": "public function getName(): string {", "documentation": null @@ -107,7 +107,7 @@ }, "containerName": "Fixtures\\Prophecy\\WithReturnTypehints" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type__tostring": "\\parent", "type": {}, "declarationLine": "public function getParent(): parent {", "documentation": null diff --git a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json index c9b02d0..1bb66e1 100644 --- a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json +++ b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json @@ -33,7 +33,7 @@ }, "containerName": "A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Array_", + "type__tostring": "string[]", "type": {}, "declarationLine": "protected $foo;", "documentation": null diff --git a/tests/Validation/cases/classDefinition1.php.expected.json b/tests/Validation/cases/classDefinition1.php.expected.json index aad36bb..5167a4c 100644 --- a/tests/Validation/cases/classDefinition1.php.expected.json +++ b/tests/Validation/cases/classDefinition1.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "TestNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Integer", + "type__tostring": "int", "type": {}, "declarationLine": "public $a;", "documentation": null diff --git a/tests/Validation/cases/classProperty1.php.expected.json b/tests/Validation/cases/classProperty1.php.expected.json index 671c019..05d90c1 100644 --- a/tests/Validation/cases/classProperty1.php.expected.json +++ b/tests/Validation/cases/classProperty1.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "TestNamespace\\TestClass" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public $testProperty;", "documentation": null @@ -77,7 +77,7 @@ }, "containerName": "TestNamespace\\TestClass" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public function testMethod($testParameter)", "documentation": null diff --git a/tests/Validation/cases/constants.php.expected.json b/tests/Validation/cases/constants.php.expected.json index eef5cdd..ba80e25 100644 --- a/tests/Validation/cases/constants.php.expected.json +++ b/tests/Validation/cases/constants.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", "documentation": null diff --git a/tests/Validation/cases/constants2.php.expected.json b/tests/Validation/cases/constants2.php.expected.json index 6ecb2a6..e08408e 100644 --- a/tests/Validation/cases/constants2.php.expected.json +++ b/tests/Validation/cases/constants2.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", "documentation": null diff --git a/tests/Validation/cases/constants3.php.expected.json b/tests/Validation/cases/constants3.php.expected.json index d49903c..dbbb959 100644 --- a/tests/Validation/cases/constants3.php.expected.json +++ b/tests/Validation/cases/constants3.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", "documentation": null diff --git a/tests/Validation/cases/constants4.php.expected.json b/tests/Validation/cases/constants4.php.expected.json index 2c01864..24523a8 100644 --- a/tests/Validation/cases/constants4.php.expected.json +++ b/tests/Validation/cases/constants4.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public function suite()", "documentation": null diff --git a/tests/Validation/cases/constants5.php.expected.json b/tests/Validation/cases/constants5.php.expected.json index a0876b5..1498165 100644 --- a/tests/Validation/cases/constants5.php.expected.json +++ b/tests/Validation/cases/constants5.php.expected.json @@ -55,7 +55,7 @@ }, "containerName": "MyNamespace\\Mbstring" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type__tostring": "\\MyNamespace\\PHP_INT_MAX", "type": {}, "declarationLine": "const MB_CASE_FOLD = PHP_INT_MAX;", "documentation": null diff --git a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json index 98aa7cd..3ff0ca1 100644 --- a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json +++ b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json @@ -37,7 +37,7 @@ }, "containerName": "A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b ($a = MY_CONSTANT);", "documentation": null diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json index 6641af3..74cf36b 100644 --- a/tests/Validation/cases/magicConsts.php.expected.json +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -37,7 +37,7 @@ }, "containerName": "A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Array_", + "type__tostring": "\\__CLASS__[]", "type": {}, "declarationLine": "private static $deprecationsTriggered;", "documentation": null diff --git a/tests/Validation/cases/memberAccess1.php.expected.json b/tests/Validation/cases/memberAccess1.php.expected.json index ff4868b..efe4d3a 100644 --- a/tests/Validation/cases/memberAccess1.php.expected.json +++ b/tests/Validation/cases/memberAccess1.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "static function a() {", "documentation": null diff --git a/tests/Validation/cases/memberAccess2.php.expected.json b/tests/Validation/cases/memberAccess2.php.expected.json index a6707d0..1725a5b 100644 --- a/tests/Validation/cases/memberAccess2.php.expected.json +++ b/tests/Validation/cases/memberAccess2.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "static function a() {", "documentation": null diff --git a/tests/Validation/cases/memberAccess3.php.expected.json b/tests/Validation/cases/memberAccess3.php.expected.json index df58d1e..9b1b4ee 100644 --- a/tests/Validation/cases/memberAccess3.php.expected.json +++ b/tests/Validation/cases/memberAccess3.php.expected.json @@ -73,7 +73,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public static function getInitializer(ClassLoader $loader)", "documentation": null diff --git a/tests/Validation/cases/memberAccess4.php.expected.json b/tests/Validation/cases/memberAccess4.php.expected.json index 1cfabb3..3e26d72 100644 --- a/tests/Validation/cases/memberAccess4.php.expected.json +++ b/tests/Validation/cases/memberAccess4.php.expected.json @@ -64,7 +64,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public function testRequest()", "documentation": null diff --git a/tests/Validation/cases/memberAccess5.php.expected.json b/tests/Validation/cases/memberAccess5.php.expected.json index 5023cd6..c7158b6 100644 --- a/tests/Validation/cases/memberAccess5.php.expected.json +++ b/tests/Validation/cases/memberAccess5.php.expected.json @@ -55,7 +55,7 @@ }, "containerName": "MyNamespace\\ParseErrorsTest" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public function setUp()", "documentation": null diff --git a/tests/Validation/cases/memberCall1.php.expected.json b/tests/Validation/cases/memberCall1.php.expected.json index 4cf2cd8..bd31b2f 100644 --- a/tests/Validation/cases/memberCall1.php.expected.json +++ b/tests/Validation/cases/memberCall1.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\ParseErrorsTest" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public function setAccount(AccountInterface $account)", "documentation": null diff --git a/tests/Validation/cases/methodReturnType.php b/tests/Validation/cases/methodReturnType.php new file mode 100644 index 0000000..b4b937d --- /dev/null +++ b/tests/Validation/cases/methodReturnType.php @@ -0,0 +1,7 @@ +foo()": { + "fqn": "FooClass->foo()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "foo", + "kind": 6, + "location": { + "uri": "./methodReturnType.php" + }, + "containerName": "FooClass" + }, + "type__tostring": "\\FooClass", + "type": {}, + "declarationLine": "public function foo(): FooClass {", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/multipleNamespaces.php.expected.json b/tests/Validation/cases/multipleNamespaces.php.expected.json index 2ed59de..3533e8c 100644 --- a/tests/Validation/cases/multipleNamespaces.php.expected.json +++ b/tests/Validation/cases/multipleNamespaces.php.expected.json @@ -64,7 +64,7 @@ }, "containerName": "MyNamespace1\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -121,7 +121,7 @@ }, "containerName": "MyNamespace2\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/multiplePreceedingComments.php.expected.json b/tests/Validation/cases/multiplePreceedingComments.php.expected.json index ca97671..c331b5e 100644 --- a/tests/Validation/cases/multiplePreceedingComments.php.expected.json +++ b/tests/Validation/cases/multiplePreceedingComments.php.expected.json @@ -33,7 +33,7 @@ }, "containerName": "Foo" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type__tostring": "\\Iterator", "type": {}, "declarationLine": "public function fn()", "documentation": "Foo" diff --git a/tests/Validation/cases/nameToken.php.expected.json b/tests/Validation/cases/nameToken.php.expected.json index 1bd944d..af73f78 100644 --- a/tests/Validation/cases/nameToken.php.expected.json +++ b/tests/Validation/cases/nameToken.php.expected.json @@ -33,7 +33,7 @@ }, "containerName": "A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null diff --git a/tests/Validation/cases/objectCreation.php.expected.json b/tests/Validation/cases/objectCreation.php.expected.json index cea0343..a0e8f72 100644 --- a/tests/Validation/cases/objectCreation.php.expected.json +++ b/tests/Validation/cases/objectCreation.php.expected.json @@ -55,7 +55,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/objectCreation2.php.expected.json b/tests/Validation/cases/objectCreation2.php.expected.json index 7f856b1..0119bf7 100644 --- a/tests/Validation/cases/objectCreation2.php.expected.json +++ b/tests/Validation/cases/objectCreation2.php.expected.json @@ -76,7 +76,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/objectCreation3.php.expected.json b/tests/Validation/cases/objectCreation3.php.expected.json index c6dcab9..75cc011 100644 --- a/tests/Validation/cases/objectCreation3.php.expected.json +++ b/tests/Validation/cases/objectCreation3.php.expected.json @@ -37,7 +37,7 @@ }, "containerName": "A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/param1.php.expected.json b/tests/Validation/cases/param1.php.expected.json index adbe002..ee952f3 100644 --- a/tests/Validation/cases/param1.php.expected.json +++ b/tests/Validation/cases/param1.php.expected.json @@ -37,7 +37,7 @@ }, "containerName": "MyNamespace" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function init(Hi $view)", "documentation": null diff --git a/tests/Validation/cases/parent1.php.expected.json b/tests/Validation/cases/parent1.php.expected.json index 3391cd4..961b35b 100644 --- a/tests/Validation/cases/parent1.php.expected.json +++ b/tests/Validation/cases/parent1.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -97,7 +97,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json index 2c4915d..e2cf2c6 100644 --- a/tests/Validation/cases/parent3.php.expected.json +++ b/tests/Validation/cases/parent3.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -100,7 +100,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/propertyName1.php.expected.json b/tests/Validation/cases/propertyName1.php.expected.json index 8032ecc..0a37c20 100644 --- a/tests/Validation/cases/propertyName1.php.expected.json +++ b/tests/Validation/cases/propertyName1.php.expected.json @@ -33,7 +33,7 @@ }, "containerName": "MyClass" }, - "type__class": "phpDocumentor\\Reflection\\Types\\String_", + "type__tostring": "string", "type": {}, "declarationLine": "protected $mainPropertyName;", "documentation": "The name of the main property, or NULL if there is none." diff --git a/tests/Validation/cases/propertyName2.php.expected.json b/tests/Validation/cases/propertyName2.php.expected.json index a70c515..9cba945 100644 --- a/tests/Validation/cases/propertyName2.php.expected.json +++ b/tests/Validation/cases/propertyName2.php.expected.json @@ -33,7 +33,7 @@ }, "containerName": "MyClass" }, - "type__class": "phpDocumentor\\Reflection\\Types\\String_", + "type__tostring": "string", "type": {}, "declarationLine": "protected $mainPropertyName;", "documentation": "The name of the main property, or NULL if there is none." diff --git a/tests/Validation/cases/returnType.php.expected.json b/tests/Validation/cases/returnType.php.expected.json index 9515a6b..6ddca7e 100644 --- a/tests/Validation/cases/returnType.php.expected.json +++ b/tests/Validation/cases/returnType.php.expected.json @@ -40,7 +40,7 @@ }, "containerName": "TestNamespace" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type__tostring": "\\TestNamespace\\TestClass", "type": {}, "declarationLine": "function whatever(TestClass $param): TestClass2 {", "documentation": "Aute duis elit reprehenderit tempor cillum proident anim laborum eu laboris reprehenderit ea incididunt." diff --git a/tests/Validation/cases/scopedPropertyAccess.php.expected.json b/tests/Validation/cases/scopedPropertyAccess.php.expected.json index 6797a0e..3eeda77 100644 --- a/tests/Validation/cases/scopedPropertyAccess.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess.php.expected.json @@ -58,7 +58,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "static function a() {", "documentation": null diff --git a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json index d29387a..81cbfb6 100644 --- a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json @@ -40,7 +40,7 @@ }, "containerName": "A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\String_", + "type__tostring": "string", "type": {}, "declarationLine": "static $a;", "documentation": null diff --git a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json index ab1e214..27f0509 100644 --- a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json @@ -46,7 +46,7 @@ }, "containerName": "TestClass" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Array_", + "type__tostring": "\\TestClass[]", "type": {}, "declarationLine": "public static $testProperty;", "documentation": "Lorem excepteur officia sit anim velit veniam enim." diff --git a/tests/Validation/cases/self1.php.expected.json b/tests/Validation/cases/self1.php.expected.json index 6531b53..41525d8 100644 --- a/tests/Validation/cases/self1.php.expected.json +++ b/tests/Validation/cases/self1.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -100,7 +100,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/self2.php.expected.json b/tests/Validation/cases/self2.php.expected.json index 8300de2..eb31aba 100644 --- a/tests/Validation/cases/self2.php.expected.json +++ b/tests/Validation/cases/self2.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -100,7 +100,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json index 3d5d4ad..f50b80c 100644 --- a/tests/Validation/cases/self3.php.expected.json +++ b/tests/Validation/cases/self3.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -100,7 +100,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index 9a01912..4946001 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -70,7 +70,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", "documentation": null diff --git a/tests/Validation/cases/self5.php.expected.json b/tests/Validation/cases/self5.php.expected.json index 781cd24..e1c99af 100644 --- a/tests/Validation/cases/self5.php.expected.json +++ b/tests/Validation/cases/self5.php.expected.json @@ -55,7 +55,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public function typesProvider()", "documentation": null diff --git a/tests/Validation/cases/static1.php.expected.json b/tests/Validation/cases/static1.php.expected.json index b0323b2..ca49245 100644 --- a/tests/Validation/cases/static1.php.expected.json +++ b/tests/Validation/cases/static1.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -100,7 +100,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/static2.php.expected.json b/tests/Validation/cases/static2.php.expected.json index c8d81fb..06a0627 100644 --- a/tests/Validation/cases/static2.php.expected.json +++ b/tests/Validation/cases/static2.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -100,7 +100,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json index bb556bf..745bd56 100644 --- a/tests/Validation/cases/static3.php.expected.json +++ b/tests/Validation/cases/static3.php.expected.json @@ -61,7 +61,7 @@ }, "containerName": "MyNamespace\\B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", "documentation": null @@ -100,7 +100,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index 6a28028..67c677a 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -60,7 +60,7 @@ }, "containerName": "MyNamespace\\A" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/staticMethodReturnType.php b/tests/Validation/cases/staticMethodReturnType.php new file mode 100644 index 0000000..325738a --- /dev/null +++ b/tests/Validation/cases/staticMethodReturnType.php @@ -0,0 +1,9 @@ +bar()": { + "fqn": "FooClass->bar()", + "extends": [], + "isGlobal": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "bar", + "kind": 6, + "location": { + "uri": "./staticMethodReturnType.php" + }, + "containerName": "FooClass" + }, + "type__tostring": "mixed", + "type": {}, + "declarationLine": "public function bar() { }", + "documentation": null + } + } +} \ No newline at end of file diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json index 5ac910b..982dba5 100644 --- a/tests/Validation/cases/stringVariable.php.expected.json +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -33,7 +33,7 @@ }, "containerName": "B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Integer", + "type__tostring": "int", "type": {}, "declarationLine": "public $hi;", "documentation": null @@ -52,7 +52,7 @@ }, "containerName": "B" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", "documentation": null diff --git a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json index f072722..d40ef63 100644 --- a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json +++ b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json @@ -40,7 +40,7 @@ }, "containerName": "Foo" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Object_", + "type__tostring": "\\", "type": {}, "declarationLine": "protected $bar;", "documentation": null @@ -59,7 +59,7 @@ }, "containerName": "Foo" }, - "type__class": "phpDocumentor\\Reflection\\Types\\Mixed", + "type__tostring": "mixed", "type": {}, "declarationLine": "public function foo () {", "documentation": null From cc3f0da21ae5e83402652acae417cf8aa9939dbb Mon Sep 17 00:00:00 2001 From: Stephan Unverwerth Date: Sat, 10 Jun 2017 11:37:39 +0200 Subject: [PATCH 013/117] Fix 'find references' for unused symbols (#392) * Add tests for unused symbols * Fix tests for unused symbols --- fixtures/global_symbols.php | 12 ++++++ tests/Server/ServerTestCase.php | 3 ++ .../TextDocument/References/GlobalTest.php | 39 +++++++++++++++++++ tests/Server/Workspace/SymbolTest.php | 6 ++- 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/fixtures/global_symbols.php b/fixtures/global_symbols.php index ac93b68..25b928b 100644 --- a/fixtures/global_symbols.php +++ b/fixtures/global_symbols.php @@ -105,3 +105,15 @@ class ChildClass extends TestClass {} define('TEST_DEFINE_CONSTANT', false); print TEST_DEFINE_CONSTANT ? 'true' : 'false'; + +/** + * Neither this class nor its members are referenced anywhere + */ +class UnusedClass +{ + public $unusedProperty; + + public function unusedMethod() + { + } +} diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index d8203c3..04e5976 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -85,6 +85,9 @@ abstract class ServerTestCase extends TestCase 'TestClass::staticTestMethod()' => new Location($globalSymbolsUri, new Range(new Position(46, 4), new Position(49, 5))), 'TestClass::testMethod()' => new Location($globalSymbolsUri, new Range(new Position(57, 4), new Position(60, 5))), 'test_function()' => new Location($globalSymbolsUri, new Range(new Position(78, 0), new Position(81, 1))), + 'UnusedClass' => new Location($globalSymbolsUri, new Range(new Position(111, 0), new Position(118, 1))), + 'UnusedClass::unusedProperty' => new Location($globalSymbolsUri, new Range(new Position(113,11), new Position(113, 26))), + 'UnusedClass::unusedMethod' => new Location($globalSymbolsUri, new Range(new Position(115, 4), new Position(117, 5))), 'whatever()' => new Location($globalReferencesUri, new Range(new Position(21, 0), new Position(23, 1))), // Namespaced diff --git a/tests/Server/TextDocument/References/GlobalTest.php b/tests/Server/TextDocument/References/GlobalTest.php index 4febaf3..fcb6e71 100644 --- a/tests/Server/TextDocument/References/GlobalTest.php +++ b/tests/Server/TextDocument/References/GlobalTest.php @@ -159,4 +159,43 @@ class GlobalTest extends ServerTestCase )->wait(); $this->assertEquals($this->getReferenceLocations('TestClass'), $result); } + + public function testReferencesForUnusedClass() + { + // class UnusedClass + // Get references for UnusedClass + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($symbolsUri), + new Position(111, 10) + )->wait(); + $this->assertEquals([], $result); + } + + public function testReferencesForUnusedProperty() + { + // public $unusedProperty + // Get references for unusedProperty + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($symbolsUri), + new Position(113, 18) + )->wait(); + $this->assertEquals([], $result); + } + + public function testReferencesForUnusedMethod() + { + // public function unusedMethod() + // Get references for unusedMethod + $symbolsUri = pathToUri(realpath(__DIR__ . '/../../../../fixtures/global_symbols.php')); + $result = $this->textDocument->references( + new ReferenceContext, + new TextDocumentIdentifier($symbolsUri), + new Position(115, 26) + )->wait(); + $this->assertEquals([], $result); + } } diff --git a/tests/Server/Workspace/SymbolTest.php b/tests/Server/Workspace/SymbolTest.php index 8f2680d..765841b 100644 --- a/tests/Server/Workspace/SymbolTest.php +++ b/tests/Server/Workspace/SymbolTest.php @@ -27,6 +27,7 @@ class SymbolTest extends ServerTestCase // Request symbols $result = $this->workspace->symbol('')->wait(); $referencesUri = pathToUri(realpath(__DIR__ . '/../../../fixtures/references.php')); + // @codingStandardsIgnoreStart $this->assertEquals([ new SymbolInformation('TestNamespace', SymbolKind::NAMESPACE, new Location($referencesUri, new Range(new Position(2, 0), new Position(2, 24))), ''), @@ -59,9 +60,12 @@ class SymbolTest extends ServerTestCase new SymbolInformation('test_function', SymbolKind::FUNCTION, $this->getDefinitionLocation('test_function()'), ''), new SymbolInformation('ChildClass', SymbolKind::CLASS_, $this->getDefinitionLocation('ChildClass'), ''), new SymbolInformation('TEST_DEFINE_CONSTANT', SymbolKind::CONSTANT, $this->getDefinitionLocation('TEST_DEFINE_CONSTANT'), ''), + new SymbolInformation('UnusedClass', SymbolKind::CLASS_, $this->getDefinitionLocation('UnusedClass'), ''), + new SymbolInformation('unusedProperty', SymbolKind::PROPERTY, $this->getDefinitionLocation('UnusedClass::unusedProperty'), 'UnusedClass'), + new SymbolInformation('unusedMethod', SymbolKind::METHOD, $this->getDefinitionLocation('UnusedClass::unusedMethod'), 'UnusedClass'), new SymbolInformation('whatever', SymbolKind::FUNCTION, $this->getDefinitionLocation('whatever()'), ''), - new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), '') + new SymbolInformation('SecondTestNamespace', SymbolKind::NAMESPACE, $this->getDefinitionLocation('SecondTestNamespace'), ''), ], $result); // @codingStandardsIgnoreEnd } From 4c1d7bd1bc82652a97fb9c7a1b87c24e43691d5c Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Sat, 10 Jun 2017 18:47:19 +0200 Subject: [PATCH 014/117] Add true, false, null to keywords (#396) --- src/CompletionProvider.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 160798d..0c160aa 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -49,6 +49,7 @@ class CompletionProvider 'eval', 'exit', 'extends', + 'false', 'final', 'finally', 'for', @@ -67,6 +68,7 @@ class CompletionProvider 'list', 'namespace', 'new', + 'null', 'or', 'print', 'private', @@ -79,6 +81,7 @@ class CompletionProvider 'switch', 'throw', 'trait', + 'true', 'try', 'unset', 'use', From fe7e9d580041238682f1952d3429569f183afd23 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 10 Jun 2017 21:36:16 +0200 Subject: [PATCH 015/117] Rename $stmts to $sourceFileNode everywhere The root node is now a SourceFileNode, not an array --- src/PhpDocument.php | 18 +++++++++--------- src/TreeAnalyzer.php | 20 ++++++++++---------- tests/DefinitionResolverTest.php | 16 ++++++++-------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/PhpDocument.php b/src/PhpDocument.php index f7b813e..7820701 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -56,9 +56,9 @@ class PhpDocument /** * The AST of the document * - * @var Node + * @var Node\SourceFileNode */ - private $stmts; + private $sourceFileNode; /** * Map from fully qualified name (FQN) to Definition @@ -172,7 +172,7 @@ class PhpDocument $this->index->addReferenceUri($fqn, $this->uri); } - $this->stmts = $treeAnalyzer->getStmts(); + $this->sourceFileNode = $treeAnalyzer->getSourceFileNode(); } /** @@ -221,11 +221,11 @@ class PhpDocument /** * Returns the AST of the document * - * @return Node | null + * @return Node\SourceFileNode|null */ - public function getStmts() + public function getSourceFileNode() { - return $this->stmts; + return $this->sourceFileNode; } /** @@ -236,12 +236,12 @@ class PhpDocument */ public function getNodeAtPosition(Position $position) { - if ($this->stmts === null) { + if ($this->sourceFileNode === null) { return null; } - $offset = $position->toOffset($this->stmts->getFileContents()); - $node = $this->stmts->getDescendantNodeAtPosition($offset); + $offset = $position->toOffset($this->sourceFileNode->getFileContents()); + $node = $this->sourceFileNode->getDescendantNodeAtPosition($offset); if ($node !== null && $node->getStart() > $offset) { return null; } diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 08c60b0..3296ae8 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -15,8 +15,8 @@ class TreeAnalyzer /** @var PhpParser\Parser */ private $parser; - /** @var Node */ - private $stmts; + /** @var Node\SourceFileNode */ + private $sourceFileNode; /** @var Diagnostic[] */ private $diagnostics; @@ -46,17 +46,17 @@ class TreeAnalyzer $this->docBlockFactory = $docBlockFactory; $this->definitionResolver = $definitionResolver; $this->content = $content; - $this->stmts = $this->parser->parseSourceFile($content, $uri); + $this->sourceFileNode = $this->parser->parseSourceFile($content, $uri); // TODO - docblock errors - $this->collectDefinitionsAndReferences($this->stmts); + $this->collectDefinitionsAndReferences($this->sourceFileNode); } - private function collectDefinitionsAndReferences(Node $stmts) + private function collectDefinitionsAndReferences(Node $sourceFileNode) { - foreach ($stmts::CHILD_NAMES as $name) { - $node = $stmts->$name; + foreach ($sourceFileNode::CHILD_NAMES as $name) { + $node = $sourceFileNode->$name; if ($node === null) { continue; @@ -200,10 +200,10 @@ class TreeAnalyzer } /** - * @return Node[] + * @return Node\SourceFileNode */ - public function getStmts() + public function getSourceFileNode() { - return $this->stmts; + return $this->sourceFileNode; } } diff --git a/tests/DefinitionResolverTest.php b/tests/DefinitionResolverTest.php index e7d8b78..0397e71 100644 --- a/tests/DefinitionResolverTest.php +++ b/tests/DefinitionResolverTest.php @@ -14,11 +14,11 @@ class DefinitionResolverTest extends TestCase { $parser = new PhpParser\Parser; $doc = new MockPhpDocument; - $stmts = $parser->parseSourceFile("getUri()); + $sourceFileNode = $parser->parseSourceFile("getUri()); $index = new Index; $definitionResolver = new DefinitionResolver($index); - $def = $definitionResolver->createDefinitionFromNode($stmts->statementList[1]->expression, '\TEST_DEFINE'); + $def = $definitionResolver->createDefinitionFromNode($sourceFileNode->statementList[1]->expression, '\TEST_DEFINE'); $this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $def->type); } @@ -27,11 +27,11 @@ class DefinitionResolverTest extends TestCase { $parser = new PhpParser\Parser; $doc = new MockPhpDocument; - $stmts = $parser->parseSourceFile("getUri()); + $sourceFileNode = $parser->parseSourceFile("getUri()); $index = new Index; $definitionResolver = new DefinitionResolver($index); - $type = $definitionResolver->getTypeFromNode($stmts->statementList[1]->expression); + $type = $definitionResolver->getTypeFromNode($sourceFileNode->statementList[1]->expression); $this->assertInstanceOf(\phpDocumentor\Reflection\Types\Boolean::class, $type); } @@ -41,11 +41,11 @@ class DefinitionResolverTest extends TestCase // define('XXX') (only one argument) must not introduce a new symbol $parser = new PhpParser\Parser; $doc = new MockPhpDocument; - $stmts = $parser->parseSourceFile("getUri()); + $sourceFileNode = $parser->parseSourceFile("getUri()); $index = new Index; $definitionResolver = new DefinitionResolver($index); - $fqn = $definitionResolver->getDefinedFqn($stmts->statementList[1]->expression); + $fqn = $definitionResolver->getDefinedFqn($sourceFileNode->statementList[1]->expression); $this->assertNull($fqn); } @@ -54,11 +54,11 @@ class DefinitionResolverTest extends TestCase { $parser = new PhpParser\Parser; $doc = new MockPhpDocument; - $stmts = $parser->parseSourceFile("getUri()); + $sourceFileNode = $parser->parseSourceFile("getUri()); $index = new Index; $definitionResolver = new DefinitionResolver($index); - $fqn = $definitionResolver->getDefinedFqn($stmts->statementList[1]->expression); + $fqn = $definitionResolver->getDefinedFqn($sourceFileNode->statementList[1]->expression); $this->assertEquals('TEST_DEFINE', $fqn); } From 8d1732ed02b59554f3a71055b4b8a210ebcd3726 Mon Sep 17 00:00:00 2001 From: Nicholas Narsing Date: Sun, 11 Jun 2017 17:24:17 -0400 Subject: [PATCH 016/117] Exclude directory paths from file system search (#401) * Exclude directories from file system search Directories can also match the glob search pattern if their names end in ".php", which will cause a read error later since the ContentRetriever implementers are expecting files. As far as I know, the only way to fix this is to do an additional check to ensure the URI is not of a directory. This resolves #306. --- src/FilesFinder/FileSystemFilesFinder.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/FilesFinder/FileSystemFilesFinder.php b/src/FilesFinder/FileSystemFilesFinder.php index 52df4b6..a26b5d8 100644 --- a/src/FilesFinder/FileSystemFilesFinder.php +++ b/src/FilesFinder/FileSystemFilesFinder.php @@ -22,7 +22,11 @@ class FileSystemFilesFinder implements FilesFinder return coroutine(function () use ($glob) { $uris = []; foreach (new GlobIterator($glob) as $path) { - $uris[] = pathToUri($path); + // Exclude any directories that also match the glob pattern + if (!is_dir($path)) { + $uris[] = pathToUri($path); + } + yield timeout(); } return $uris; From 3b633369a709c1548bbdf50eb79414dce9cdd248 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 15 Jun 2017 03:44:03 -0700 Subject: [PATCH 017/117] Fix error getting completions for 'new static' type (#405) --- src/DefinitionResolver.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 32defab..d331cba 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -906,11 +906,12 @@ class DefinitionResolver // Anonymous class return new Types\Object_; } - $className = (string)$class->getResolvedName(); - - if ($className === 'static') { + if ($class instanceof PhpParser\Token && $class->kind === PhpParser\TokenKind::StaticKeyword) { + // `new static` return new Types\Static_; } + $className = (string)$class->getResolvedName(); + if ($className === 'self' || $className === 'parent') { $classNode = $class->getFirstAncestor(Node\Statement\ClassDeclaration::class); if ($className === 'parent') { From 4a98afe5404c249144785333ec1b0f66b7ad5905 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 15 Jun 2017 17:03:25 +0200 Subject: [PATCH 018/117] Fix docblock union types --- src/DefinitionResolver.php | 16 ++++++++-------- src/ParserHelpers.php | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index d331cba..c936ba3 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -54,7 +54,7 @@ class DefinitionResolver { // If node is part of a declaration list, build a declaration line that discludes other elements in the list // - [PropertyDeclaration] // public $a, [$b = 3], $c; => public $b = 3; - // - [ConstDeclaration | ClassConstDeclaration] // "const A = 3, [B = 4];" => "const B = 4;" + // - [ConstDeclaration|ClassConstDeclaration] // "const A = 3, [B = 4];" => "const B = 4;" if ( ($declaration = ParserHelpers\tryGetPropertyDeclaration($node)) && ($elements = $declaration->propertyElements) || ($declaration = ParserHelpers\tryGetConstOrClassConstDeclaration($node)) && ($elements = $declaration->constElements) @@ -131,7 +131,7 @@ class DefinitionResolver * Gets Doc Block with resolved names for a Node * * @param Node $node - * @return DocBlock | null + * @return DocBlock|null */ private function getDocBlock(Node $node) { @@ -482,8 +482,8 @@ class DefinitionResolver /** * Returns the assignment or parameter node where a variable was defined * - * @param Node\Expression\Variable | Node\Expression\ClosureUse $var The variable access - * @return Node\Expression\Assign | Node\Expression\AssignOp|Node\Param | Node\Expression\ClosureUse|null + * @param Node\Expression\Variable|Node\Expression\ClosureUse $var The variable access + * @return Node\Expression\Assign|Node\Expression\AssignOp|Node\Param|Node\Expression\ClosureUse|null */ public function resolveVariableToNode($var) { @@ -894,7 +894,7 @@ class DefinitionResolver * Takes any class name node (from a static method call, or new node) and returns a Type object * Resolves keywords like self, static and parent * - * @param Node | PhpParser\Token $class + * @param Node|PhpParser\Token $class * @return Type */ public function resolveClassNameToType($class): Type @@ -1191,9 +1191,9 @@ class DefinitionResolver } /** - * @param DocBlock | null $docBlock - * @param string | null $variableName - * @return DocBlock\Tags\Param | null + * @param DocBlock|null $docBlock + * @param string|null $variableName + * @return DocBlock\Tags\Param|null */ private function tryGetDocBlockTagForParameter($docBlock, $variableName) { diff --git a/src/ParserHelpers.php b/src/ParserHelpers.php index 5025ae3..0ea210a 100644 --- a/src/ParserHelpers.php +++ b/src/ParserHelpers.php @@ -77,7 +77,7 @@ function isBooleanExpression($expression) : bool /** * Tries to get the parent property declaration given a Node * @param Node $node - * @return Node\PropertyDeclaration | null $node + * @return Node\PropertyDeclaration|null $node */ function tryGetPropertyDeclaration(Node $node) { @@ -93,7 +93,7 @@ function tryGetPropertyDeclaration(Node $node) /** * Tries to get the parent ConstDeclaration or ClassConstDeclaration given a Node * @param Node $node - * @return Node\Statement\ConstDeclaration | Node\ClassConstDeclaration | null $node + * @return Node\Statement\ConstDeclaration|Node\ClassConstDeclaration|null $node */ function tryGetConstOrClassConstDeclaration(Node $node) { From 663ccd5f2396489ffd49a09b196150de51e6711a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 15 Jun 2017 17:11:57 +0200 Subject: [PATCH 019/117] Update CodeSniffer --- composer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 93dec97..5375d79 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "phpdocumentor/reflection-docblock": "^3.0", "sabre/event": "^5.0", "felixfbecker/advanced-json-rpc": "^2.0", - "squizlabs/php_codesniffer" : "3.0.0RC3", + "squizlabs/php_codesniffer" : "^3.0", "netresearch/jsonmapper": "^1.0", "webmozart/path-util": "^2.3", "webmozart/glob": "^4.1", @@ -49,7 +49,8 @@ "files" : [ "src/utils.php", "src/FqnUtilities.php", - "src/ParserHelpers.php" + "src/ParserHelpers.php", + "vendor/squizlabs/php_codesniffer/autoload.php" ] }, "autoload-dev": { From 0e3727a8d629eae2f5c732b8ecdecd346db22547 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 16 Jun 2017 20:31:13 +0200 Subject: [PATCH 020/117] Improve CompletionProvider (#412) - Better performance - More documentation - Add field to Definition for global namespace fallback Fixes #380 --- .../inside_namespace_and_method.php | 11 + phpcs.xml.dist | 2 + src/CompletionProvider.php | 221 +++++++++++------- src/Definition.php | 7 + src/DefinitionResolver.php | 10 + tests/Server/TextDocument/CompletionTest.php | 30 ++- .../WithReturnTypehints.php.expected.json | 5 + ...embersShouldNotBeSymbols.php.expected.json | 1 + ...rrayValueShouldBeBoolean.php.expected.json | 2 + .../cases/caseStatement1.php.expected.json | 1 + .../cases/classDefinition1.php.expected.json | 3 + .../cases/classProperty1.php.expected.json | 4 + .../cases/constants.php.expected.json | 3 + .../cases/constants2.php.expected.json | 3 + .../cases/constants3.php.expected.json | 3 + .../cases/constants4.php.expected.json | 3 + .../cases/constants5.php.expected.json | 3 + ...tsInFunctionParamDefault.php.expected.json | 2 + ...cksOnNamespaceDefinition.php.expected.json | 1 + .../cases/exceptions1.php.expected.json | 1 + .../cases/ifStatement1.php.expected.json | 1 + .../cases/interfaceProperty.php.expected.json | 1 + ...cConstantsShouldBeGlobal.php.expected.json | 1 + .../cases/magicConsts.php.expected.json | 2 + .../cases/memberAccess1.php.expected.json | 3 + .../cases/memberAccess2.php.expected.json | 3 + .../cases/memberAccess3.php.expected.json | 3 + .../cases/memberAccess4.php.expected.json | 3 + .../cases/memberAccess5.php.expected.json | 3 + .../cases/memberCall1.php.expected.json | 3 + .../cases/methodReturnType.php.expected.json | 2 + .../multipleNamespaces.php.expected.json | 6 + ...ltiplePreceedingComments.php.expected.json | 2 + .../cases/nameToken.php.expected.json | 2 + .../cases/namespaces2.php.expected.json | 1 + .../cases/namespaces5.php.expected.json | 1 + .../cases/namespaces6.php.expected.json | 1 + .../cases/namespaces8.php.expected.json | 1 + .../cases/objectCreation.php.expected.json | 3 + .../cases/objectCreation2.php.expected.json | 4 + .../cases/objectCreation3.php.expected.json | 2 + .../Validation/cases/param1.php.expected.json | 2 + .../cases/parent1.php.expected.json | 5 + .../cases/parent3.php.expected.json | 5 + .../cases/propertyName1.php.expected.json | 2 + .../cases/propertyName2.php.expected.json | 2 + .../cases/returnType.php.expected.json | 2 + .../scopedPropertyAccess.php.expected.json | 3 + .../scopedPropertyAccess2.php.expected.json | 1 + .../scopedPropertyAccess3.php.expected.json | 2 + .../scopedPropertyAccess5.php.expected.json | 2 + .../Validation/cases/self1.php.expected.json | 5 + .../Validation/cases/self2.php.expected.json | 5 + .../Validation/cases/self3.php.expected.json | 5 + .../Validation/cases/self4.php.expected.json | 3 + .../Validation/cases/self5.php.expected.json | 3 + .../cases/static1.php.expected.json | 5 + .../cases/static2.php.expected.json | 5 + .../cases/static3.php.expected.json | 5 + .../cases/static4.php.expected.json | 3 + .../staticMethodReturnType.php.expected.json | 3 + .../cases/stringVariable.php.expected.json | 3 + ...edNameOutsideOfNamespace.php.expected.json | 1 + ...rifyFqsenOnClassProperty.php.expected.json | 3 + 64 files changed, 353 insertions(+), 86 deletions(-) create mode 100644 fixtures/completion/inside_namespace_and_method.php diff --git a/fixtures/completion/inside_namespace_and_method.php b/fixtures/completion/inside_namespace_and_method.php new file mode 100644 index 0000000..698ac4a --- /dev/null +++ b/fixtures/completion/inside_namespace_and_method.php @@ -0,0 +1,11 @@ + + + diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 0c160aa..f4f091d 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -128,6 +128,7 @@ class CompletionProvider // This can be made much more performant if the tree follows specific invariants. $node = $doc->getNodeAtPosition($pos); + // Get the node at the position under the cursor $offset = $node === null ? -1 : $pos->toOffset($node->getFileContents()); if ( $node !== null @@ -148,22 +149,31 @@ class CompletionProvider $node = $node->parent; } + // Inspect the type of expression under the cursor + if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) { + // HTML, beginning of file + + // Inside HTML and at the beginning of the file, propose textEdit = new TextEdit( new Range($pos, $pos), stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), 'items[] = $item; - } /* - VARIABLES */ - elseif ( - $node instanceof Node\Expression\Variable && - !( - $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression && - $node->parent->memberName === $node) + } elseif ( + $node instanceof Node\Expression\Variable + && !( + $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression + && $node->parent->memberName === $node + ) ) { + // Variables + // + // $| + // $a| + // Find variables, parameters and use statements in the scope $namePrefix = $node->getName() ?? ''; foreach ($this->suggestVariablesAtNode($node, $namePrefix) as $var) { @@ -178,23 +188,28 @@ class CompletionProvider ); $list->items[] = $item; } - } /* - MEMBER ACCESS EXPRESSIONS - $a->c# - $a-># */ - elseif ($node instanceof Node\Expression\MemberAccessExpression) { + } elseif ($node instanceof Node\Expression\MemberAccessExpression) { + // Member access expressions + // + // $a->c| + // $a->| + + // Multiple prefixes for all possible types $prefixes = FqnUtilities\getFqnsFromType( $this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression) ); + + // Include parent classes $prefixes = $this->expandParentFqns($prefixes); + // Add the object access operator to only get members foreach ($prefixes as &$prefix) { $prefix .= '->'; } - unset($prefix); + // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { if (substr($fqn, 0, strlen($prefix)) === $prefix && !$def->isGlobal) { @@ -202,30 +217,35 @@ class CompletionProvider } } } - } /* - SCOPED PROPERTY ACCESS EXPRESSIONS - A\B\C::$a# - A\B\C::# - A\B\C::$# - A\B\C::foo# - TODO: $a::# */ - elseif ( + } elseif ( ($scoped = $node->parent) instanceof Node\Expression\ScopedPropertyAccessExpression || ($scoped = $node) instanceof Node\Expression\ScopedPropertyAccessExpression ) { + // Static class members and constants + // + // A\B\C::$a| + // A\B\C::| + // A\B\C::$| + // A\B\C::foo| + // + // TODO: $a::| + + // Resolve all possible types to FQNs $prefixes = FqnUtilities\getFqnsFromType( $classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier) ); + // Add parent classes $prefixes = $this->expandParentFqns($prefixes); + // Append :: operator to only get static members foreach ($prefixes as &$prefix) { $prefix .= '::'; } - unset($prefix); + // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && !$def->isGlobal) { @@ -233,83 +253,124 @@ class CompletionProvider } } } - } elseif (ParserHelpers\isConstantFetch($node) || - ($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression || - (($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()); + } elseif ( + ParserHelpers\isConstantFetch($node) + // Creation gets set in case of an instantiation (`new` expression) + || ($creation = $node->parent) instanceof Node\Expression\ObjectCreationExpression + || (($creation = $node) instanceof Node\Expression\ObjectCreationExpression) + ) { + // Class instantiations, function calls, constant fetches, class names + // + // new MyCl| + // my_func| + // MY_CONS| + // MyCla| - $namespaceDefinition = $node->getNamespaceDefinition(); + // The name Node under the cursor + $nameNode = isset($creation) ? $creation->classTypeDesignator : $node; - list($namespaceImportTable,,) = $node->getImportTablesForCurrentScope(); - foreach ($namespaceImportTable as $alias => $name) { - $namespaceImportTable[$alias] = (string)$name; + /** The typed name */ + $prefix = $nameNode instanceof Node\QualifiedName + ? (string)PhpParser\ResolvedName::buildName($nameNode->nameParts, $nameNode->getFileContents()) + : $nameNode->getText($node->getFileContents()); + $prefixLen = strlen($prefix); + + /** Whether the prefix is qualified (contains at least one backslash) */ + $isQualified = $nameNode instanceof Node\QualifiedName && $nameNode->isQualifiedName(); + + /** Whether the prefix is fully qualified (begins with a backslash) */ + $isFullyQualified = $nameNode instanceof Node\QualifiedName && $nameNode->isFullyQualifiedName(); + + /** The closest NamespaceDefinition Node */ + $namespaceNode = $node->getNamespaceDefinition(); + + /** @var string The name of the namespace */ + $namespacedPrefix = null; + if ($namespaceNode) { + $namespacedPrefix = (string)PhpParser\ResolvedName::buildName($namespaceNode->name->nameParts, $node->getFileContents()) . '\\' . $prefix; + $namespacedPrefixLen = strlen($namespacedPrefix); } - foreach ($this->index->getDefinitions() as $fqn => $def) { - $fqnStartsWithPrefix = substr($fqn, 0, strlen($prefix)) === $prefix; - $fqnContainsPrefix = empty($prefix) || strpos($fqn, $prefix) !== false; - if (($def->canBeInstantiated || ($def->isGlobal && !isset($creation))) && $fqnContainsPrefix) { - if ($namespaceDefinition !== null && $namespaceDefinition->name !== null) { - $namespacePrefix = (string)PhpParser\ResolvedName::buildName($namespaceDefinition->name->nameParts, $node->getFileContents()); + // Get the namespace use statements + // TODO: use function statements, use const statements - $isAliased = false; + /** @var string[] $aliases A map from local alias to fully qualified name */ + list($aliases,,) = $node->getImportTablesForCurrentScope(); - $isNotFullyQualified = !($class instanceof Node\QualifiedName) || !$class->isFullyQualifiedName(); - if ($isNotFullyQualified) { - foreach ($namespaceImportTable as $alias => $name) { - if (substr($fqn, 0, strlen($name)) === $name) { - $fqn = $alias; - $isAliased = true; - break; - } - } - } + foreach ($aliases as $alias => $name) { + $aliases[$alias] = (string)$name; + } - $prefixWithNamespace = $namespacePrefix . "\\" . $prefix; - $fqnMatchesPrefixWithNamespace = substr($fqn, 0, strlen($prefixWithNamespace)) === $prefixWithNamespace; - $isFullyQualifiedAndPrefixMatches = !$isNotFullyQualified && ($fqnStartsWithPrefix || $fqnMatchesPrefixWithNamespace); - if (!$isFullyQualifiedAndPrefixMatches && !$isAliased) { - if (!array_search($fqn, array_values($namespaceImportTable))) { - if (empty($prefix)) { - $fqn = '\\' . $fqn; - } elseif ($fqnMatchesPrefixWithNamespace) { - $fqn = substr($fqn, strlen($namespacePrefix) + 1); - } else { - continue; - } - } else { - continue; - } - } - } elseif ($fqnStartsWithPrefix && $class instanceof Node\QualifiedName && $class->isFullyQualifiedName()) { - $fqn = '\\' . $fqn; + // If there is a prefix that does not start with a slash, suggest `use`d symbols + if ($prefix && !$isFullyQualified) { + foreach ($aliases as $alias => $fqn) { + // Suggest symbols that have been `use`d and match the prefix + if (substr($alias, 0, $prefixLen) === $prefix && ($def = $this->index->getDefinition($fqn))) { + $list->items[] = CompletionItem::fromDefinition($def); } + } + } + // Suggest global symbols that either + // - start with the current namespace + prefix, if the Name node is not fully qualified + // - start with just the prefix, if the Name node is fully qualified + foreach ($this->index->getDefinitions() as $fqn => $def) { + + $fqnStartsWithPrefix = substr($fqn, 0, $prefixLen) === $prefix; + + if ( + // Exclude methods, properties etc. + $def->isGlobal + && ( + !$prefix + || ( + // Either not qualified, but a matching prefix with global fallback + ($def->roamed && !$isQualified && $fqnStartsWithPrefix) + // Or not in a namespace or a fully qualified name or AND matching the prefix + || ((!$namespaceNode || $isFullyQualified) && $fqnStartsWithPrefix) + // Or in a namespace, not fully qualified and matching the prefix + current namespace + || ( + $namespaceNode + && !$isFullyQualified + && substr($fqn, 0, $namespacedPrefixLen) === $namespacedPrefix + ) + ) + ) + // Only suggest classes for `new` + && (!isset($creation) || $def->canBeInstantiated) + ) { $item = CompletionItem::fromDefinition($def); - - $item->insertText = $fqn; + // Find the shortest name to reference the symbol + if ($namespaceNode && ($alias = array_search($fqn, $aliases, true)) !== false) { + // $alias is the name under which this definition is aliased in the current namespace + $item->insertText = $alias; + } else if ($namespaceNode && !($prefix && $isFullyQualified)) { + // Insert the global FQN with leading backslash + $item->insertText = '\\' . $fqn; + } else { + // Insert the FQN without leading backlash + $item->insertText = $fqn; + } + // Don't insert the parenthesis for functions + // TODO return a snippet and put the cursor inside + if (substr($item->insertText, -2) === '()') { + $item->insertText = substr($item->insertText, 0, -2); + } $list->items[] = $item; } } + // If not a class instantiation, also suggest keywords if (!isset($creation)) { foreach (self::KEYWORDS as $keyword) { - $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); - $item->insertText = $keyword . ' '; - $list->items[] = $item; + if (substr($keyword, 0, $prefixLen) === $prefix) { + $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); + $item->insertText = $keyword; + $list->items[] = $item; + } } } - } elseif (ParserHelpers\isConstantFetch($node)) { - $prefix = (string) ($node->getResolvedName() ?? PhpParser\ResolvedName::buildName($node->nameParts, $node->getFileContents())); - foreach (self::KEYWORDS as $keyword) { - $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); - $item->insertText = $keyword . ' '; - $list->items[] = $item; - } } return $list; diff --git a/src/Definition.php b/src/Definition.php index e302f71..cf0f574 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -44,6 +44,13 @@ class Definition */ public $isGlobal; + /** + * True if this definition is affected by global namespace fallback (global function or global constant) + * + * @var bool + */ + public $roamed; + /** * False for instance methods and properties * diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index c936ba3..7772990 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -193,6 +193,16 @@ class DefinitionResolver ($node instanceof Node\ConstElement && $node->parent->parent instanceof Node\Statement\ConstDeclaration) ); + // Definition is affected by global namespace fallback if it is a global constant or a global function + $def->roamed = ( + $fqn !== null + && strpos($fqn, '\\') === false + && ( + ($node instanceof Node\ConstElement && $node->parent->parent instanceof Node\Statement\ConstDeclaration) + || $node instanceof Node\Statement\FunctionDeclaration + ) + ); + // Static methods and static property declarations $def->isStatic = ( ($node instanceof Node\MethodDeclaration && $node->isStatic()) || diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 923acc1..be34200 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -69,6 +69,27 @@ class CompletionTest extends TestCase ], true), $items); } + public function testGlobalFunctionInsideNamespaceAndClass() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/inside_namespace_and_method.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(8, 11) + )->wait(); + $this->assertCompletionsListSubset(new CompletionList([ + new CompletionItem( + 'test_function', + CompletionItemKind::FUNCTION, + 'void', // Return type + 'Officia aliquip adipisicing et nulla et laboris dolore labore.', + null, + null, + '\test_function' + ) + ], true), $items); + } + public function testPropertyAndMethodWithoutPrefix() { $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/property.php'); @@ -234,10 +255,7 @@ class CompletionTest extends TestCase '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" . 'consequat sunt culpa exercitation pariatur id reprehenderit nisi incididunt Lorem' . "\n" . - 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.', - null, - null, - 'TestClass' + 'sint. Officia culpa pariatur laborum nostrud cupidatat consequat mollit.' ) ], true), $items); } @@ -397,8 +415,8 @@ class CompletionTest extends TestCase new Position(2, 1) )->wait(); $this->assertCompletionsListSubset(new CompletionList([ - new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class '), - new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone ') + new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class'), + new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone') ], true), $items); } diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index 271c203..26b00e2 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -21,6 +21,7 @@ "fqn": "Fixtures\\Prophecy", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -41,6 +42,7 @@ "Fixtures\\Prophecy\\EmptyClass" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -59,6 +61,7 @@ "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -78,6 +81,7 @@ "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getName()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -97,6 +101,7 @@ "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getParent()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json index 189d154..b0625d7 100644 --- a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json +++ b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json @@ -5,6 +5,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json index 1bb66e1..d58986d 100644 --- a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json +++ b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json @@ -5,6 +5,7 @@ "fqn": "A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -23,6 +24,7 @@ "fqn": "A->foo", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/caseStatement1.php.expected.json b/tests/Validation/cases/caseStatement1.php.expected.json index 7b6dbf2..573dd17 100644 --- a/tests/Validation/cases/caseStatement1.php.expected.json +++ b/tests/Validation/cases/caseStatement1.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/classDefinition1.php.expected.json b/tests/Validation/cases/classDefinition1.php.expected.json index 5167a4c..6c418b7 100644 --- a/tests/Validation/cases/classDefinition1.php.expected.json +++ b/tests/Validation/cases/classDefinition1.php.expected.json @@ -12,6 +12,7 @@ "fqn": "TestNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "TestNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "TestNamespace\\A->a", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/classProperty1.php.expected.json b/tests/Validation/cases/classProperty1.php.expected.json index 05d90c1..e00107e 100644 --- a/tests/Validation/cases/classProperty1.php.expected.json +++ b/tests/Validation/cases/classProperty1.php.expected.json @@ -12,6 +12,7 @@ "fqn": "TestNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "TestNamespace\\TestClass", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "TestNamespace\\TestClass->testProperty", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -67,6 +70,7 @@ "fqn": "TestNamespace\\TestClass->testMethod()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/constants.php.expected.json b/tests/Validation/cases/constants.php.expected.json index ba80e25..d629870 100644 --- a/tests/Validation/cases/constants.php.expected.json +++ b/tests/Validation/cases/constants.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A::suite()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/constants2.php.expected.json b/tests/Validation/cases/constants2.php.expected.json index e08408e..66c678f 100644 --- a/tests/Validation/cases/constants2.php.expected.json +++ b/tests/Validation/cases/constants2.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A::suite()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/constants3.php.expected.json b/tests/Validation/cases/constants3.php.expected.json index dbbb959..03f00ba 100644 --- a/tests/Validation/cases/constants3.php.expected.json +++ b/tests/Validation/cases/constants3.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A::suite()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/constants4.php.expected.json b/tests/Validation/cases/constants4.php.expected.json index 24523a8..62ce430 100644 --- a/tests/Validation/cases/constants4.php.expected.json +++ b/tests/Validation/cases/constants4.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A->suite()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/constants5.php.expected.json b/tests/Validation/cases/constants5.php.expected.json index 1498165..bb441c8 100644 --- a/tests/Validation/cases/constants5.php.expected.json +++ b/tests/Validation/cases/constants5.php.expected.json @@ -9,6 +9,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "MyNamespace\\Mbstring", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -45,6 +47,7 @@ "fqn": "MyNamespace\\Mbstring::MB_CASE_FOLD", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json index 3ff0ca1..8f9a212 100644 --- a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json +++ b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json @@ -9,6 +9,7 @@ "fqn": "A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "A->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json index d73ecb5..73f6bee 100644 --- a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json +++ b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json @@ -5,6 +5,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/exceptions1.php.expected.json b/tests/Validation/cases/exceptions1.php.expected.json index 4a13354..a4a71d1 100644 --- a/tests/Validation/cases/exceptions1.php.expected.json +++ b/tests/Validation/cases/exceptions1.php.expected.json @@ -9,6 +9,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/ifStatement1.php.expected.json b/tests/Validation/cases/ifStatement1.php.expected.json index 4375a2a..18efe9f 100644 --- a/tests/Validation/cases/ifStatement1.php.expected.json +++ b/tests/Validation/cases/ifStatement1.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/interfaceProperty.php.expected.json b/tests/Validation/cases/interfaceProperty.php.expected.json index 896d09e..178834d 100644 --- a/tests/Validation/cases/interfaceProperty.php.expected.json +++ b/tests/Validation/cases/interfaceProperty.php.expected.json @@ -5,6 +5,7 @@ "fqn": "A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json index b61333f..f62d214 100644 --- a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json +++ b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json @@ -12,6 +12,7 @@ "fqn": "B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json index 74cf36b..a37791b 100644 --- a/tests/Validation/cases/magicConsts.php.expected.json +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -9,6 +9,7 @@ "fqn": "A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "A::$deprecationsTriggered", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/memberAccess1.php.expected.json b/tests/Validation/cases/memberAccess1.php.expected.json index efe4d3a..7f9630a 100644 --- a/tests/Validation/cases/memberAccess1.php.expected.json +++ b/tests/Validation/cases/memberAccess1.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A::a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/memberAccess2.php.expected.json b/tests/Validation/cases/memberAccess2.php.expected.json index 1725a5b..7b3ce1d 100644 --- a/tests/Validation/cases/memberAccess2.php.expected.json +++ b/tests/Validation/cases/memberAccess2.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A::a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/memberAccess3.php.expected.json b/tests/Validation/cases/memberAccess3.php.expected.json index 9b1b4ee..520dae8 100644 --- a/tests/Validation/cases/memberAccess3.php.expected.json +++ b/tests/Validation/cases/memberAccess3.php.expected.json @@ -27,6 +27,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -45,6 +46,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -63,6 +65,7 @@ "fqn": "MyNamespace\\A::getInitializer()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/memberAccess4.php.expected.json b/tests/Validation/cases/memberAccess4.php.expected.json index 3e26d72..1d51b85 100644 --- a/tests/Validation/cases/memberAccess4.php.expected.json +++ b/tests/Validation/cases/memberAccess4.php.expected.json @@ -18,6 +18,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -36,6 +37,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -54,6 +56,7 @@ "fqn": "MyNamespace\\A->testRequest()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/memberAccess5.php.expected.json b/tests/Validation/cases/memberAccess5.php.expected.json index c7158b6..57aca0a 100644 --- a/tests/Validation/cases/memberAccess5.php.expected.json +++ b/tests/Validation/cases/memberAccess5.php.expected.json @@ -9,6 +9,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "MyNamespace\\ParseErrorsTest", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -45,6 +47,7 @@ "fqn": "MyNamespace\\ParseErrorsTest->setUp()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/memberCall1.php.expected.json b/tests/Validation/cases/memberCall1.php.expected.json index bd31b2f..416a705 100644 --- a/tests/Validation/cases/memberCall1.php.expected.json +++ b/tests/Validation/cases/memberCall1.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\ParseErrorsTest", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\ParseErrorsTest->setAccount()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/methodReturnType.php.expected.json b/tests/Validation/cases/methodReturnType.php.expected.json index 860f276..54d79d6 100644 --- a/tests/Validation/cases/methodReturnType.php.expected.json +++ b/tests/Validation/cases/methodReturnType.php.expected.json @@ -9,6 +9,7 @@ "fqn": "FooClass", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "FooClass->foo()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/multipleNamespaces.php.expected.json b/tests/Validation/cases/multipleNamespaces.php.expected.json index 3533e8c..30fe596 100644 --- a/tests/Validation/cases/multipleNamespaces.php.expected.json +++ b/tests/Validation/cases/multipleNamespaces.php.expected.json @@ -18,6 +18,7 @@ "fqn": "MyNamespace1", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -36,6 +37,7 @@ "fqn": "MyNamespace1\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -54,6 +56,7 @@ "fqn": "MyNamespace1\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -73,6 +76,7 @@ "fqn": "MyNamespace2", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -93,6 +97,7 @@ "MyNamespace2\\MyNamespace1\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -111,6 +116,7 @@ "fqn": "MyNamespace2\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/multiplePreceedingComments.php.expected.json b/tests/Validation/cases/multiplePreceedingComments.php.expected.json index c331b5e..5ca3dd7 100644 --- a/tests/Validation/cases/multiplePreceedingComments.php.expected.json +++ b/tests/Validation/cases/multiplePreceedingComments.php.expected.json @@ -5,6 +5,7 @@ "fqn": "Foo", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -23,6 +24,7 @@ "fqn": "Foo->fn()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/nameToken.php.expected.json b/tests/Validation/cases/nameToken.php.expected.json index af73f78..0ecd53f 100644 --- a/tests/Validation/cases/nameToken.php.expected.json +++ b/tests/Validation/cases/nameToken.php.expected.json @@ -5,6 +5,7 @@ "fqn": "A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -23,6 +24,7 @@ "fqn": "A->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/namespaces2.php.expected.json b/tests/Validation/cases/namespaces2.php.expected.json index 903d544..42243e5 100644 --- a/tests/Validation/cases/namespaces2.php.expected.json +++ b/tests/Validation/cases/namespaces2.php.expected.json @@ -18,6 +18,7 @@ "fqn": "MyNamespace1", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/namespaces5.php.expected.json b/tests/Validation/cases/namespaces5.php.expected.json index ccc22fd..5ffe02d 100644 --- a/tests/Validation/cases/namespaces5.php.expected.json +++ b/tests/Validation/cases/namespaces5.php.expected.json @@ -27,6 +27,7 @@ "fqn": "B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/namespaces6.php.expected.json b/tests/Validation/cases/namespaces6.php.expected.json index 874ee7f..1657f28 100644 --- a/tests/Validation/cases/namespaces6.php.expected.json +++ b/tests/Validation/cases/namespaces6.php.expected.json @@ -5,6 +5,7 @@ "fqn": "A\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/namespaces8.php.expected.json b/tests/Validation/cases/namespaces8.php.expected.json index 7a77f7c..71017d9 100644 --- a/tests/Validation/cases/namespaces8.php.expected.json +++ b/tests/Validation/cases/namespaces8.php.expected.json @@ -15,6 +15,7 @@ "fqn": "LanguageServer\\Tests\\Utils", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/objectCreation.php.expected.json b/tests/Validation/cases/objectCreation.php.expected.json index a0e8f72..a8fce0f 100644 --- a/tests/Validation/cases/objectCreation.php.expected.json +++ b/tests/Validation/cases/objectCreation.php.expected.json @@ -9,6 +9,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -45,6 +47,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/objectCreation2.php.expected.json b/tests/Validation/cases/objectCreation2.php.expected.json index 0119bf7..2ec8314 100644 --- a/tests/Validation/cases/objectCreation2.php.expected.json +++ b/tests/Validation/cases/objectCreation2.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -66,6 +69,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/objectCreation3.php.expected.json b/tests/Validation/cases/objectCreation3.php.expected.json index 75cc011..f0cc31a 100644 --- a/tests/Validation/cases/objectCreation3.php.expected.json +++ b/tests/Validation/cases/objectCreation3.php.expected.json @@ -9,6 +9,7 @@ "fqn": "A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/param1.php.expected.json b/tests/Validation/cases/param1.php.expected.json index ee952f3..33c99db 100644 --- a/tests/Validation/cases/param1.php.expected.json +++ b/tests/Validation/cases/param1.php.expected.json @@ -9,6 +9,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "MyNamespace\\init()", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/parent1.php.expected.json b/tests/Validation/cases/parent1.php.expected.json index 961b35b..eab232e 100644 --- a/tests/Validation/cases/parent1.php.expected.json +++ b/tests/Validation/cases/parent1.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -69,6 +72,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -87,6 +91,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json index e2cf2c6..aedb4b2 100644 --- a/tests/Validation/cases/parent3.php.expected.json +++ b/tests/Validation/cases/parent3.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -72,6 +75,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -90,6 +94,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/propertyName1.php.expected.json b/tests/Validation/cases/propertyName1.php.expected.json index 0a37c20..42b2ee9 100644 --- a/tests/Validation/cases/propertyName1.php.expected.json +++ b/tests/Validation/cases/propertyName1.php.expected.json @@ -5,6 +5,7 @@ "fqn": "MyClass", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -23,6 +24,7 @@ "fqn": "MyClass->mainPropertyName", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/propertyName2.php.expected.json b/tests/Validation/cases/propertyName2.php.expected.json index 9cba945..5a5c914 100644 --- a/tests/Validation/cases/propertyName2.php.expected.json +++ b/tests/Validation/cases/propertyName2.php.expected.json @@ -5,6 +5,7 @@ "fqn": "MyClass", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -23,6 +24,7 @@ "fqn": "MyClass->mainPropertyName", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/returnType.php.expected.json b/tests/Validation/cases/returnType.php.expected.json index 6ddca7e..cf9cc63 100644 --- a/tests/Validation/cases/returnType.php.expected.json +++ b/tests/Validation/cases/returnType.php.expected.json @@ -12,6 +12,7 @@ "fqn": "TestNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "TestNamespace\\whatever()", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/scopedPropertyAccess.php.expected.json b/tests/Validation/cases/scopedPropertyAccess.php.expected.json index 3eeda77..52b6e7a 100644 --- a/tests/Validation/cases/scopedPropertyAccess.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -48,6 +50,7 @@ "fqn": "MyNamespace\\A::a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json index 69ac4c7..e5f6850 100644 --- a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json @@ -9,6 +9,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json index 81cbfb6..aa508bc 100644 --- a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json @@ -12,6 +12,7 @@ "fqn": "A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "A::$a", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json index 27f0509..bd4ee70 100644 --- a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json @@ -18,6 +18,7 @@ "fqn": "TestClass", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -36,6 +37,7 @@ "fqn": "TestClass::$testProperty", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/self1.php.expected.json b/tests/Validation/cases/self1.php.expected.json index 41525d8..eb37fc7 100644 --- a/tests/Validation/cases/self1.php.expected.json +++ b/tests/Validation/cases/self1.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -72,6 +75,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -90,6 +94,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/self2.php.expected.json b/tests/Validation/cases/self2.php.expected.json index eb31aba..2280b1a 100644 --- a/tests/Validation/cases/self2.php.expected.json +++ b/tests/Validation/cases/self2.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -72,6 +75,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -90,6 +94,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json index f50b80c..3f69299 100644 --- a/tests/Validation/cases/self3.php.expected.json +++ b/tests/Validation/cases/self3.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -72,6 +75,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -90,6 +94,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index 4946001..ec3ae07 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -24,6 +24,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -42,6 +43,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -60,6 +62,7 @@ "fqn": "MyNamespace\\A::suite()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/self5.php.expected.json b/tests/Validation/cases/self5.php.expected.json index e1c99af..6727689 100644 --- a/tests/Validation/cases/self5.php.expected.json +++ b/tests/Validation/cases/self5.php.expected.json @@ -9,6 +9,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "MyNamespace\\A", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -45,6 +47,7 @@ "fqn": "MyNamespace\\A->typesProvider()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/static1.php.expected.json b/tests/Validation/cases/static1.php.expected.json index ca49245..69f8de0 100644 --- a/tests/Validation/cases/static1.php.expected.json +++ b/tests/Validation/cases/static1.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -72,6 +75,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -90,6 +94,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/static2.php.expected.json b/tests/Validation/cases/static2.php.expected.json index 06a0627..17f9a66 100644 --- a/tests/Validation/cases/static2.php.expected.json +++ b/tests/Validation/cases/static2.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -72,6 +75,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -90,6 +94,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json index 745bd56..f6e5189 100644 --- a/tests/Validation/cases/static3.php.expected.json +++ b/tests/Validation/cases/static3.php.expected.json @@ -15,6 +15,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -33,6 +34,7 @@ "fqn": "MyNamespace\\B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -51,6 +53,7 @@ "fqn": "MyNamespace\\B->b()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -72,6 +75,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -90,6 +94,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index 67c677a..e1e371f 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -12,6 +12,7 @@ "fqn": "MyNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -32,6 +33,7 @@ "MyNamespace\\B" ], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -50,6 +52,7 @@ "fqn": "MyNamespace\\A->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/staticMethodReturnType.php.expected.json b/tests/Validation/cases/staticMethodReturnType.php.expected.json index 21c989c..c178f0a 100644 --- a/tests/Validation/cases/staticMethodReturnType.php.expected.json +++ b/tests/Validation/cases/staticMethodReturnType.php.expected.json @@ -9,6 +9,7 @@ "fqn": "FooClass", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -27,6 +28,7 @@ "fqn": "FooClass::staticFoo()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": true, "canBeInstantiated": false, "symbolInformation": { @@ -46,6 +48,7 @@ "fqn": "FooClass->bar()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json index 982dba5..61669c8 100644 --- a/tests/Validation/cases/stringVariable.php.expected.json +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -5,6 +5,7 @@ "fqn": "B", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -23,6 +24,7 @@ "fqn": "B->hi", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -42,6 +44,7 @@ "fqn": "B->a()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json index 1f71b37..f686093 100644 --- a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json +++ b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json @@ -9,6 +9,7 @@ "fqn": "SomeNamespace", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { diff --git a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json index d40ef63..f6851bf 100644 --- a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json +++ b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json @@ -12,6 +12,7 @@ "fqn": "Foo", "extends": [], "isGlobal": true, + "roamed": false, "isStatic": false, "canBeInstantiated": true, "symbolInformation": { @@ -30,6 +31,7 @@ "fqn": "Foo->bar", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { @@ -49,6 +51,7 @@ "fqn": "Foo->foo()", "extends": [], "isGlobal": false, + "roamed": false, "isStatic": false, "canBeInstantiated": false, "symbolInformation": { From a772d9a2d70e223efbc8147518c8756607dbae70 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 16 Jun 2017 20:31:29 +0200 Subject: [PATCH 021/117] Remove content (#413) --- src/PhpDocument.php | 25 +++++++------------------ src/TreeAnalyzer.php | 3 +-- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/PhpDocument.php b/src/PhpDocument.php index 7820701..ff3cf8b 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -46,13 +46,6 @@ class PhpDocument */ private $uri; - /** - * The content of the document - * - * @var string - */ - private $content; - /** * The AST of the document * @@ -133,8 +126,6 @@ class PhpDocument */ public function updateContent(string $content) { - $this->content = $content; - // Unregister old definitions if (isset($this->definitions)) { foreach ($this->definitions as $fqn => $definition) { @@ -182,10 +173,10 @@ class PhpDocument */ public function getFormattedText() { - if (empty($this->content)) { + if (empty($this->getContent())) { return []; } - return Formatter::format($this->content, $this->uri); + return Formatter::format($this->getContent(), $this->uri); } /** @@ -195,7 +186,7 @@ class PhpDocument */ public function getContent() { - return $this->content; + return $this->sourceFileNode->fileContents; } /** @@ -256,12 +247,10 @@ class PhpDocument */ public function getRange(Range $range) { - if ($this->content === null) { - return null; - } - $start = $range->start->toOffset($this->content); - $length = $range->end->toOffset($this->content) - $start; - return substr($this->content, $start, $length); + $content = $this->getContent(); + $start = $range->start->toOffset($content); + $length = $range->end->toOffset($content) - $start; + return substr($content, $start, $length); } /** diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 3296ae8..2e34aa6 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -45,7 +45,6 @@ class TreeAnalyzer $this->parser = $parser; $this->docBlockFactory = $docBlockFactory; $this->definitionResolver = $definitionResolver; - $this->content = $content; $this->sourceFileNode = $this->parser->parseSourceFile($content, $uri); // TODO - docblock errors @@ -76,7 +75,7 @@ class TreeAnalyzer } if (($error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) { - $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->content); + $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents); $this->diagnostics[] = new Diagnostic( $error->message, From 548120314d96d5c58a8e3fcb36f1576aa54f721f Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 16 Jun 2017 20:39:32 +0200 Subject: [PATCH 022/117] Revert "Update CodeSniffer" This reverts commit 663ccd5f2396489ffd49a09b196150de51e6711a. --- composer.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 5375d79..93dec97 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "phpdocumentor/reflection-docblock": "^3.0", "sabre/event": "^5.0", "felixfbecker/advanced-json-rpc": "^2.0", - "squizlabs/php_codesniffer" : "^3.0", + "squizlabs/php_codesniffer" : "3.0.0RC3", "netresearch/jsonmapper": "^1.0", "webmozart/path-util": "^2.3", "webmozart/glob": "^4.1", @@ -49,8 +49,7 @@ "files" : [ "src/utils.php", "src/FqnUtilities.php", - "src/ParserHelpers.php", - "vendor/squizlabs/php_codesniffer/autoload.php" + "src/ParserHelpers.php" ] }, "autoload-dev": { From f97105740d68e185a71d378d974951855da2cbd1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 17 Jun 2017 01:53:08 -0700 Subject: [PATCH 023/117] Bump tolerant-php-parser (#415) * Bump tolerant-php-parser * Update test for new parser static support --- composer.json | 2 +- tests/Validation/cases/static4.php.expected.json | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 93dec97..3b5d58a 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "sabre/uri": "^2.0", "jetbrains/phpstorm-stubs": "dev-master", "composer/composer": "^1.3", - "Microsoft/tolerant-php-parser": "^0.0.2" + "Microsoft/tolerant-php-parser": "^0.0.3" }, "minimum-stability": "dev", "prefer-stable": true, diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index e1e371f..3d05ede 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -2,9 +2,6 @@ "references": { "MyNamespace\\B": [ "./static4.php" - ], - "static": [ - "./static4.php" ] }, "definitions": { From dae3f2576c95d1dc52460d372dae11ed64700e51 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Mon, 19 Jun 2017 13:23:43 +0300 Subject: [PATCH 024/117] Add $this completion (#419) --- fixtures/completion/this.php | 15 ++++ fixtures/completion/this_with_prefix.php | 15 ++++ src/DefinitionResolver.php | 3 +- tests/Server/TextDocument/CompletionTest.php | 84 ++++++++++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 fixtures/completion/this.php create mode 100644 fixtures/completion/this_with_prefix.php diff --git a/fixtures/completion/this.php b/fixtures/completion/this.php new file mode 100644 index 0000000..b3d684c --- /dev/null +++ b/fixtures/completion/this.php @@ -0,0 +1,15 @@ + + } +} diff --git a/fixtures/completion/this_with_prefix.php b/fixtures/completion/this_with_prefix.php new file mode 100644 index 0000000..6325b99 --- /dev/null +++ b/fixtures/completion/this_with_prefix.php @@ -0,0 +1,15 @@ +m + } +} diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 7772990..713d868 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -574,8 +574,9 @@ class DefinitionResolver // $this -> Type\this // $myVariable -> type of corresponding assignment expression if ($expr instanceof Node\Expression\Variable || $expr instanceof Node\UseVariableName) { + // TODO: this will need to change when fluent interfaces are supported if ($expr->getName() === 'this') { - return new Types\This; + return new Types\Object_(new Fqsen('\\' . $this->getContainingClassFqn($expr))); } // Find variable definition (parameter or assignment expression) $defNode = $this->resolveVariableToNode($expr); diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index be34200..3cdb5f8 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -569,4 +569,88 @@ class CompletionTest extends TestCase $this->assertEquals($subsetList->isIncomplete, $list->isIncomplete); } + + public function testThisWithoutPrefix() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(12, 15) + )->wait(); + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'foo', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'bar', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'method', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ), + new CompletionItem( + 'test', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ) + ], true), $items); + } + + public function testThisWithPrefix() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(12, 16) + )->wait(); + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'testProperty', + CompletionItemKind::PROPERTY, + '\TestClass', // Type of the property + 'Reprehenderit magna velit mollit ipsum do.' + ), + new CompletionItem( + 'testMethod', + CompletionItemKind::METHOD, + '\TestClass', // Return type of the method + 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' + ), + new CompletionItem( + 'foo', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'bar', + CompletionItemKind::PROPERTY, + 'mixed', // Type of the property + null + ), + new CompletionItem( + 'method', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ), + new CompletionItem( + 'test', + CompletionItemKind::METHOD, + 'mixed', // Return type of the method + null + ) + ], true), $items); + } } From a454cd2873c0e137e538560e1bddada55386001f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 19 Jun 2017 23:35:47 -0700 Subject: [PATCH 025/117] Add vendor/validation folders to search.exclude (#420) --- .gitignore | 1 - .vscode/settings.json | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index e7997bf..f6b089b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .DS_Store -.vscode .idea vendor/ .phpls/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..38150bd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "search.exclude": { + "**/validation": true, + "**/tests/Validation/cases": true + } +} From 08fe84de35221d107d581654b07f84aed736cc3c Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Tue, 20 Jun 2017 08:38:06 +0200 Subject: [PATCH 026/117] Add launch.json --- .vscode/launch.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0f77709 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + }, + { + "name": "PHPUnit", + "type": "php", + "request": "launch", + "program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit", + "cwd": "${workspaceRoot}", + "args": [ + // "--filter", "CompletionTest" + ] + } + ] +} From f43ce50d5ab534581212e0abb74841d9fb18a90a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 21 Jun 2017 11:48:41 +0200 Subject: [PATCH 027/117] Default memory limit to 4GB --- README.md | 2 +- bin/php-language-server.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 37800e0..432d874 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ Example: #### `--memory-limit=integer` (optional) Sets memory limit for language server. Equivalent to [memory-limit](http://php.net/manual/en/ini.core.php#ini.memory-limit) php.ini directive. -By default there is no memory limit. +The default is 4GB (which is way more than needed). Example: diff --git a/bin/php-language-server.php b/bin/php-language-server.php index 0e3de2a..2a2ab69 100644 --- a/bin/php-language-server.php +++ b/bin/php-language-server.php @@ -6,7 +6,7 @@ use Composer\{Factory, XdebugHandler}; $options = getopt('', ['tcp::', 'tcp-server::', 'memory-limit::']); -ini_set('memory_limit', $options['memory-limit'] ?? -1); +ini_set('memory_limit', $options['memory-limit'] ?? '4G'); foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) { if (file_exists($file)) { From 00552120ad6efadd9e7f2e167b363372c92dc6aa Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 21 Jun 2017 14:17:36 +0200 Subject: [PATCH 028/117] Restrict workspace/symbol results to non-dependency symbols (#426) This improves performance a lot and matches what other language servers do --- src/Server/Workspace.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index d2f8cec..e663a06 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -33,7 +33,7 @@ class Workspace * * @var ProjectIndex */ - private $index; + private $projectIndex; /** * @var DependenciesIndex @@ -57,17 +57,17 @@ class Workspace /** * @param LanguageClient $client LanguageClient instance used to signal updated results - * @param ProjectIndex $index Index that is searched on a workspace/symbol request + * @param ProjectIndex $projectIndex Index that is used to wait for full index completeness * @param DependenciesIndex $dependenciesIndex Index that is used on a workspace/xreferences request * @param DependenciesIndex $sourceIndex Index that is used on a workspace/xreferences request * @param \stdClass $composerLock The parsed composer.lock of the project, if any * @param PhpDocumentLoader $documentLoader PhpDocumentLoader instance to load documents */ - public function __construct(LanguageClient $client, ProjectIndex $index, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null) + public function __construct(LanguageClient $client, ProjectIndex $projectIndex, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null) { $this->client = $client; $this->sourceIndex = $sourceIndex; - $this->index = $index; + $this->projectIndex = $projectIndex; $this->dependenciesIndex = $dependenciesIndex; $this->composerLock = $composerLock; $this->documentLoader = $documentLoader; @@ -84,11 +84,11 @@ class Workspace { return coroutine(function () use ($query) { // Wait until indexing for definitions finished - if (!$this->index->isStaticComplete()) { - yield waitForEvent($this->index, 'static-complete'); + if (!$this->sourceIndex->isStaticComplete()) { + yield waitForEvent($this->sourceIndex, 'static-complete'); } $symbols = []; - foreach ($this->index->getDefinitions() as $fqn => $definition) { + foreach ($this->sourceIndex->getDefinitions() as $fqn => $definition) { if ($query === '' || stripos($fqn, $query) !== false) { $symbols[] = $definition->symbolInformation; } @@ -126,8 +126,8 @@ class Workspace return []; } // Wait until indexing finished - if (!$this->index->isComplete()) { - yield waitForEvent($this->index, 'complete'); + if (!$this->projectIndex->isComplete()) { + yield waitForEvent($this->projectIndex, 'complete'); } /** Map from URI to array of referenced FQNs in dependencies */ $refs = []; From fced1d5af62db1d2c2c3d7b16d2ee055fb3c64b7 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 22 Jun 2017 17:34:28 +0200 Subject: [PATCH 029/117] Fix textDocument/xdefinition (#429) --- src/Server/TextDocument.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 2e8e4bf..be34dc2 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -368,9 +368,10 @@ class TextDocument return []; } // Handle definition nodes + $fqn = DefinitionResolver::getDefinedFqn($node); while (true) { if ($fqn) { - $def = $this->index->getDefinition($definedFqn); + $def = $this->index->getDefinition($fqn); } else { // Handle reference nodes $def = $this->definitionResolver->resolveReferenceNodeToDefinition($node); From fc0bf4c163e94d39ff14c9209050b67bbda2bf30 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 22 Jun 2017 20:06:10 +0200 Subject: [PATCH 030/117] Fix workspace/xreferences (#424) * Make Descriptors minimal SymbolDescriptor and PackageDescriptor should only contain the minumum amount of properties needed * Add missing use * Fixes * Ignore ReferenceInformation->symbol --- src/Protocol/PackageDescriptor.php | 25 +++++++++++++++++++ src/Protocol/SymbolDescriptor.php | 17 +++++++------ src/Server/TextDocument.php | 39 +++++++++++++++--------------- src/Server/Workspace.php | 35 ++++----------------------- 4 files changed, 59 insertions(+), 57 deletions(-) create mode 100644 src/Protocol/PackageDescriptor.php diff --git a/src/Protocol/PackageDescriptor.php b/src/Protocol/PackageDescriptor.php new file mode 100644 index 0000000..e635740 --- /dev/null +++ b/src/Protocol/PackageDescriptor.php @@ -0,0 +1,25 @@ +name = $name; + } +} diff --git a/src/Protocol/SymbolDescriptor.php b/src/Protocol/SymbolDescriptor.php index fa74bcb..4116864 100644 --- a/src/Protocol/SymbolDescriptor.php +++ b/src/Protocol/SymbolDescriptor.php @@ -3,7 +3,10 @@ declare(strict_types = 1); namespace LanguageServer\Protocol; -class SymbolDescriptor extends SymbolInformation +/** + * Uniquely identifies a symbol + */ +class SymbolDescriptor { /** * The fully qualified structural element name, a globally unique identifier for the symbol. @@ -13,19 +16,17 @@ class SymbolDescriptor extends SymbolInformation public $fqsen; /** - * A package from the composer.lock file or the contents of the composer.json - * Example: https://github.com/composer/composer/blob/master/composer.lock#L10 - * Available fields may differ + * Identifies the Composer package the symbol is defined in (if any) * - * @var object|null + * @var PackageDescriptor|null */ public $package; /** - * @param string $fqsen The fully qualified structural element name, a globally unique identifier for the symbol. - * @param object $package A package from the composer.lock file or the contents of the composer.json + * @param string $fqsen The fully qualified structural element name, a globally unique identifier for the symbol. + * @param PackageDescriptor $package Identifies the Composer package the symbol is defined in */ - public function __construct(string $fqsen = null, $package = null) + public function __construct(string $fqsen = null, PackageDescriptor $package = null) { $this->fqsen = $fqsen; $this->package = $package; diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index be34dc2..5a2819e 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -8,14 +8,26 @@ use LanguageServer\{ }; use LanguageServer\Index\ReadableIndex; use LanguageServer\Protocol\{ - FormattingOptions, Hover, Location, MarkedString, Position, Range, ReferenceContext, SymbolDescriptor, SymbolLocationInformation, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier + FormattingOptions, + Hover, + Location, + MarkedString, + Position, + Range, + ReferenceContext, + SymbolDescriptor, + PackageDescriptor, + SymbolLocationInformation, + TextDocumentIdentifier, + TextDocumentItem, + VersionedTextDocumentIdentifier }; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; use Sabre\Event\Promise; use Sabre\Uri; use function LanguageServer\{ - isVendored, waitForEvent + isVendored, waitForEvent, getPackageName }; use function Sabre\Event\coroutine; @@ -389,25 +401,14 @@ class TextDocument ) { return []; } - $symbol = new SymbolDescriptor; - foreach (get_object_vars($def->symbolInformation) as $prop => $val) { - $symbol->$prop = $val; - } - $symbol->fqsen = $def->fqn; + // if Definition is inside a dependency, use the package name $packageName = getPackageName($def->symbolInformation->location->uri, $this->composerJson); - if ($packageName && $this->composerLock !== null) { - // Definition is inside a dependency - foreach (array_merge($this->composerLock->packages, $this->composerLock->{'packages-dev'}) as $package) { - if ($package->name === $packageName) { - $symbol->package = $package; - break; - } - } - } else if ($this->composerJson !== null) { - // Definition belongs to a root package - $symbol->package = $this->composerJson; + // else use the package name of the root package (if exists) + if (!$packageName && $this->composerJson !== null) { + $packageName = $this->composerJson->name; } - return [new SymbolLocationInformation($symbol, $symbol->location)]; + $descriptor = new SymbolDescriptor($def->fqn, new PackageDescriptor($packageName)); + return [new SymbolLocationInformation($descriptor, $def->symbolInformation->location)]; }); } } diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index e663a06..61db068 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -10,13 +10,15 @@ use LanguageServer\Protocol\{ FileEvent, SymbolInformation, SymbolDescriptor, + PackageDescriptor, ReferenceInformation, DependencyReference, - Location + Location, + MessageType }; use Sabre\Event\Promise; use function Sabre\Event\coroutine; -use function LanguageServer\{waitForEvent, getPackageName}; +use function LanguageServer\waitForEvent; /** * Provides method handlers for all workspace/* methods @@ -146,38 +148,11 @@ class Workspace $refInfos = []; foreach ($refs as $uri => $fqns) { foreach ($fqns as $fqn) { - $def = $this->dependenciesIndex->getDefinition($fqn); - $symbol = new SymbolDescriptor; - $symbol->fqsen = $fqn; - foreach (get_object_vars($def->symbolInformation) as $prop => $val) { - $symbol->$prop = $val; - } - // Find out package name - $packageName = getPackageName($def->symbolInformation->location->uri, $this->composerJson); - foreach (array_merge($this->composerLock->packages, $this->composerLock->{'packages-dev'}) as $package) { - if ($package->name === $packageName) { - $symbol->package = $package; - break; - } - } - // If there was no FQSEN provided, check if query attributes match - if (!isset($query->fqsen)) { - $matches = true; - foreach (get_object_vars($query) as $prop => $val) { - if ($query->$prop != $symbol->$prop) { - $matches = false; - break; - } - } - if (!$matches) { - continue; - } - } $doc = yield $this->documentLoader->getOrLoad($uri); foreach ($doc->getReferenceNodesByFqn($fqn) as $node) { $refInfo = new ReferenceInformation; $refInfo->reference = Location::fromNode($node); - $refInfo->symbol = $symbol; + $refInfo->symbol = $query; $refInfos[] = $refInfo; } } From 94fc0405fd4aed4fe7d779dc04a2924e3ced5b45 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 1 Jul 2017 14:32:56 +0200 Subject: [PATCH 031/117] Correct parser link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 432d874..ef351ec 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A pure PHP implementation of the open [Language Server Protocol](https://github.com/Microsoft/language-server-protocol). Provides static code analysis for PHP for any IDE. -Uses the great [PHP-Parser](https://github.com/nikic/PHP-Parser), +Uses the great [Tolerant PHP Parser](https://github.com/Microsoft/tolerant-php-parser), [phpDocumentor's DocBlock reflection](https://github.com/phpDocumentor/ReflectionDocBlock) and an [event loop](http://sabre.io/event/loop/) for concurrency. From 35f33c8c91222b8409006cc68738e4000e33a4a3 Mon Sep 17 00:00:00 2001 From: Ivan Bozhanov Date: Fri, 7 Jul 2017 14:18:19 +0300 Subject: [PATCH 032/117] Fluent interfaces support (#421) --- fixtures/completion/this_return_value.php | 23 +++++++++++ src/CompletionProvider.php | 42 +++++++++----------- src/Definition.php | 31 +++++++++++++++ src/DefinitionResolver.php | 19 ++++++--- tests/Server/TextDocument/CompletionTest.php | 27 +++++++++++++ 5 files changed, 112 insertions(+), 30 deletions(-) create mode 100644 fixtures/completion/this_return_value.php diff --git a/fixtures/completion/this_return_value.php b/fixtures/completion/this_return_value.php new file mode 100644 index 0000000..167d0b4 --- /dev/null +++ b/fixtures/completion/this_return_value.php @@ -0,0 +1,23 @@ +foo()->q + } + public function qux() + { + } +} diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index f4f091d..2414ead 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -14,6 +14,7 @@ use LanguageServer\Protocol\{ }; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; +use Generator; class CompletionProvider { @@ -196,18 +197,15 @@ class CompletionProvider // $a->| // Multiple prefixes for all possible types - $prefixes = FqnUtilities\getFqnsFromType( + $fqns = FqnUtilities\getFqnsFromType( $this->definitionResolver->resolveExpressionNodeToType($node->dereferencableExpression) ); - // Include parent classes - $prefixes = $this->expandParentFqns($prefixes); - - // Add the object access operator to only get members - foreach ($prefixes as &$prefix) { - $prefix .= '->'; + // Add the object access operator to only get members of all parents + $prefixes = []; + foreach ($this->expandParentFqns($fqns) as $prefix) { + $prefixes[] = $prefix . '->'; } - unset($prefix); // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { @@ -232,18 +230,15 @@ class CompletionProvider // TODO: $a::| // Resolve all possible types to FQNs - $prefixes = FqnUtilities\getFqnsFromType( + $fqns = FqnUtilities\getFqnsFromType( $classType = $this->definitionResolver->resolveExpressionNodeToType($scoped->scopeResolutionQualifier) ); - // Add parent classes - $prefixes = $this->expandParentFqns($prefixes); - - // Append :: operator to only get static members - foreach ($prefixes as &$prefix) { - $prefix .= '::'; + // Append :: operator to only get static members of all parents + $prefixes = []; + foreach ($this->expandParentFqns($fqns) as $prefix) { + $prefixes[] = $prefix . '::'; } - unset($prefix); // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { @@ -377,23 +372,22 @@ class CompletionProvider } /** - * Adds the FQNs of all parent classes to an array of FQNs of classes + * Yields FQNs from an array along with the FQNs of all parent classes * * @param string[] $fqns - * @return string[] + * @return Generator */ - private function expandParentFqns(array $fqns): array + private function expandParentFqns(array $fqns) : Generator { - $expanded = $fqns; foreach ($fqns as $fqn) { + yield $fqn; $def = $this->index->getDefinition($fqn); - if ($def) { - foreach ($this->expandParentFqns($def->extends ?? []) as $parent) { - $expanded[] = $parent; + if ($def !== null) { + foreach ($def->getAncestorDefinitions($this->index) as $name => $def) { + yield $name; } } } - return $expanded; } /** diff --git a/src/Definition.php b/src/Definition.php index cf0f574..c03e852 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -3,9 +3,11 @@ declare(strict_types = 1); namespace LanguageServer; +use LanguageServer\Index\ReadableIndex; use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver}; use LanguageServer\Protocol\SymbolInformation; use Exception; +use Generator; /** * Class used to represent symbols @@ -95,4 +97,33 @@ class Definition * @var string */ public $documentation; + + /** + * Yields the definitons of all ancestor classes (the Definition fqn is yielded as key) + * + * @param ReadableIndex $index the index to search for needed definitions + * @param bool $includeSelf should the first yielded value be the current definition itself + * @return Generator + */ + public function getAncestorDefinitions(ReadableIndex $index, bool $includeSelf = false): Generator + { + if ($includeSelf) { + yield $this->fqn => $this; + } + if ($this->extends !== null) { + // iterating once, storing the references and iterating again + // guarantees that closest definitions are yielded first + $definitions = []; + foreach ($this->extends as $fqn) { + $def = $index->getDefinition($fqn); + if ($def !== null) { + yield $def->fqn => $def; + $definitions[] = $def; + } + } + foreach ($definitions as $def) { + yield from $def->getAncestorDefinitions($index); + } + } + } } diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 713d868..51c5955 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -574,7 +574,6 @@ class DefinitionResolver // $this -> Type\this // $myVariable -> type of corresponding assignment expression if ($expr instanceof Node\Expression\Variable || $expr instanceof Node\UseVariableName) { - // TODO: this will need to change when fluent interfaces are supported if ($expr->getName() === 'this') { return new Types\Object_(new Fqsen('\\' . $this->getContainingClassFqn($expr))); } @@ -667,13 +666,21 @@ class DefinitionResolver } else { $classFqn = substr((string)$t->getFqsen(), 1); } - $fqn = $classFqn . '->' . $expr->memberName->getText($expr->getFileContents()); + $add = '->' . $expr->memberName->getText($expr->getFileContents()); if ($expr->parent instanceof Node\Expression\CallExpression) { - $fqn .= '()'; + $add .= '()'; } - $def = $this->index->getDefinition($fqn); - if ($def !== null) { - return $def->type; + $classDef = $this->index->getDefinition($classFqn); + if ($classDef !== null) { + foreach ($classDef->getAncestorDefinitions($this->index, true) as $fqn => $def) { + $def = $this->index->getDefinition($fqn . $add); + if ($def !== null) { + if ($def->type instanceof Types\This) { + return new Types\Object_(new Fqsen('\\' . $classFqn)); + } + return $def->type; + } + } } } } diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 3cdb5f8..0d68ec3 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -653,4 +653,31 @@ class CompletionTest extends TestCase ) ], true), $items); } + + public function testThisReturnValue() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_return_value.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(17, 23) + )->wait(); + $this->assertEquals(new CompletionList([ + new CompletionItem( + 'foo', + CompletionItemKind::METHOD, + '$this' // Return type of the method + ), + new CompletionItem( + 'bar', + CompletionItemKind::METHOD, + 'mixed' // Return type of the method + ), + new CompletionItem( + 'qux', + CompletionItemKind::METHOD, + 'mixed' // Return type of the method + ) + ], true), $items); + } } From 7ce228417605bc8559716d300f21d0ce26209fda Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 19 Jul 2017 13:15:48 +0200 Subject: [PATCH 033/117] Pin phpdocumentor/reflection-docblock dependency https://github.com/phpDocumentor/ReflectionDocBlock/issues/109 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3b5d58a..fb9054f 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ }, "require": { "php": ">=7.0", - "phpdocumentor/reflection-docblock": "^3.0", + "phpdocumentor/reflection-docblock": "~3.1.1", "sabre/event": "^5.0", "felixfbecker/advanced-json-rpc": "^2.0", "squizlabs/php_codesniffer" : "3.0.0RC3", From 63bf43e40c446070a899ad068033ff1d878a0520 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Aug 2017 10:29:55 -0700 Subject: [PATCH 034/117] Bump tolerant-php-parser to get fix (#457) for https://github.com/Microsoft/tolerant-php-parser/issues/12 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fb9054f..50f314b 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "sabre/uri": "^2.0", "jetbrains/phpstorm-stubs": "dev-master", "composer/composer": "^1.3", - "Microsoft/tolerant-php-parser": "^0.0.3" + "Microsoft/tolerant-php-parser": "^0.0.4" }, "minimum-stability": "dev", "prefer-stable": true, From a4739430f82063ac0a1800a12bbe94d01fb1128f Mon Sep 17 00:00:00 2001 From: John Nguyen Date: Tue, 22 Aug 2017 15:43:17 +1000 Subject: [PATCH 035/117] Fix memory leak issue (#459) Closes #425 --- src/FqnUtilities.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FqnUtilities.php b/src/FqnUtilities.php index a3aab05..b5d01a9 100644 --- a/src/FqnUtilities.php +++ b/src/FqnUtilities.php @@ -22,7 +22,7 @@ function getFqnsFromType($type): array } if ($type instanceof Types\Compound) { for ($i = 0; $t = $type->get($i); $i++) { - foreach (getFqnsFromType($type) as $fqn) { + foreach (getFqnsFromType($t) as $fqn) { $fqns[] = $fqn; } } From d4443465bb93d2f3cf3013b5c4a3ee482b66f905 Mon Sep 17 00:00:00 2001 From: Stephan Unverwerth Date: Thu, 28 Sep 2017 21:53:12 +0200 Subject: [PATCH 036/117] Fix missing diagnostics for nodes (#484) * Fix missing diagnostics for nodes * Refactor TreeAnalyzer --- src/TreeAnalyzer.php | 92 +++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 2e34aa6..0e43a8c 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -49,44 +49,73 @@ class TreeAnalyzer // TODO - docblock errors - $this->collectDefinitionsAndReferences($this->sourceFileNode); + $this->traverse($this->sourceFileNode); } - private function collectDefinitionsAndReferences(Node $sourceFileNode) + /** + * Collects Parser diagnostic messages for the Node/Token + * and transforms them into LSP Format + * + * @param Node|Token $node + * @return Diagnostic + */ + private function collectDiagnostics($node) { - foreach ($sourceFileNode::CHILD_NAMES as $name) { - $node = $sourceFileNode->$name; + if (($error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) { + $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents); - if ($node === null) { - continue; + switch ($error->kind) { + case \Microsoft\PhpParser\DiagnosticKind::Error: + $severity = DiagnosticSeverity::ERROR; + break; + case \Microsoft\PhpParser\DiagnosticKind::Warning: + default: + $severity = DiagnosticSeverity::WARNING; + break; } - if (\is_array($node)) { - foreach ($node as $child) { - if ($child instanceof Node) { - $this->update($child); - } + $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, + $severity, + 'php' + ); + } + } + + /** + * Recursive AST traversal to collect definitions/references and diagnostics + * + * @param Node|Token $currentNode The node/token to process + */ + private function traverse($currentNode) + { + $this->collectDiagnostics($currentNode); + + // Only update/descend into Nodes, Tokens are leaves + if ($currentNode instanceof Node) { + $this->collectDefinitionsAndReferences($currentNode); + + foreach ($currentNode::CHILD_NAMES as $name) { + $child = $currentNode->$name; + + if ($child === null) { + continue; } - continue; - } - if ($node instanceof Node) { - $this->update($node); - } - - if (($error = PhpParser\DiagnosticsProvider::checkDiagnostics($node)) !== null) { - $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents); - - $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' - ); + if (\is_array($child)) { + foreach ($child as $actualChild) { + if ($actualChild !== null) { + $this->traverse($actualChild); + } + } + } else { + $this->traverse($child); + } } } } @@ -96,7 +125,7 @@ class TreeAnalyzer * * @param Node $node */ - private function update(Node $node) + private function collectDefinitionsAndReferences(Node $node) { $fqn = ($this->definitionResolver)::getDefinedFqn($node); // Only index definitions with an FQN (no variables) @@ -152,7 +181,6 @@ class TreeAnalyzer } } } - $this->collectDefinitionsAndReferences($node); } /** From d24c42008e0e84bb2646e0829f958efdb6e77375 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 22:36:04 +0200 Subject: [PATCH 037/117] Exclude non-essential files in .gitattributes (#486) * Exclude non-essential files in .gitattributes https://www.reddit.com/r/PHP/comments/2jzp6k/i_dont_need_your_tests_in_my_production/ * Add validation and .gitmodules --- .gitattributes | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7600d2f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +* text=auto + +/.vscode export-ignore +/fixtures export-ignore +/tests export-ignore +/images export-ignore +/validation export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.gitmodules export-ignore +/.dockerignore export-ignore +/.travis.yml export-ignore +/codecov.yml export-ignore +/Dockerfile export-ignore +/Performance.php export-ignore +/php.ini export-ignore +/phpcs.xml.dist export-ignore +/phpunit.xml.dist export-ignore +/README.md export-ignore From 3d8655d504dd5ba0e6837c4c40180d0b178f5976 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 22:37:28 +0200 Subject: [PATCH 038/117] Update phpunit config (#488) * Update phpunit config * Rename DocumentHighlight class --- phpunit.xml.dist | 23 ++++++++++++++++++----- src/Protocol/DocumentHighlight.php | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 38df71a..a6970f3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,14 +1,27 @@ - + - ./tests + ./tests - - - ./src + + ./src diff --git a/src/Protocol/DocumentHighlight.php b/src/Protocol/DocumentHighlight.php index e00c842..167d45a 100644 --- a/src/Protocol/DocumentHighlight.php +++ b/src/Protocol/DocumentHighlight.php @@ -7,7 +7,7 @@ namespace LanguageServer\Protocol; * special attention. Usually a document highlight is visualized by changing * the background color of its range. */ -class DocumentHighlightKind +class DocumentHighlight { /** * The range this highlight applies to. From b9ebfb52c9f6498eb9ac6063b458e07857e89445 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 22:58:37 +0200 Subject: [PATCH 039/117] Update composer.json structure (#487) --- composer.json | 58 +++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/composer.json b/composer.json index 50f314b..d28ef31 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,7 @@ { "name": "felixfbecker/language-server", "description": "PHP Implementation of the Visual Studio Code Language Server Protocol", - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], "license": "ISC", - "type": "library", "keywords": [ "php", "language", @@ -21,27 +14,30 @@ "autocompletion", "refactor" ], - "bin": ["bin/php-language-server.php"], - "scripts": { - "parse-stubs": "LanguageServer\\ComposerScripts::parseStubs", - "post-install-cmd": "@parse-stubs" - }, + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], "require": { - "php": ">=7.0", + "php": "^7.0", + "composer/composer": "^1.3", + "felixfbecker/advanced-json-rpc": "^2.0", + "jetbrains/phpstorm-stubs": "dev-master", + "microsoft/tolerant-php-parser": "^0.0.4", + "netresearch/jsonmapper": "^1.0", "phpdocumentor/reflection-docblock": "~3.1.1", "sabre/event": "^5.0", - "felixfbecker/advanced-json-rpc": "^2.0", - "squizlabs/php_codesniffer" : "3.0.0RC3", - "netresearch/jsonmapper": "^1.0", - "webmozart/path-util": "^2.3", - "webmozart/glob": "^4.1", "sabre/uri": "^2.0", - "jetbrains/phpstorm-stubs": "dev-master", - "composer/composer": "^1.3", - "Microsoft/tolerant-php-parser": "^0.0.4" + "squizlabs/php_codesniffer": "3.0.0RC3", + "webmozart/glob": "^4.1", + "webmozart/path-util": "^2.3" + }, + "require-dev": { + "phpunit/phpunit": "^5.5", + "phpunit/php-code-coverage": "^4.0" }, - "minimum-stability": "dev", - "prefer-stable": true, "autoload": { "psr-4": { "LanguageServer\\": "src/" @@ -57,8 +53,16 @@ "LanguageServer\\Tests\\": "tests/" } }, - "require-dev": { - "phpunit/phpunit": "^5.5", - "phpunit/php-code-coverage": "^4.0" - } + "bin": [ + "bin/php-language-server.php" + ], + "scripts": { + "parse-stubs": "LanguageServer\\ComposerScripts::parseStubs", + "post-install-cmd": "@parse-stubs" + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true } From 0c399150a3e6349f0d1985c025bd099e3bb15206 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Mon, 2 Oct 2017 23:11:06 +0200 Subject: [PATCH 040/117] Update travis and phpunit (#489) --- .travis.yml | 30 ++++++++++++++++-------------- composer.json | 3 +-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7fb9c9a..05ebb2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,30 @@ language: php php: - - '7.0' + - 7.0 + - 7.1 + - 7.2 services: - - docker + - docker cache: directories: - - $HOME/.composer/cache + - $HOME/.composer/cache install: - - composer install - - composer run-script parse-stubs + - composer install --prefer-dist --no-interaction + - composer run-script parse-stubs script: - - vendor/bin/phpcs -n - - vendor/bin/phpunit --coverage-clover=coverage.xml + - vendor/bin/phpcs -n + - vendor/bin/phpunit --coverage-clover=coverage.xml after_success: - - bash <(curl -s https://codecov.io/bash) - - | - if [[ $TRAVIS_TAG == v* ]]; then - docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . - docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" - docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} - fi + - bash <(curl -s https://codecov.io/bash) + - | + if [[ $TRAVIS_TAG == v* ]]; then + docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . + docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" + docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} + fi diff --git a/composer.json b/composer.json index d28ef31..c5f6722 100644 --- a/composer.json +++ b/composer.json @@ -35,8 +35,7 @@ "webmozart/path-util": "^2.3" }, "require-dev": { - "phpunit/phpunit": "^5.5", - "phpunit/php-code-coverage": "^4.0" + "phpunit/phpunit": "^6.3" }, "autoload": { "psr-4": { From e31f7b5923a536e3f1162acdbe3cd2014f7610fc Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 19 Oct 2017 14:38:20 -0700 Subject: [PATCH 041/117] Add more Composer scripts --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c5f6722..1f62a5a 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,10 @@ ], "scripts": { "parse-stubs": "LanguageServer\\ComposerScripts::parseStubs", - "post-install-cmd": "@parse-stubs" + "post-install-cmd": "@parse-stubs", + "post-update-cmd": "@parse-stubs", + "test": "vendor/bin/phpunit", + "lint": "vendor/bin/phpcs" }, "config": { "sort-packages": true From 19bf94ac7bd28ef5cd9a1dc035b55013f60441c5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 19 Oct 2017 14:44:56 -0700 Subject: [PATCH 042/117] Improve README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ef351ec..3745ff6 100644 --- a/README.md +++ b/README.md @@ -196,8 +196,8 @@ Then parse the stubs with Run the tests with - vendor/bin/phpunit + composer test Lint with - vendor/bin/phpcs + composer lint From 1240f25e0192f7c5282b86427656e54687cc3278 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 19 Oct 2017 14:45:36 -0700 Subject: [PATCH 043/117] Update parser --- composer.json | 2 +- tests/Validation/cases/stringVariable.php.expected.json | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 1f62a5a..225de3b 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "composer/composer": "^1.3", "felixfbecker/advanced-json-rpc": "^2.0", "jetbrains/phpstorm-stubs": "dev-master", - "microsoft/tolerant-php-parser": "^0.0.4", + "microsoft/tolerant-php-parser": "^0.0.6", "netresearch/jsonmapper": "^1.0", "phpdocumentor/reflection-docblock": "~3.1.1", "sabre/event": "^5.0", diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json index 61669c8..fed8a23 100644 --- a/tests/Validation/cases/stringVariable.php.expected.json +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -1,5 +1,9 @@ { - "references": [], + "references": { + "B->hi": [ + "./stringVariable.php" + ] + }, "definitions": { "B": { "fqn": "B", From 7b1176dd9da863c3b094f01a303733ec5faae952 Mon Sep 17 00:00:00 2001 From: "Dependencies.io Bot" Date: Fri, 20 Oct 2017 20:15:31 +0000 Subject: [PATCH 044/117] ci(dependencies.io): add dependencies.yml config --- dependencies.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 dependencies.yml diff --git a/dependencies.yml b/dependencies.yml new file mode 100644 index 0000000..6f8dfc7 --- /dev/null +++ b/dependencies.yml @@ -0,0 +1,13 @@ +collectors: + +- type: php-composer + path: / + actors: + # pull requests for updates to our major version + - type: php-composer + versions: "L.Y.Y" + # create issues for new major versions + - type: repo-issue + versions: "Y.0.0" + settings: + commit_message_prefix: "chore: " From a934aff7a9216824faf4a7f23d5bbff0d42e51de Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 21 Oct 2017 18:39:31 -0700 Subject: [PATCH 045/117] ci(release): use semantic-release --- .dockerignore | 1 + .editorconfig | 2 +- .gitignore | 3 ++- .npmrc | 1 + .travis.yml | 16 ++++++++++------ README.md | 1 + dependencies.yml | 5 +---- package.json | 28 ++++++++++++++++++++++++++++ release-docker.sh | 6 ++++++ 9 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 .npmrc create mode 100644 package.json create mode 100644 release-docker.sh diff --git a/.dockerignore b/.dockerignore index a839c69..29f27b6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,3 +7,4 @@ fixtures/ coverage/ coverage.xml images/ +node_modules/ diff --git a/.editorconfig b/.editorconfig index d162066..2954e68 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,7 @@ trim_trailing_whitespace = true indent_style = space indent_size = 4 -[*.json,*.yml] +[*.{json,yml}] indent_size = 2 [composer.json] diff --git a/.gitignore b/.gitignore index f6b089b..6476dbe 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ vendor/ .phpls/ composer.lock stubs -*.ast \ No newline at end of file +*.ast +node_modules/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.travis.yml b/.travis.yml index 05ebb2a..02e1797 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ services: cache: directories: - $HOME/.composer/cache + - $HOME/.npm install: - composer install --prefer-dist --no-interaction @@ -22,9 +23,12 @@ script: after_success: - bash <(curl -s https://codecov.io/bash) - - | - if [[ $TRAVIS_TAG == v* ]]; then - docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . - docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" - docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} - fi + - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* + - git fetch --tags + - nvm install 8 && nvm use 8 + - npm install + - npm run semantic-release + +branches: + except: + - /^v\d+\.\d+\.\d+$/ diff --git a/README.md b/README.md index 3745ff6..0aad4e5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Build Status](https://travis-ci.org/felixfbecker/php-language-server.svg?branch=master)](https://travis-ci.org/felixfbecker/php-language-server) [![Coverage](https://codecov.io/gh/felixfbecker/php-language-server/branch/master/graph/badge.svg)](https://codecov.io/gh/felixfbecker/php-language-server) [![Dependency Status](https://gemnasium.com/badges/github.com/felixfbecker/php-language-server.svg)](https://gemnasium.com/github.com/felixfbecker/php-language-server) +[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://php.net/) [![License](https://img.shields.io/packagist/l/felixfbecker/language-server.svg)](https://github.com/felixfbecker/php-language-server/blob/master/LICENSE.txt) [![Gitter](https://badges.gitter.im/felixfbecker/php-language-server.svg)](https://gitter.im/felixfbecker/php-language-server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) diff --git a/dependencies.yml b/dependencies.yml index 6f8dfc7..320ae87 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -3,11 +3,8 @@ collectors: - type: php-composer path: / actors: - # pull requests for updates to our major version + # pull requests for new major versions - type: php-composer - versions: "L.Y.Y" - # create issues for new major versions - - type: repo-issue versions: "Y.0.0" settings: commit_message_prefix: "chore: " diff --git a/package.json b/package.json new file mode 100644 index 0000000..d67062f --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "php-language-server", + "version": "0.0.0-development", + "private": true, + "scripts": { + "commitmsg": "validate-commit-msg", + "semantic-release": "semantic-release pre && ./release-docker.sh && semantic-release post" + }, + "devDependencies": { + "cz-conventional-changelog": "^2.0.0", + "husky": "^0.14.3", + "last-release-git": "0.0.3", + "semantic-release": "^8.2.0", + "validate-commit-msg": "^2.14.0" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "release": { + "getLastRelease": "last-release-git" + }, + "repository": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server.git" + } +} diff --git a/release-docker.sh b/release-docker.sh new file mode 100644 index 0000000..35e89d9 --- /dev/null +++ b/release-docker.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . +docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" +docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} From 4384d494142e3f476481c103b3268b8572f4c7ff Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 22 Oct 2017 17:22:28 -0700 Subject: [PATCH 046/117] ci(travis): remove redundant parse-stubs step --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 02e1797..16dcb76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,6 @@ cache: install: - composer install --prefer-dist --no-interaction - - composer run-script parse-stubs script: - vendor/bin/phpcs -n From 16cf8f53e9d7106fedef98167d52b21c44cd0bf5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 22 Oct 2017 17:34:08 -0700 Subject: [PATCH 047/117] fix(docblocks): update to phpdocumentor/reflection-docblock ^4.0.0 closes #139 --- composer.json | 4 ++-- src/Definition.php | 2 +- src/DefinitionResolver.php | 32 ++++++++++++++++---------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index 225de3b..0504c00 100644 --- a/composer.json +++ b/composer.json @@ -23,11 +23,11 @@ "require": { "php": "^7.0", "composer/composer": "^1.3", - "felixfbecker/advanced-json-rpc": "^2.0", + "felixfbecker/advanced-json-rpc": "^3.0.0", "jetbrains/phpstorm-stubs": "dev-master", "microsoft/tolerant-php-parser": "^0.0.6", "netresearch/jsonmapper": "^1.0", - "phpdocumentor/reflection-docblock": "~3.1.1", + "phpdocumentor/reflection-docblock": "^4.0.0", "sabre/event": "^5.0", "sabre/uri": "^2.0", "squizlabs/php_codesniffer": "3.0.0RC3", diff --git a/src/Definition.php b/src/Definition.php index c03e852..9a485c4 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -78,7 +78,7 @@ class Definition * For functions and methods, this is the return type. * For any other declaration it will be null. * Can also be a compound type. - * If it is unknown, will be Types\Mixed. + * If it is unknown, will be Types\Mixed_. * * @var \phpDocumentor\Type|null */ diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 51c5955..dd1040e 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -551,7 +551,7 @@ class DefinitionResolver /** * Given an expression node, resolves that expression recursively to a type. - * If the type could not be resolved, returns Types\Mixed. + * If the type could not be resolved, returns Types\Mixed_. * * @param Node\Expression $expr * @return \phpDocumentor\Reflection\Type|null @@ -567,7 +567,7 @@ class DefinitionResolver if ($expr == null || $expr instanceof PhpParser\MissingToken || $expr instanceof PhpParser\SkippedToken) { // TODO some members are null or Missing/SkippedToken // How do we handle this more generally? - return new Types\Mixed; + return new Types\Mixed_; } // VARIABLE @@ -597,7 +597,7 @@ class DefinitionResolver // Find the function definition if ($expr->callableExpression instanceof Node\Expression) { // Cannot get type for dynamic function call - return new Types\Mixed; + return new Types\Mixed_; } if ($expr->callableExpression instanceof Node\QualifiedName) { @@ -646,7 +646,7 @@ class DefinitionResolver // MEMBER ACCESS EXPRESSION if ($expr instanceof Node\Expression\MemberAccessExpression) { if ($expr->memberName instanceof Node\Expression) { - return new Types\Mixed; + return new Types\Mixed_; } $var = $expr->dereferencableExpression; @@ -659,10 +659,10 @@ class DefinitionResolver if ($t instanceof Types\This) { $classFqn = self::getContainingClassFqn($expr); if ($classFqn === null) { - return new Types\Mixed; + return new Types\Mixed_; } } else if (!($t instanceof Types\Object_) || $t->getFqsen() === null) { - return new Types\Mixed; + return new Types\Mixed_; } else { $classFqn = substr((string)$t->getFqsen(), 1); } @@ -689,7 +689,7 @@ class DefinitionResolver if ($expr instanceof Node\Expression\ScopedPropertyAccessExpression) { $classType = $this->resolveClassNameToType($expr->scopeResolutionQualifier); if (!($classType instanceof Types\Object_) || $classType->getFqsen() === null) { - return new Types\Mixed; + return new Types\Mixed_; } $fqn = substr((string)$classType->getFqsen(), 1) . '::'; @@ -701,7 +701,7 @@ class DefinitionResolver $def = $this->index->getDefinition($fqn); if ($def === null) { - return new Types\Mixed; + return new Types\Mixed_; } return $def->type; } @@ -888,7 +888,7 @@ class DefinitionResolver if ($expr instanceof Node\Expression\SubscriptExpression) { $varType = $this->resolveExpressionNodeToType($expr->postfixExpression); if (!($varType instanceof Types\Array_)) { - return new Types\Mixed; + return new Types\Mixed_; } return $varType->getValueType(); } @@ -897,14 +897,14 @@ class DefinitionResolver // include, require, include_once, require_once if ($expr instanceof Node\Expression\ScriptInclusionExpression) { // TODO: resolve path to PhpDocument and find return statement - return new Types\Mixed; + return new Types\Mixed_; } if ($expr instanceof Node\QualifiedName) { return $this->resolveClassNameToType($expr); } - return new Types\Mixed; + return new Types\Mixed_; } @@ -918,7 +918,7 @@ class DefinitionResolver public function resolveClassNameToType($class): Type { if ($class instanceof Node\Expression) { - return new Types\Mixed; + return new Types\Mixed_; } if ($class instanceof PhpParser\Token && $class->kind === PhpParser\TokenKind::ClassKeyword) { // Anonymous class @@ -958,7 +958,7 @@ class DefinitionResolver * For classes and interfaces, this is the class type (object). * For variables / assignments, this is the documented type or type the assignment resolves to. * Can also be a compound type. - * If it is unknown, will be Types\Mixed. + * If it is unknown, will be Types\Mixed_. * Returns null if the node does not have a type. * * @param Node $node @@ -1012,7 +1012,7 @@ class DefinitionResolver } $type = $defaultType; } - return $type ?? new Types\Mixed; + return $type ?? new Types\Mixed_; } // FUNCTIONS AND METHODS @@ -1040,7 +1040,7 @@ class DefinitionResolver return new Types\Object_(new Fqsen('\\' . (string)$node->returnType->getResolvedName())); } // Unknown return type - return new Types\Mixed; + return new Types\Mixed_; } // PROPERTIES, CONSTS, CLASS CONSTS, ASSIGNMENT EXPRESSIONS @@ -1077,7 +1077,7 @@ class DefinitionResolver // TODO: read @property tags of class // TODO: Try to infer the type from default value / constant value // Unknown - return new Types\Mixed; + return new Types\Mixed_; } // The node does not have a type From 1db6b7bbb3af458feffe356427fae6f85f8a025c Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sun, 22 Oct 2017 22:54:38 -0700 Subject: [PATCH 048/117] chore: fixes for unused variables and phpdoc (#496) The identifier doesn't need to be generated for a notification to the client, since there's no response Add undeclared properties to TreeAnalyzer Fix other bugs in phpdoc --- src/ClientHandler.php | 1 - src/ComposerScripts.php | 3 ++- src/Server/TextDocument.php | 2 +- src/Server/Workspace.php | 1 + src/TreeAnalyzer.php | 11 +++++++++-- src/utils.php | 3 +-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/ClientHandler.php b/src/ClientHandler.php index 7b5a702..9fe921b 100644 --- a/src/ClientHandler.php +++ b/src/ClientHandler.php @@ -71,7 +71,6 @@ class ClientHandler */ public function notify(string $method, $params): Promise { - $id = $this->idGenerator->generate(); return $this->protocolWriter->write( new Protocol\Message( new AdvancedJsonRpc\Notification($method, (object)$params) diff --git a/src/ComposerScripts.php b/src/ComposerScripts.php index 487c39d..2a584d2 100644 --- a/src/ComposerScripts.php +++ b/src/ComposerScripts.php @@ -56,7 +56,8 @@ class ComposerScripts $parts['scheme'] = 'phpstubs'; $uri = Uri\build($parts); - $document = new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver); + // Create a new document and add it to $index + new PhpDocument($uri, $content, $index, $parser, $docBlockFactory, $definitionResolver); } $index->setComplete(); diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 5a2819e..2bcda2c 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -151,7 +151,7 @@ class TextDocument * The document's truth now exists where the document's uri points to (e.g. if the document's uri is a file uri the * truth now exists on disk). * - * @param \LanguageServer\Protocol\TextDocumentItem $textDocument The document that was closed + * @param \LanguageServer\Protocol\TextDocumentIdentifier $textDocument The document that was closed * @return void */ public function didClose(TextDocumentIdentifier $textDocument) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 61db068..fe1dda6 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -123,6 +123,7 @@ class Workspace */ public function xreferences($query, array $files = null): Promise { + // TODO: $files is unused in the coroutine return coroutine(function () use ($query, $files) { if ($this->composerLock === null) { return []; diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 0e43a8c..81c96e1 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -9,12 +9,19 @@ use phpDocumentor\Reflection\DocBlockFactory; use Sabre\Uri; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; +use Microsoft\PhpParser\Token; class TreeAnalyzer { /** @var PhpParser\Parser */ private $parser; + /** @var DocBlockFactory */ + private $docBlockFactory; + + /** @var DefinitionResolver */ + private $definitionResolver; + /** @var Node\SourceFileNode */ private $sourceFileNode; @@ -57,7 +64,7 @@ class TreeAnalyzer * and transforms them into LSP Format * * @param Node|Token $node - * @return Diagnostic + * @return void */ private function collectDiagnostics($node) { @@ -203,7 +210,7 @@ class TreeAnalyzer } /** - * @return Definition + * @return Definition[] */ public function getDefinitions() { diff --git a/src/utils.php b/src/utils.php index 636b41e..97e091d 100644 --- a/src/utils.php +++ b/src/utils.php @@ -148,9 +148,8 @@ function isVendored(PhpDocument $document, \stdClass $composerJson = null): bool * Check a given URI against the composer.json to see if it * is a vendored URI * - * @param \stdClass|null $composerJson * @param string $uri - * @param array $matches + * @param \stdClass|null $composerJson * @return string|null */ function getPackageName(string $uri, \stdClass $composerJson = null) From fbaa7b3cc536fce0e274743aaf2245a3fce3d5bc Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Sat, 28 Oct 2017 21:27:32 +0200 Subject: [PATCH 049/117] refactor: use ClassLike interface (#506) --- src/DefinitionResolver.php | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index dd1040e..f6d82c5 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -182,9 +182,7 @@ class DefinitionResolver // Interfaces, classes, traits, namespaces, functions, and global const elements $def->isGlobal = ( - $node instanceof Node\Statement\InterfaceDeclaration || - $node instanceof Node\Statement\ClassDeclaration || - $node instanceof Node\Statement\TraitDeclaration || + $node instanceof PhpParser\ClassLike || ($node instanceof Node\Statement\NamespaceDefinition && $node->name !== null) || @@ -1101,9 +1099,7 @@ class DefinitionResolver // interface C { } A\B\C // trait C { } A\B\C if ( - $node instanceof Node\Statement\ClassDeclaration || - $node instanceof Node\Statement\InterfaceDeclaration || - $node instanceof Node\Statement\TraitDeclaration + $node instanceof PhpParser\ClassLike ) { return (string) $node->getNamespacedName(); } @@ -1134,9 +1130,7 @@ class DefinitionResolver // Class method: use ClassName->methodName() as name $class = $node->getFirstAncestor( Node\Expression\ObjectCreationExpression::class, - Node\Statement\ClassDeclaration::class, - Node\Statement\InterfaceDeclaration::class, - Node\Statement\TraitDeclaration::class + PhpParser\ClassLike::class ); if (!isset($class->name)) { // Ignore anonymous classes @@ -1160,9 +1154,7 @@ class DefinitionResolver ($classDeclaration = $node->getFirstAncestor( Node\Expression\ObjectCreationExpression::class, - Node\Statement\ClassDeclaration::class, - Node\Statement\InterfaceDeclaration::class, - Node\Statement\TraitDeclaration::class + PhpParser\ClassLike::class ) ) !== null && isset($classDeclaration->name)) { $name = $node->getName(); @@ -1190,9 +1182,7 @@ class DefinitionResolver // Class constant: use ClassName::CONSTANT_NAME as name $classDeclaration = $constDeclaration->getFirstAncestor( Node\Expression\ObjectCreationExpression::class, - Node\Statement\ClassDeclaration::class, - Node\Statement\InterfaceDeclaration::class, - Node\Statement\TraitDeclaration::class + PhpParser\ClassLike::class ); if (!isset($classDeclaration->name)) { From 95f49d3a706806b9a8d74da1e4f757683ce66ff4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 13:18:41 -0700 Subject: [PATCH 050/117] ci: set BUILD_LEADER_ID see https://github.com/semantic-release/travis-deploy-once/issues/22 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 16dcb76..ac0aec5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,10 @@ php: services: - docker +env: + global: + - BUILD_LEADER_ID=1 + cache: directories: - $HOME/.composer/cache From b86d6c96c78f82b0340e2221e01c97450f29ba03 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 13:38:17 -0700 Subject: [PATCH 051/117] build: make release-docker.sh executable --- release-docker.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 release-docker.sh diff --git a/release-docker.sh b/release-docker.sh old mode 100644 new mode 100755 From 9e551a310b5e510989a1bd44777349e98fc132c4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 13:59:02 -0700 Subject: [PATCH 052/117] build: use PHP for release-docker script --- package.json | 2 +- release-docker.php | 10 ++++++++++ release-docker.sh | 6 ------ 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100755 release-docker.php delete mode 100755 release-docker.sh diff --git a/package.json b/package.json index d67062f..75f02e1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "commitmsg": "validate-commit-msg", - "semantic-release": "semantic-release pre && ./release-docker.sh && semantic-release post" + "semantic-release": "semantic-release pre && php release-docker.php && semantic-release post" }, "devDependencies": { "cz-conventional-changelog": "^2.0.0", diff --git a/release-docker.php b/release-docker.php new file mode 100755 index 0000000..d442fe5 --- /dev/null +++ b/release-docker.php @@ -0,0 +1,10 @@ +version; + +system("docker login -e=$dockerEmail -u=$dockerEmail -p=$dockerPassword"); +system("docker build -t felixfbecker/php-language-server:$version ."); +system("docker push felixfbecker/php-language-server:$version"); diff --git a/release-docker.sh b/release-docker.sh deleted file mode 100755 index 35e89d9..0000000 --- a/release-docker.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} . -docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" -docker push felixfbecker/php-language-server:${TRAVIS_TAG:1} From 99d8a361db205ee1179d541314b7e0c81d4d06f5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 28 Oct 2017 14:24:36 -0700 Subject: [PATCH 053/117] build: fix typo in release-docker script --- release-docker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-docker.php b/release-docker.php index d442fe5..8cac2db 100755 --- a/release-docker.php +++ b/release-docker.php @@ -5,6 +5,6 @@ $dockerUsername = getenv('DOCKER_USERNAME'); $dockerPassword = getenv('DOCKER_PASSWORD'); $version = json_decode(file_get_contents(__DIR__ . '/package.json'))->version; -system("docker login -e=$dockerEmail -u=$dockerEmail -p=$dockerPassword"); +system("docker login -e=$dockerEmail -u=$dockerUsername -p=$dockerPassword"); system("docker build -t felixfbecker/php-language-server:$version ."); system("docker push felixfbecker/php-language-server:$version"); From c74076d84f4e865eb71208445231e5ab5c3e2e8f Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 13:06:44 -0700 Subject: [PATCH 054/117] fix(cache): bump cache version (#508) the update of reflection-docblock means old caches are no longer valid. fixes #507 --- src/Indexer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Indexer.php b/src/Indexer.php index 9f8749d..2618d43 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -16,9 +16,9 @@ use function Sabre\Event\coroutine; class Indexer { /** - * @var The prefix for every cache item + * @var int The prefix for every cache item */ - const CACHE_VERSION = 1; + const CACHE_VERSION = 2; /** * @var FilesFinder From 7ae6452d1aaf44e96bb683ea7192a54576ad6cce Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 17:45:06 -0700 Subject: [PATCH 055/117] refactor(index): rename isGlobal to isMember (#511) isGlobal was confusing because a non-member can be considered global vs namespaced --- src/CompletionProvider.php | 6 +++--- src/Definition.php | 5 +++-- src/DefinitionResolver.php | 2 +- .../cases/WithReturnTypehints.php.expected.json | 10 +++++----- ...sClassMembersShouldNotBeSymbols.php.expected.json | 2 +- .../arrayValueShouldBeBoolean.php.expected.json | 4 ++-- .../cases/caseStatement1.php.expected.json | 2 +- .../cases/classDefinition1.php.expected.json | 6 +++--- .../cases/classProperty1.php.expected.json | 8 ++++---- tests/Validation/cases/constants.php.expected.json | 6 +++--- tests/Validation/cases/constants2.php.expected.json | 6 +++--- tests/Validation/cases/constants3.php.expected.json | 6 +++--- tests/Validation/cases/constants4.php.expected.json | 6 +++--- tests/Validation/cases/constants5.php.expected.json | 6 +++--- ...constantsInFunctionParamDefault.php.expected.json | 4 ++-- .../docBlocksOnNamespaceDefinition.php.expected.json | 2 +- tests/Validation/cases/exceptions1.php.expected.json | 2 +- .../Validation/cases/ifStatement1.php.expected.json | 2 +- .../cases/interfaceProperty.php.expected.json | 2 +- .../magicConstantsShouldBeGlobal.php.expected.json | 2 +- tests/Validation/cases/magicConsts.php.expected.json | 4 ++-- .../Validation/cases/memberAccess1.php.expected.json | 6 +++--- .../Validation/cases/memberAccess2.php.expected.json | 6 +++--- .../Validation/cases/memberAccess3.php.expected.json | 6 +++--- .../Validation/cases/memberAccess4.php.expected.json | 6 +++--- .../Validation/cases/memberAccess5.php.expected.json | 6 +++--- tests/Validation/cases/memberCall1.php.expected.json | 6 +++--- .../cases/methodReturnType.php.expected.json | 4 ++-- .../cases/multipleNamespaces.php.expected.json | 12 ++++++------ .../multiplePreceedingComments.php.expected.json | 4 ++-- tests/Validation/cases/nameToken.php.expected.json | 4 ++-- tests/Validation/cases/namespaces2.php.expected.json | 2 +- tests/Validation/cases/namespaces5.php.expected.json | 2 +- tests/Validation/cases/namespaces6.php.expected.json | 2 +- tests/Validation/cases/namespaces8.php.expected.json | 2 +- .../cases/objectCreation.php.expected.json | 6 +++--- .../cases/objectCreation2.php.expected.json | 8 ++++---- .../cases/objectCreation3.php.expected.json | 4 ++-- tests/Validation/cases/param1.php.expected.json | 4 ++-- tests/Validation/cases/parent1.php.expected.json | 10 +++++----- tests/Validation/cases/parent3.php.expected.json | 10 +++++----- .../Validation/cases/propertyName1.php.expected.json | 4 ++-- .../Validation/cases/propertyName2.php.expected.json | 4 ++-- tests/Validation/cases/returnType.php.expected.json | 4 ++-- .../cases/scopedPropertyAccess.php.expected.json | 6 +++--- .../cases/scopedPropertyAccess2.php.expected.json | 2 +- .../cases/scopedPropertyAccess3.php.expected.json | 4 ++-- .../cases/scopedPropertyAccess5.php.expected.json | 4 ++-- tests/Validation/cases/self1.php.expected.json | 10 +++++----- tests/Validation/cases/self2.php.expected.json | 10 +++++----- tests/Validation/cases/self3.php.expected.json | 10 +++++----- tests/Validation/cases/self4.php.expected.json | 6 +++--- tests/Validation/cases/self5.php.expected.json | 6 +++--- tests/Validation/cases/static1.php.expected.json | 10 +++++----- tests/Validation/cases/static2.php.expected.json | 10 +++++----- tests/Validation/cases/static3.php.expected.json | 10 +++++----- tests/Validation/cases/static4.php.expected.json | 6 +++--- .../cases/staticMethodReturnType.php.expected.json | 6 +++--- .../cases/stringVariable.php.expected.json | 6 +++--- ...QualifiedNameOutsideOfNamespace.php.expected.json | 2 +- .../verifyFqsenOnClassProperty.php.expected.json | 6 +++--- 61 files changed, 165 insertions(+), 164 deletions(-) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 2414ead..9a000e6 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -210,7 +210,7 @@ class CompletionProvider // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { - if (substr($fqn, 0, strlen($prefix)) === $prefix && !$def->isGlobal) { + if (substr($fqn, 0, strlen($prefix)) === $prefix && $def->isMember) { $list->items[] = CompletionItem::fromDefinition($def); } } @@ -243,7 +243,7 @@ class CompletionProvider // Collect all definitions that match any of the prefixes foreach ($this->index->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { - if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && !$def->isGlobal) { + if (substr(strtolower($fqn), 0, strlen($prefix)) === strtolower($prefix) && $def->isMember) { $list->items[] = CompletionItem::fromDefinition($def); } } @@ -316,7 +316,7 @@ class CompletionProvider if ( // Exclude methods, properties etc. - $def->isGlobal + !$def->isMember && ( !$prefix || ( diff --git a/src/Definition.php b/src/Definition.php index 9a485c4..c27f871 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -39,12 +39,13 @@ class Definition public $extends; /** - * Only true for classes, interfaces, traits, functions and non-class constants + * False for classes, interfaces, traits, functions and non-class constants + * True for methods, properties and class constants * This is so methods and properties are not suggested in the global scope * * @var bool */ - public $isGlobal; + public $isMember; /** * True if this definition is affected by global namespace fallback (global function or global constant) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index f6d82c5..93f961d 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -181,7 +181,7 @@ class DefinitionResolver ); // Interfaces, classes, traits, namespaces, functions, and global const elements - $def->isGlobal = ( + $def->isMember = !( $node instanceof PhpParser\ClassLike || ($node instanceof Node\Statement\NamespaceDefinition && $node->name !== null) || diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index 26b00e2..d11f271 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -20,7 +20,7 @@ "Fixtures\\Prophecy": { "fqn": "Fixtures\\Prophecy", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -41,7 +41,7 @@ "extends": [ "Fixtures\\Prophecy\\EmptyClass" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -60,7 +60,7 @@ "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -80,7 +80,7 @@ "Fixtures\\Prophecy\\WithReturnTypehints->getName()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getName()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -100,7 +100,7 @@ "Fixtures\\Prophecy\\WithReturnTypehints->getParent()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getParent()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json index b0625d7..51343f1 100644 --- a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json +++ b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json @@ -4,7 +4,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json index d58986d..107877e 100644 --- a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json +++ b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json @@ -4,7 +4,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "A->foo": { "fqn": "A->foo", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/caseStatement1.php.expected.json b/tests/Validation/cases/caseStatement1.php.expected.json index 573dd17..9746749 100644 --- a/tests/Validation/cases/caseStatement1.php.expected.json +++ b/tests/Validation/cases/caseStatement1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/classDefinition1.php.expected.json b/tests/Validation/cases/classDefinition1.php.expected.json index 6c418b7..670b5de 100644 --- a/tests/Validation/cases/classDefinition1.php.expected.json +++ b/tests/Validation/cases/classDefinition1.php.expected.json @@ -11,7 +11,7 @@ "TestNamespace": { "fqn": "TestNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "TestNamespace\\A": { "fqn": "TestNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "TestNamespace\\A->a": { "fqn": "TestNamespace\\A->a", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/classProperty1.php.expected.json b/tests/Validation/cases/classProperty1.php.expected.json index e00107e..921bf0b 100644 --- a/tests/Validation/cases/classProperty1.php.expected.json +++ b/tests/Validation/cases/classProperty1.php.expected.json @@ -11,7 +11,7 @@ "TestNamespace": { "fqn": "TestNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "TestNamespace\\TestClass": { "fqn": "TestNamespace\\TestClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "TestNamespace\\TestClass->testProperty": { "fqn": "TestNamespace\\TestClass->testProperty", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -69,7 +69,7 @@ "TestNamespace\\TestClass->testMethod()": { "fqn": "TestNamespace\\TestClass->testMethod()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants.php.expected.json b/tests/Validation/cases/constants.php.expected.json index d629870..a76c059 100644 --- a/tests/Validation/cases/constants.php.expected.json +++ b/tests/Validation/cases/constants.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants2.php.expected.json b/tests/Validation/cases/constants2.php.expected.json index 66c678f..ae5a2ce 100644 --- a/tests/Validation/cases/constants2.php.expected.json +++ b/tests/Validation/cases/constants2.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants3.php.expected.json b/tests/Validation/cases/constants3.php.expected.json index 03f00ba..c6ad922 100644 --- a/tests/Validation/cases/constants3.php.expected.json +++ b/tests/Validation/cases/constants3.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants4.php.expected.json b/tests/Validation/cases/constants4.php.expected.json index 62ce430..bc46cf1 100644 --- a/tests/Validation/cases/constants4.php.expected.json +++ b/tests/Validation/cases/constants4.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A->suite()": { "fqn": "MyNamespace\\A->suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constants5.php.expected.json b/tests/Validation/cases/constants5.php.expected.json index bb441c8..6bd7b8e 100644 --- a/tests/Validation/cases/constants5.php.expected.json +++ b/tests/Validation/cases/constants5.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\Mbstring": { "fqn": "MyNamespace\\Mbstring", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\Mbstring::MB_CASE_FOLD": { "fqn": "MyNamespace\\Mbstring::MB_CASE_FOLD", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json index 8f9a212..b49f5a9 100644 --- a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json +++ b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json @@ -8,7 +8,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "A->b()": { "fqn": "A->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json index 73f6bee..dd1737a 100644 --- a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json +++ b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json @@ -4,7 +4,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/exceptions1.php.expected.json b/tests/Validation/cases/exceptions1.php.expected.json index a4a71d1..c1ee1bd 100644 --- a/tests/Validation/cases/exceptions1.php.expected.json +++ b/tests/Validation/cases/exceptions1.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/ifStatement1.php.expected.json b/tests/Validation/cases/ifStatement1.php.expected.json index 18efe9f..98e6572 100644 --- a/tests/Validation/cases/ifStatement1.php.expected.json +++ b/tests/Validation/cases/ifStatement1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/interfaceProperty.php.expected.json b/tests/Validation/cases/interfaceProperty.php.expected.json index 178834d..10c49f0 100644 --- a/tests/Validation/cases/interfaceProperty.php.expected.json +++ b/tests/Validation/cases/interfaceProperty.php.expected.json @@ -4,7 +4,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json index f62d214..a9c2162 100644 --- a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json +++ b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json @@ -11,7 +11,7 @@ "B": { "fqn": "B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json index a37791b..6f863b9 100644 --- a/tests/Validation/cases/magicConsts.php.expected.json +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -8,7 +8,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "A::$deprecationsTriggered": { "fqn": "A::$deprecationsTriggered", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess1.php.expected.json b/tests/Validation/cases/memberAccess1.php.expected.json index 7f9630a..c039e5e 100644 --- a/tests/Validation/cases/memberAccess1.php.expected.json +++ b/tests/Validation/cases/memberAccess1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess2.php.expected.json b/tests/Validation/cases/memberAccess2.php.expected.json index 7b3ce1d..50902a1 100644 --- a/tests/Validation/cases/memberAccess2.php.expected.json +++ b/tests/Validation/cases/memberAccess2.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess3.php.expected.json b/tests/Validation/cases/memberAccess3.php.expected.json index 520dae8..d91b27d 100644 --- a/tests/Validation/cases/memberAccess3.php.expected.json +++ b/tests/Validation/cases/memberAccess3.php.expected.json @@ -26,7 +26,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -45,7 +45,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -64,7 +64,7 @@ "MyNamespace\\A::getInitializer()": { "fqn": "MyNamespace\\A::getInitializer()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess4.php.expected.json b/tests/Validation/cases/memberAccess4.php.expected.json index 1d51b85..0b7e1bf 100644 --- a/tests/Validation/cases/memberAccess4.php.expected.json +++ b/tests/Validation/cases/memberAccess4.php.expected.json @@ -17,7 +17,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -36,7 +36,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -55,7 +55,7 @@ "MyNamespace\\A->testRequest()": { "fqn": "MyNamespace\\A->testRequest()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberAccess5.php.expected.json b/tests/Validation/cases/memberAccess5.php.expected.json index 57aca0a..050cf3b 100644 --- a/tests/Validation/cases/memberAccess5.php.expected.json +++ b/tests/Validation/cases/memberAccess5.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\ParseErrorsTest": { "fqn": "MyNamespace\\ParseErrorsTest", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\ParseErrorsTest->setUp()": { "fqn": "MyNamespace\\ParseErrorsTest->setUp()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/memberCall1.php.expected.json b/tests/Validation/cases/memberCall1.php.expected.json index 416a705..d02d7e3 100644 --- a/tests/Validation/cases/memberCall1.php.expected.json +++ b/tests/Validation/cases/memberCall1.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\ParseErrorsTest": { "fqn": "MyNamespace\\ParseErrorsTest", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\ParseErrorsTest->setAccount()": { "fqn": "MyNamespace\\ParseErrorsTest->setAccount()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/methodReturnType.php.expected.json b/tests/Validation/cases/methodReturnType.php.expected.json index 54d79d6..2c89994 100644 --- a/tests/Validation/cases/methodReturnType.php.expected.json +++ b/tests/Validation/cases/methodReturnType.php.expected.json @@ -8,7 +8,7 @@ "FooClass": { "fqn": "FooClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "FooClass->foo()": { "fqn": "FooClass->foo()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/multipleNamespaces.php.expected.json b/tests/Validation/cases/multipleNamespaces.php.expected.json index 30fe596..fa51f2d 100644 --- a/tests/Validation/cases/multipleNamespaces.php.expected.json +++ b/tests/Validation/cases/multipleNamespaces.php.expected.json @@ -17,7 +17,7 @@ "MyNamespace1": { "fqn": "MyNamespace1", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -36,7 +36,7 @@ "MyNamespace1\\B": { "fqn": "MyNamespace1\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -55,7 +55,7 @@ "MyNamespace1\\B->b()": { "fqn": "MyNamespace1\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -75,7 +75,7 @@ "MyNamespace2": { "fqn": "MyNamespace2", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -96,7 +96,7 @@ "extends": [ "MyNamespace2\\MyNamespace1\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -115,7 +115,7 @@ "MyNamespace2\\A->a()": { "fqn": "MyNamespace2\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/multiplePreceedingComments.php.expected.json b/tests/Validation/cases/multiplePreceedingComments.php.expected.json index 5ca3dd7..96cbb4a 100644 --- a/tests/Validation/cases/multiplePreceedingComments.php.expected.json +++ b/tests/Validation/cases/multiplePreceedingComments.php.expected.json @@ -4,7 +4,7 @@ "Foo": { "fqn": "Foo", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "Foo->fn()": { "fqn": "Foo->fn()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/nameToken.php.expected.json b/tests/Validation/cases/nameToken.php.expected.json index 0ecd53f..c06217b 100644 --- a/tests/Validation/cases/nameToken.php.expected.json +++ b/tests/Validation/cases/nameToken.php.expected.json @@ -4,7 +4,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "A->b()": { "fqn": "A->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces2.php.expected.json b/tests/Validation/cases/namespaces2.php.expected.json index 42243e5..0dffc9f 100644 --- a/tests/Validation/cases/namespaces2.php.expected.json +++ b/tests/Validation/cases/namespaces2.php.expected.json @@ -17,7 +17,7 @@ "MyNamespace1": { "fqn": "MyNamespace1", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces5.php.expected.json b/tests/Validation/cases/namespaces5.php.expected.json index 5ffe02d..e609ca2 100644 --- a/tests/Validation/cases/namespaces5.php.expected.json +++ b/tests/Validation/cases/namespaces5.php.expected.json @@ -26,7 +26,7 @@ "B": { "fqn": "B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces6.php.expected.json b/tests/Validation/cases/namespaces6.php.expected.json index 1657f28..bf5a045 100644 --- a/tests/Validation/cases/namespaces6.php.expected.json +++ b/tests/Validation/cases/namespaces6.php.expected.json @@ -4,7 +4,7 @@ "A\\B": { "fqn": "A\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/namespaces8.php.expected.json b/tests/Validation/cases/namespaces8.php.expected.json index 71017d9..d303647 100644 --- a/tests/Validation/cases/namespaces8.php.expected.json +++ b/tests/Validation/cases/namespaces8.php.expected.json @@ -14,7 +14,7 @@ "LanguageServer\\Tests\\Utils": { "fqn": "LanguageServer\\Tests\\Utils", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/objectCreation.php.expected.json b/tests/Validation/cases/objectCreation.php.expected.json index a8fce0f..8fec38f 100644 --- a/tests/Validation/cases/objectCreation.php.expected.json +++ b/tests/Validation/cases/objectCreation.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/objectCreation2.php.expected.json b/tests/Validation/cases/objectCreation2.php.expected.json index 2ec8314..e546528 100644 --- a/tests/Validation/cases/objectCreation2.php.expected.json +++ b/tests/Validation/cases/objectCreation2.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -68,7 +68,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/objectCreation3.php.expected.json b/tests/Validation/cases/objectCreation3.php.expected.json index f0cc31a..d2d3e2f 100644 --- a/tests/Validation/cases/objectCreation3.php.expected.json +++ b/tests/Validation/cases/objectCreation3.php.expected.json @@ -8,7 +8,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "A->a()": { "fqn": "A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/param1.php.expected.json b/tests/Validation/cases/param1.php.expected.json index 33c99db..40cfbd1 100644 --- a/tests/Validation/cases/param1.php.expected.json +++ b/tests/Validation/cases/param1.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\init()": { "fqn": "MyNamespace\\init()", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/parent1.php.expected.json b/tests/Validation/cases/parent1.php.expected.json index eab232e..8b29461 100644 --- a/tests/Validation/cases/parent1.php.expected.json +++ b/tests/Validation/cases/parent1.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -71,7 +71,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -90,7 +90,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json index aedb4b2..1e51b84 100644 --- a/tests/Validation/cases/parent3.php.expected.json +++ b/tests/Validation/cases/parent3.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/propertyName1.php.expected.json b/tests/Validation/cases/propertyName1.php.expected.json index 42b2ee9..cc31ed6 100644 --- a/tests/Validation/cases/propertyName1.php.expected.json +++ b/tests/Validation/cases/propertyName1.php.expected.json @@ -4,7 +4,7 @@ "MyClass": { "fqn": "MyClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "MyClass->mainPropertyName": { "fqn": "MyClass->mainPropertyName", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/propertyName2.php.expected.json b/tests/Validation/cases/propertyName2.php.expected.json index 5a5c914..d4efb42 100644 --- a/tests/Validation/cases/propertyName2.php.expected.json +++ b/tests/Validation/cases/propertyName2.php.expected.json @@ -4,7 +4,7 @@ "MyClass": { "fqn": "MyClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -23,7 +23,7 @@ "MyClass->mainPropertyName": { "fqn": "MyClass->mainPropertyName", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/returnType.php.expected.json b/tests/Validation/cases/returnType.php.expected.json index cf9cc63..20cdadf 100644 --- a/tests/Validation/cases/returnType.php.expected.json +++ b/tests/Validation/cases/returnType.php.expected.json @@ -11,7 +11,7 @@ "TestNamespace": { "fqn": "TestNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "TestNamespace\\whatever()": { "fqn": "TestNamespace\\whatever()", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess.php.expected.json b/tests/Validation/cases/scopedPropertyAccess.php.expected.json index 52b6e7a..ec50c7a 100644 --- a/tests/Validation/cases/scopedPropertyAccess.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess.php.expected.json @@ -11,7 +11,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -30,7 +30,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -49,7 +49,7 @@ "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json index e5f6850..e1712cd 100644 --- a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json index aa508bc..913721b 100644 --- a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json @@ -11,7 +11,7 @@ "A": { "fqn": "A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -30,7 +30,7 @@ "A::$a": { "fqn": "A::$a", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json index bd4ee70..7e56123 100644 --- a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json @@ -17,7 +17,7 @@ "TestClass": { "fqn": "TestClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -36,7 +36,7 @@ "TestClass::$testProperty": { "fqn": "TestClass::$testProperty", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self1.php.expected.json b/tests/Validation/cases/self1.php.expected.json index eb37fc7..2ce8e43 100644 --- a/tests/Validation/cases/self1.php.expected.json +++ b/tests/Validation/cases/self1.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self2.php.expected.json b/tests/Validation/cases/self2.php.expected.json index 2280b1a..cc00e3a 100644 --- a/tests/Validation/cases/self2.php.expected.json +++ b/tests/Validation/cases/self2.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json index 3f69299..9f25ba1 100644 --- a/tests/Validation/cases/self3.php.expected.json +++ b/tests/Validation/cases/self3.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index ec3ae07..7f7b25e 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -23,7 +23,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -42,7 +42,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -61,7 +61,7 @@ "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, diff --git a/tests/Validation/cases/self5.php.expected.json b/tests/Validation/cases/self5.php.expected.json index 6727689..eb41832 100644 --- a/tests/Validation/cases/self5.php.expected.json +++ b/tests/Validation/cases/self5.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -27,7 +27,7 @@ "MyNamespace\\A": { "fqn": "MyNamespace\\A", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -46,7 +46,7 @@ "MyNamespace\\A->typesProvider()": { "fqn": "MyNamespace\\A->typesProvider()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static1.php.expected.json b/tests/Validation/cases/static1.php.expected.json index 69f8de0..453edc8 100644 --- a/tests/Validation/cases/static1.php.expected.json +++ b/tests/Validation/cases/static1.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static2.php.expected.json b/tests/Validation/cases/static2.php.expected.json index 17f9a66..87e8e9a 100644 --- a/tests/Validation/cases/static2.php.expected.json +++ b/tests/Validation/cases/static2.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json index f6e5189..37524ea 100644 --- a/tests/Validation/cases/static3.php.expected.json +++ b/tests/Validation/cases/static3.php.expected.json @@ -14,7 +14,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -33,7 +33,7 @@ "MyNamespace\\B": { "fqn": "MyNamespace\\B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -52,7 +52,7 @@ "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -74,7 +74,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -93,7 +93,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index 3d05ede..4bcdd9d 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -8,7 +8,7 @@ "MyNamespace": { "fqn": "MyNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -29,7 +29,7 @@ "extends": [ "MyNamespace\\B" ], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -48,7 +48,7 @@ "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/staticMethodReturnType.php.expected.json b/tests/Validation/cases/staticMethodReturnType.php.expected.json index c178f0a..e6661ec 100644 --- a/tests/Validation/cases/staticMethodReturnType.php.expected.json +++ b/tests/Validation/cases/staticMethodReturnType.php.expected.json @@ -8,7 +8,7 @@ "FooClass": { "fqn": "FooClass", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "FooClass::staticFoo()": { "fqn": "FooClass::staticFoo()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": true, "canBeInstantiated": false, @@ -47,7 +47,7 @@ "FooClass->bar()": { "fqn": "FooClass->bar()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json index fed8a23..1d4c7e3 100644 --- a/tests/Validation/cases/stringVariable.php.expected.json +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -8,7 +8,7 @@ "B": { "fqn": "B", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -27,7 +27,7 @@ "B->hi": { "fqn": "B->hi", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -47,7 +47,7 @@ "B->a()": { "fqn": "B->a()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json index f686093..509907c 100644 --- a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json +++ b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json @@ -8,7 +8,7 @@ "SomeNamespace": { "fqn": "SomeNamespace", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": false, diff --git a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json index f6851bf..662a7ed 100644 --- a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json +++ b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json @@ -11,7 +11,7 @@ "Foo": { "fqn": "Foo", "extends": [], - "isGlobal": true, + "isMember": false, "roamed": false, "isStatic": false, "canBeInstantiated": true, @@ -30,7 +30,7 @@ "Foo->bar": { "fqn": "Foo->bar", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, @@ -50,7 +50,7 @@ "Foo->foo()": { "fqn": "Foo->foo()", "extends": [], - "isGlobal": false, + "isMember": true, "roamed": false, "isStatic": false, "canBeInstantiated": false, From 744062c14e3d104a3159cb969881baf9bb4dfc9e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 17:53:37 -0700 Subject: [PATCH 056/117] ci: add AppVeyor to test Windows closes #40 --- README.md | 3 +- appveyor.yml | 51 +++++++++++++++++++++++++++++ tests/Validation/ValidationTest.php | 5 ++- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 appveyor.yml diff --git a/README.md b/README.md index 0aad4e5..017ba72 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # PHP Language Server [![Version](https://img.shields.io/packagist/v/felixfbecker/language-server.svg)](https://packagist.org/packages/felixfbecker/language-server) -[![Build Status](https://travis-ci.org/felixfbecker/php-language-server.svg?branch=master)](https://travis-ci.org/felixfbecker/php-language-server) +[![Linux Build Status](https://travis-ci.org/felixfbecker/php-language-server.svg?branch=master)](https://travis-ci.org/felixfbecker/php-language-server) +[![Windows Build status](https://ci.appveyor.com/api/projects/status/2sp5ll052wdjqmdm/branch/master?svg=true)](https://ci.appveyor.com/project/felixfbecker/php-language-server/branch/master) [![Coverage](https://codecov.io/gh/felixfbecker/php-language-server/branch/master/graph/badge.svg)](https://codecov.io/gh/felixfbecker/php-language-server) [![Dependency Status](https://gemnasium.com/badges/github.com/felixfbecker/php-language-server.svg)](https://gemnasium.com/github.com/felixfbecker/php-language-server) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..13dfd74 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,51 @@ +version: '{build}' + +platform: + - x64 + +skip_tags: true +skip_branch_with_pr: true +clone_depth: 1 +max_jobs: 3 + +cache: + - '%LOCALAPPDATA%\Composer' + +environment: + ANSICON: 121x90 (121x90) + matrix: + - { PHP_VERSION: '7.1.11', VC_VERSION: '14', XDEBUG_VERSION: '2.5.5' } + +install: + # Enable Windows Update service, needed to install vcredist2015 (dependency of php) + - ps: Set-Service wuauserv -StartupType Manual + - choco install -y php --version %PHP_VERSION% + - choco install -y composer + - refreshenv + - composer install --no-interaction --no-progress --prefer-dist + # Install XDebug for code coverage + - ps: | + $client = New-Object System.Net.WebClient + $phpMinorVersion = $env:PHP_VERSION -replace '\.\d+$' + $xdebugUrl = "https://xdebug.org/files/php_xdebug-$env:XDEBUG_VERSION-$phpMinorVersion-vc14-nts-x86_64.dll" + $phpDir = (Get-Item (Get-Command php).Source).Directory.FullName + $xdebugPath = Join-Path $phpDir ext\xdebug.dll + $client.DownloadFile($xdebugUrl, $xdebugPath) + $phpIniPath = Join-Path $phpDir php.ini + Add-Content $phpIniPath @" + zend_extension=$xdebugPath + "@ + +build: off + +test_script: + - vendor\bin\phpunit --coverage-clover=coverage/coverage.xml + +after_test: + - ps: | + # Delete vendor because it causes problems with codecovs report search + # https://github.com/codecov/codecov-bash/issues/96 + Remove-Item -Recurse -Force vendor + $env:PATH = 'C:\msys64\usr\bin;' + $env:PATH + Invoke-WebRequest -Uri 'https://codecov.io/bash' -OutFile codecov.sh + bash codecov.sh -f 'coverage/coverage.xml' diff --git a/tests/Validation/ValidationTest.php b/tests/Validation/ValidationTest.php index 3fc7429..9057c9d 100644 --- a/tests/Validation/ValidationTest.php +++ b/tests/Validation/ValidationTest.php @@ -96,7 +96,9 @@ class ValidationTest extends TestCase $testCasesDir = realpath(__DIR__ . '/cases'); foreach ($refsAndDefs['references'] as $key => $list) { $fixedPathRefs = array_map(function ($ref) use ($testCasesDir) { - return str_replace($testCasesDir, '.', $ref); + $ref = str_replace($testCasesDir, '.', $ref); + $ref = str_replace(DIRECTORY_SEPARATOR, '/', $ref); + return $ref; }, $list); $refsAndDefs['references']->$key = $fixedPathRefs; @@ -107,6 +109,7 @@ class ValidationTest extends TestCase 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); + $def->symbolInformation->location->uri = str_replace(DIRECTORY_SEPARATOR, '/', $def->symbolInformation->location->uri); } } From 1edbe35609c50dcd7d51219b07622f6e527b752b Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Mon, 30 Oct 2017 11:33:19 +0100 Subject: [PATCH 057/117] refactor: use FunctionLike Interface (#505) --- src/CompletionProvider.php | 4 ++-- src/DefinitionResolver.php | 4 ++-- src/ParserHelpers.php | 10 +--------- src/TreeAnalyzer.php | 4 ++-- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 9a000e6..68c5a0c 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -415,7 +415,7 @@ class CompletionProvider // Walk the AST upwards until a scope boundary is met $level = $node; - while ($level && !ParserHelpers\isFunctionLike($level)) { + while ($level && !($level instanceof PhpParser\FunctionLike)) { // Walk siblings before the node $sibling = $level; while ($sibling = $sibling->getPreviousSibling()) { @@ -429,7 +429,7 @@ class CompletionProvider // If the traversal ended because a function was met, // also add its parameters and closure uses to the result list - if ($level && ParserHelpers\isFunctionLike($level) && $level->parameters !== null) { + if ($level && $level instanceof PhpParser\FunctionLike && $level->parameters !== null) { foreach ($level->parameters->getValues() as $param) { $paramName = $param->getName(); if (empty($namePrefix) || strpos($paramName, $namePrefix) !== false) { diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 93f961d..620527e 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -509,7 +509,7 @@ class DefinitionResolver // Traverse the AST up do { // If a function is met, check the parameters and use statements - if (ParserHelpers\isFunctionLike($n)) { + if ($n instanceof PhpParser\FunctionLike) { if ($n->parameters !== null) { foreach ($n->parameters->getElements() as $param) { if ($param->getName() === $name) { @@ -1018,7 +1018,7 @@ class DefinitionResolver // 1. doc block // 2. return type hint // 3. TODO: infer from return statements - if (ParserHelpers\isFunctionLike($node)) { + if ($node instanceof PhpParser\FunctionLike) { // Functions/methods $docBlock = $this->getDocBlock($node); if ( diff --git a/src/ParserHelpers.php b/src/ParserHelpers.php index 0ea210a..f20fb1f 100644 --- a/src/ParserHelpers.php +++ b/src/ParserHelpers.php @@ -25,7 +25,7 @@ function isConstantFetch(Node $node) : bool $parent instanceof Node\Expression\CallExpression || $parent instanceof Node\Expression\ObjectCreationExpression || $parent instanceof Node\Expression\ScopedPropertyAccessExpression || - isFunctionLike($parent) || + $parent instanceof PhpParser\FunctionLike || ( $parent instanceof Node\Expression\BinaryExpression && $parent->operator->kind === PhpParser\TokenKind::InstanceOfKeyword @@ -38,14 +38,6 @@ 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)) { diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 81c96e1..58de8db 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -72,10 +72,10 @@ class TreeAnalyzer $range = PhpParser\PositionUtilities::getRangeFromPosition($error->start, $error->length, $this->sourceFileNode->fileContents); switch ($error->kind) { - case \Microsoft\PhpParser\DiagnosticKind::Error: + case PhpParser\DiagnosticKind::Error: $severity = DiagnosticSeverity::ERROR; break; - case \Microsoft\PhpParser\DiagnosticKind::Warning: + case PhpParser\DiagnosticKind::Warning: default: $severity = DiagnosticSeverity::WARNING; break; From d3c9133892022a7e8aa2711d487546cbde5ab253 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Mon, 30 Oct 2017 21:12:44 -0700 Subject: [PATCH 058/117] ci(appveyor): cache chocolatey downloads --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 13dfd74..fa6d26a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ max_jobs: 3 cache: - '%LOCALAPPDATA%\Composer' + - '%LOCALAPPDATA%\Temp\Chocolatey' environment: ANSICON: 121x90 (121x90) @@ -19,6 +20,7 @@ environment: install: # Enable Windows Update service, needed to install vcredist2015 (dependency of php) - ps: Set-Service wuauserv -StartupType Manual + - choco config set cacheLocation %LOCALAPPDATA%\Temp\Chocolatey - choco install -y php --version %PHP_VERSION% - choco install -y composer - refreshenv From ac6bce929fa1a4265d92628b13153c0361bf67ef Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Mon, 30 Oct 2017 22:51:23 -0700 Subject: [PATCH 059/117] chore: get patch versions of tolerant-php-parser --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0504c00..ceb357d 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "composer/composer": "^1.3", "felixfbecker/advanced-json-rpc": "^3.0.0", "jetbrains/phpstorm-stubs": "dev-master", - "microsoft/tolerant-php-parser": "^0.0.6", + "microsoft/tolerant-php-parser": "0.0.*", "netresearch/jsonmapper": "^1.0", "phpdocumentor/reflection-docblock": "^4.0.0", "sabre/event": "^5.0", From 6dbeef63bc12c3e0b5b1f8f384c1ac82c3a6e17e Mon Sep 17 00:00:00 2001 From: Nate Eagleson Date: Wed, 1 Nov 2017 12:38:54 -0400 Subject: [PATCH 060/117] docs: correct parse-stubs section in readme (#502) As the parse-stubs step is done automatically by `composer install` since https://github.com/felixfbecker/php-language-server/commit/34d3d2030dabc29bd919d0ea795bcb8d994228de, we no longer need to explicitly instruct people to do it. Note that sometimes you must parse the PHP stubs manually --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 017ba72..90c66cb 100644 --- a/README.md +++ b/README.md @@ -192,9 +192,6 @@ Clone the repository and run composer install to install dependencies. -Then parse the stubs with - - composer run-script parse-stubs Run the tests with @@ -203,3 +200,7 @@ Run the tests with Lint with composer lint + +The project parses PHPStorm's PHP stubs to get support for PHP builtins. It re-parses them as needed after Composer processes, but after some code changes (such as ones involving the index or parsing) you may have to explicitly re-parse them: + + composer run-script parse-stubs From e9fc97d430e691485d7ff995cb05b9659aead362 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 1 Nov 2017 23:39:38 -0700 Subject: [PATCH 061/117] chore: extend export-ignore file list --- .gitattributes | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7600d2f..688e780 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,18 +2,22 @@ /.vscode export-ignore /fixtures export-ignore -/tests export-ignore /images export-ignore +/tests export-ignore /validation export-ignore +/.dockerignore export-ignore +/.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore /.gitmodules export-ignore -/.dockerignore export-ignore +/.npmrc export-ignore /.travis.yml export-ignore /codecov.yml export-ignore +/dependencies.yml export-ignore /Dockerfile export-ignore +/package.json export-ignore /Performance.php export-ignore /php.ini export-ignore /phpcs.xml.dist export-ignore /phpunit.xml.dist export-ignore -/README.md export-ignore +/release-docker.php export-ignore From f00fd1b62c7f2928b92a08a9c258767480c7344b Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 4 Nov 2017 23:57:51 -0700 Subject: [PATCH 062/117] fix(formatting): drop PHP CodeSniffer (#504) At this point there are countless issues about the formatting done by CodeSniffer. It plain out doesn't work in many cases, overrides format options that are contributed by other extensions in VS Code and does not reuse any of our AST parsing. For that reason, I am starting to think there is no reason to keep it in here until we have proper pretty-printing support from https://github.com/Microsoft/tolerant-php-parser that actually reuses our ASTs and can work while editing. For people who want to use CodeSniffer to format their code, there could be a standalone CodeSniffer language server (like there is a TSLint language server and ESLint language server). As said, we don't reuse our state anyway. BREAKING CHANGE: removes formatting support closes #501 closes #474 closes #473 closes #468 closes #450 closes #445 closes #443 closes #423 closes #343 closes #296 closes #293 closes #499 closes #471 --- README.md | 3 - composer.json | 4 +- fixtures/format.php | 20 ---- fixtures/format_expected.php | 19 ---- images/formatDocument.gif | Bin 52182 -> 0 bytes src/Formatter.php | 107 ------------------- src/LanguageServer.php | 2 - src/PhpDocument.php | 13 --- src/Server/TextDocument.php | 14 --- tests/FormatterTest.php | 28 ----- tests/LanguageServerTest.php | 1 - tests/Server/TextDocument/FormattingTest.php | 50 --------- 12 files changed, 2 insertions(+), 259 deletions(-) delete mode 100644 fixtures/format.php delete mode 100644 fixtures/format_expected.php delete mode 100644 images/formatDocument.gif delete mode 100644 src/Formatter.php delete mode 100644 tests/FormatterTest.php delete mode 100644 tests/Server/TextDocument/FormattingTest.php diff --git a/README.md b/README.md index 90c66cb..8e7117c 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,6 @@ For Parameters, it will return the `@param` tag. The query is matched case-insensitively against the fully qualified name of the symbol. Non-Standard: An empty query will return _all_ symbols found in the workspace. -### [Document Formatting](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#document-formatting-request) -![Document Formatting demo](images/formatDocument.gif) - ### Error reporting through [Publish Diagnostics](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#publishdiagnostics-notification) ![Error reporting demo](images/publishDiagnostics.png) diff --git a/composer.json b/composer.json index ceb357d..a21535b 100644 --- a/composer.json +++ b/composer.json @@ -30,12 +30,12 @@ "phpdocumentor/reflection-docblock": "^4.0.0", "sabre/event": "^5.0", "sabre/uri": "^2.0", - "squizlabs/php_codesniffer": "3.0.0RC3", "webmozart/glob": "^4.1", "webmozart/path-util": "^2.3" }, "require-dev": { - "phpunit/phpunit": "^6.3" + "phpunit/phpunit": "^6.3", + "squizlabs/php_codesniffer": "^3.1" }, "autoload": { "psr-4": { diff --git a/fixtures/format.php b/fixtures/format.php deleted file mode 100644 index bd35640..0000000 --- a/fixtures/format.php +++ /dev/null @@ -1,20 +0,0 @@ -@&W9+-{7-kJA9ZeZoTYkbbf)fD1 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 cduZ?EjCkxGstandards = self::findConfiguration($path); - - // Autoload class to set up a bunch of PHP_CodeSniffer-specific token type constants - spl_autoload_call(Tokens::class); - - $file = new DummyFile($content, new Ruleset($config), $config); - $file->process(); - $fixed = $file->fixer->fixFile(); - if (!$fixed && $file->getErrorCount() > 0) { - throw new Exception('Unable to format file'); - } - - $new = $file->fixer->getContents(); - if ($content === $new) { - return []; - } - return [new TextEdit(new Range(new Position(0, 0), self::calculateEndPosition($content)), $new)]; - } - - /** - * Calculate position of last character. - * - * @param string $content document as string - * - * @return \LanguageServer\Protocol\Position - */ - private static function calculateEndPosition(string $content): Position - { - $lines = explode("\n", $content); - return new Position(count($lines) - 1, strlen(end($lines))); - } - - /** - * Search for PHP_CodeSniffer configuration file at given directory or its parents. - * If no configuration found then PSR2 standard is loaded by default. - * - * @param string $path path to file or directory - * @return string[] - */ - private static function findConfiguration(string $path) - { - if (is_dir($path)) { - $currentDir = $path; - } else { - $currentDir = dirname($path); - } - do { - $default = $currentDir . DIRECTORY_SEPARATOR . 'phpcs.xml'; - if (is_file($default)) { - return [$default]; - } - - $default = $currentDir . DIRECTORY_SEPARATOR . 'phpcs.xml.dist'; - if (is_file($default)) { - return [$default]; - } - - $lastDir = $currentDir; - $currentDir = dirname($currentDir); - } while ($currentDir !== '.' && $currentDir !== $lastDir); - - $standard = Config::getConfigData('default_standard') ?? 'PSR2'; - return explode(',', $standard); - } -} diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 118dd93..73560f4 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -265,8 +265,6 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $serverCapabilities->documentSymbolProvider = true; // Support "Find all symbols in workspace" $serverCapabilities->workspaceSymbolProvider = true; - // Support "Format Code" - $serverCapabilities->documentFormattingProvider = true; // Support "Go to definition" $serverCapabilities->definitionProvider = true; // Support "Find all references" diff --git a/src/PhpDocument.php b/src/PhpDocument.php index ff3cf8b..0ac8421 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -166,19 +166,6 @@ class PhpDocument $this->sourceFileNode = $treeAnalyzer->getSourceFileNode(); } - /** - * Returns array of TextEdit changes to format this document. - * - * @return \LanguageServer\Protocol\TextEdit[] - */ - public function getFormattedText() - { - if (empty($this->getContent())) { - return []; - } - return Formatter::format($this->getContent(), $this->uri); - } - /** * Returns this document's text content. * diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 2bcda2c..58e7074 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -159,20 +159,6 @@ class TextDocument $this->documentLoader->close($textDocument->uri); } - /** - * The document formatting request is sent from the server to the client to format a whole document. - * - * @param TextDocumentIdentifier $textDocument The document to format - * @param FormattingOptions $options The format options - * @return Promise - */ - public function formatting(TextDocumentIdentifier $textDocument, FormattingOptions $options) - { - return $this->documentLoader->getOrLoad($textDocument->uri)->then(function (PhpDocument $document) { - return $document->getFormattedText(); - }); - } - /** * The references request is sent from the client to the server to resolve project-wide references for the symbol * denoted by the given text document position. diff --git a/tests/FormatterTest.php b/tests/FormatterTest.php deleted file mode 100644 index a46f2ec..0000000 --- a/tests/FormatterTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertSame($output, $edits[0]->newText); - } - - public function testFormatNoChange() - { - $expected = file_get_contents(__DIR__ . '/../fixtures/format_expected.php'); - - $edits = Formatter::format($expected, 'file:///whatever'); - $this->assertSame([], $edits); - } -} diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 5d03451..5d58172 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -34,7 +34,6 @@ class LanguageServerTest extends TestCase $serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL; $serverCapabilities->documentSymbolProvider = true; $serverCapabilities->workspaceSymbolProvider = true; - $serverCapabilities->documentFormattingProvider = true; $serverCapabilities->definitionProvider = true; $serverCapabilities->referencesProvider = true; $serverCapabilities->hoverProvider = true; diff --git a/tests/Server/TextDocument/FormattingTest.php b/tests/Server/TextDocument/FormattingTest.php deleted file mode 100644 index 1844891..0000000 --- a/tests/Server/TextDocument/FormattingTest.php +++ /dev/null @@ -1,50 +0,0 @@ -uri = $uri; - $textDocumentItem->languageId = 'php'; - $textDocumentItem->version = 1; - $textDocumentItem->text = file_get_contents($path); - $textDocument->didOpen($textDocumentItem); - - // how code should look after formatting - $expected = file_get_contents(__DIR__ . '/../../../fixtures/format_expected.php'); - // Request formatting - $result = $textDocument->formatting(new TextDocumentIdentifier($uri), new FormattingOptions())->wait(); - $this->assertEquals([new TextEdit(new Range(new Position(0, 0), new Position(20, 0)), $expected)], $result); - } -} From db484617b638a8af2f617b4fe61aeb1af7f1bd9c Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 29 Oct 2017 13:18:25 -0700 Subject: [PATCH 063/117] ci: speed up submodule cloning --- .gitmodules | 9 +++++++++ .travis.yml | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/.gitmodules b/.gitmodules index 06d05cb..b26d58d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,27 +1,36 @@ [submodule "validation/frameworks/php-language-server"] path = validation/frameworks/php-language-server url = https://github.com/felixfbecker/php-language-server + shallow = true [submodule "validation/frameworks/wordpress"] path = validation/frameworks/wordpress url = https://github.com/wordpress/wordpress + shallow = true [submodule "validation/frameworks/drupal"] path = validation/frameworks/drupal url = https://github.com/drupal/drupal + shallow = true [submodule "validation/frameworks/tolerant-php-parser"] path = validation/frameworks/tolerant-php-parser url = https://github.com/microsoft/tolerant-php-parser + shallow = true [submodule "validation/frameworks/symfony"] path = validation/frameworks/symfony url = https://github.com/symfony/symfony + shallow = true [submodule "validation/frameworks/math-php"] path = validation/frameworks/math-php url = https://github.com/markrogoyski/math-php + shallow = true [submodule "validation/frameworks/codeigniter"] path = validation/frameworks/codeigniter url = https://github.com/bcit-ci/codeigniter + shallow = true [submodule "validation/frameworks/cakephp"] path = validation/frameworks/cakephp url = https://github.com/cakephp/cakephp + shallow = true [submodule "validation/frameworks/phpunit"] path = validation/frameworks/phpunit url = https://github.com/sebastianbergmann/phpunit + shallow = true diff --git a/.travis.yml b/.travis.yml index ac0aec5..5593c66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,9 @@ php: - 7.1 - 7.2 +git: + submodules: false + services: - docker @@ -18,6 +21,7 @@ cache: - $HOME/.npm install: + - git submodule update --init --jobs 9 - composer install --prefer-dist --no-interaction script: From 235a790156d69079927f73195d043f6bba96d9a3 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 01:51:33 -0800 Subject: [PATCH 064/117] ci: remove shallow submodule cloning --- .gitmodules | 9 --------- .travis.yml | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.gitmodules b/.gitmodules index b26d58d..06d05cb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,36 +1,27 @@ [submodule "validation/frameworks/php-language-server"] path = validation/frameworks/php-language-server url = https://github.com/felixfbecker/php-language-server - shallow = true [submodule "validation/frameworks/wordpress"] path = validation/frameworks/wordpress url = https://github.com/wordpress/wordpress - shallow = true [submodule "validation/frameworks/drupal"] path = validation/frameworks/drupal url = https://github.com/drupal/drupal - shallow = true [submodule "validation/frameworks/tolerant-php-parser"] path = validation/frameworks/tolerant-php-parser url = https://github.com/microsoft/tolerant-php-parser - shallow = true [submodule "validation/frameworks/symfony"] path = validation/frameworks/symfony url = https://github.com/symfony/symfony - shallow = true [submodule "validation/frameworks/math-php"] path = validation/frameworks/math-php url = https://github.com/markrogoyski/math-php - shallow = true [submodule "validation/frameworks/codeigniter"] path = validation/frameworks/codeigniter url = https://github.com/bcit-ci/codeigniter - shallow = true [submodule "validation/frameworks/cakephp"] path = validation/frameworks/cakephp url = https://github.com/cakephp/cakephp - shallow = true [submodule "validation/frameworks/phpunit"] path = validation/frameworks/phpunit url = https://github.com/sebastianbergmann/phpunit - shallow = true diff --git a/.travis.yml b/.travis.yml index 5593c66..ffeef4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: - 7.2 git: + depth: 10 submodules: false services: From 74578c7b583248231736da7791441fc376516a54 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 02:28:38 -0800 Subject: [PATCH 065/117] ci(travis): only test lowest and highest PHP version --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ffeef4a..289f969 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php php: - 7.0 - - 7.1 - 7.2 git: From 41e84880b380368ae5862c01e9a00d55067c48ec Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 02:30:34 -0800 Subject: [PATCH 066/117] ci(travis): use string versions --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 289f969..a270acf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: php php: - - 7.0 - - 7.2 + - '7.0' + - '7.2' git: depth: 10 From b03b9a239c38db5d4551ce5b9f0e3876e458edd4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 5 Nov 2017 02:54:56 -0800 Subject: [PATCH 067/117] ci(travis): run on OSX (#517) --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.travis.yml b/.travis.yml index a270acf..fa0f27f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,20 @@ php: - '7.0' - '7.2' +matrix: + include: + - os: osx + osx_image: xcode9.1 + language: generic + before_install: + # Fix ruby error https://github.com/Homebrew/brew/issues/3299 + - brew update + - brew tap homebrew/homebrew-php + - brew install php71 + - brew install homebrew/php/php71-xdebug + - curl https://getcomposer.org/installer | php + - ln -s "`pwd`/composer.phar" /usr/local/bin/composer + git: depth: 10 submodules: false From f5c45f83edb2bf73b90cff2f35fd252d391ed278 Mon Sep 17 00:00:00 2001 From: Brandon Max Date: Thu, 9 Nov 2017 19:15:36 -0500 Subject: [PATCH 068/117] docs(contributing): document how to use XDebug (#518) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8e7117c..b25680e 100644 --- a/README.md +++ b/README.md @@ -201,3 +201,9 @@ Lint with The project parses PHPStorm's PHP stubs to get support for PHP builtins. It re-parses them as needed after Composer processes, but after some code changes (such as ones involving the index or parsing) you may have to explicitly re-parse them: composer run-script parse-stubs + +To debug with xDebug ensure that you have this set as an environment variable + + COMPOSER_ALLOW_XDEBUG=1 + +This tells the Language Server to not restart without XDebug if it detects that XDebug is enabled (XDebug has a high performance impact). From b4a3134e2a74b008bcc363a8e9d2fd16692c799a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 16:57:05 -0800 Subject: [PATCH 069/117] ci(travis): use build stages --- .travis.yml | 59 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa0f27f..e99a2cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,14 @@ + language: php php: - '7.0' - '7.2' -matrix: - include: - - os: osx - osx_image: xcode9.1 - language: generic - before_install: - # Fix ruby error https://github.com/Homebrew/brew/issues/3299 - - brew update - - brew tap homebrew/homebrew-php - - brew install php71 - - brew install homebrew/php/php71-xdebug - - curl https://getcomposer.org/installer | php - - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - git: depth: 10 submodules: false -services: - - docker - env: global: - BUILD_LEADER_ID=1 @@ -37,18 +21,43 @@ cache: install: - git submodule update --init --jobs 9 - composer install --prefer-dist --no-interaction - script: - vendor/bin/phpcs -n - vendor/bin/phpunit --coverage-clover=coverage.xml - -after_success: +after_script: - bash <(curl -s https://codecov.io/bash) - - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - - git fetch --tags - - nvm install 8 && nvm use 8 - - npm install - - npm run semantic-release + +jobs: + include: + - stage: test + os: osx + osx_image: xcode9.1 + language: generic + before_install: + # Fix ruby error https://github.com/Homebrew/brew/issues/3299 + - brew update + - brew tap homebrew/homebrew-php + - brew install php71 + - brew install homebrew/php/php71-xdebug + - curl https://getcomposer.org/installer | php + - ln -s "`pwd`/composer.phar" /usr/local/bin/composer + - stage: release + language: node_js + node_js: '8' + services: + - docker + before_install: + - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* + - git fetch --tags + install: + - npm install + script: + - npm run semantic-release + +stages: + - test + - name: release + if: branch = master branches: except: From 857fe26eb51ce174d90390fde8c68fc702c3e033 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 18:48:02 -0800 Subject: [PATCH 070/117] ci(travis): optimize --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e99a2cc..3ca1f2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,16 +15,15 @@ env: cache: directories: + - $HOME/Library/Caches/Homebrew - $HOME/.composer/cache - $HOME/.npm install: - - git submodule update --init --jobs 9 - composer install --prefer-dist --no-interaction script: - vendor/bin/phpcs -n - vendor/bin/phpunit --coverage-clover=coverage.xml -after_script: - bash <(curl -s https://codecov.io/bash) jobs: @@ -51,7 +50,7 @@ jobs: - git fetch --tags install: - npm install - script: + after_success: - npm run semantic-release stages: From d54ece33669575b56021bf1c199693a7b878fd2e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 18:59:41 -0800 Subject: [PATCH 071/117] build(docker): optimize docker build --- Dockerfile | 5 ----- release-docker.php | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index f4291db..549a28f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,11 +7,6 @@ FROM php:7-cli MAINTAINER Felix Becker -RUN apt-get update \ - # Needed for CodeSniffer - && apt-get install -y libxml2 libxml2-dev \ - && rm -rf /var/lib/apt/lists/* - RUN docker-php-ext-configure pcntl --enable-pcntl RUN docker-php-ext-install pcntl COPY ./php.ini /usr/local/etc/php/conf.d/ diff --git a/release-docker.php b/release-docker.php index 8cac2db..f833d60 100755 --- a/release-docker.php +++ b/release-docker.php @@ -6,5 +6,7 @@ $dockerPassword = getenv('DOCKER_PASSWORD'); $version = json_decode(file_get_contents(__DIR__ . '/package.json'))->version; system("docker login -e=$dockerEmail -u=$dockerUsername -p=$dockerPassword"); -system("docker build -t felixfbecker/php-language-server:$version ."); +system("docker build -t felixfbecker/php-language-server:latest ."); +system("docker tag felixfbecker/php-language-server:latest felixfbecker/php-language-server:$version ."); system("docker push felixfbecker/php-language-server:$version"); +system("docker push felixfbecker/php-language-server:latest"); From eadf305a1f86adc79ad7e10f69d343613a1b49aa Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 19:07:43 -0800 Subject: [PATCH 072/117] ci(travis): fix release --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3ca1f2f..54f8f26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,11 +45,12 @@ jobs: node_js: '8' services: - docker - before_install: + install: - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - git fetch --tags - install: + - composer install --prefer-dist --no-interaction - npm install + script: skip after_success: - npm run semantic-release From 3e41244b6f82e3419d6335d17119ef7da7737c6e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 22:01:50 -0800 Subject: [PATCH 073/117] ci(travis): use PHP 7 for release --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 54f8f26..22b4766 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,13 +42,15 @@ jobs: - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - stage: release language: node_js - node_js: '8' + php: '7.0' services: - docker install: - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - git fetch --tags - composer install --prefer-dist --no-interaction + - nvm install 8 + - nvm use 8 - npm install script: skip after_success: From 0e645301cc306dc86050118816576ed20ed882d8 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 9 Nov 2017 22:40:56 -0800 Subject: [PATCH 074/117] ci(travis): remove language tag --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 22b4766..1fc8544 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,6 @@ jobs: - curl https://getcomposer.org/installer | php - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - stage: release - language: node_js php: '7.0' services: - docker From 9434cb1b67225c841ea32b0662280be4fb089d5d Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 10 Nov 2017 00:26:03 -0800 Subject: [PATCH 075/117] ci(release): set verifyConditions to empty array --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 75f02e1..d08d1a4 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ } }, "release": { + "verifyConditions": [], "getLastRelease": "last-release-git" }, "repository": { From 1804ac8d978a318a4a6b88dc02b4362854b6a65b Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 10 Nov 2017 01:16:15 -0800 Subject: [PATCH 076/117] ci(travis): correct BUILD_LEADER_ID --- .travis.yml | 2 +- package.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1fc8544..5496dee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ git: env: global: - - BUILD_LEADER_ID=1 + - BUILD_LEADER_ID=4 cache: directories: diff --git a/package.json b/package.json index d08d1a4..75f02e1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ } }, "release": { - "verifyConditions": [], "getLastRelease": "last-release-git" }, "repository": { From 0afc3320d518967b5a1aba0b9c2610c2f5b67a8f Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 12 Nov 2017 12:40:13 -0800 Subject: [PATCH 077/117] ci(travis): pin version to 7.2RC5 7.2RC6 is causing segfaults --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5496dee..4eb97ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: php php: - '7.0' - - '7.2' + - '7.2RC5' git: depth: 10 From 1ec8d8d8e20e6d4370e28743851aa6128e579280 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 12 Nov 2017 12:41:51 -0800 Subject: [PATCH 078/117] ci(travis): correct version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4eb97ee..fa4520e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: php php: - '7.0' - - '7.2RC5' + - '7.2.0RC5' git: depth: 10 From 607cd8158d01f15c0d7fe784082a66aa7fa2e25a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 15 Nov 2017 13:08:15 -0800 Subject: [PATCH 079/117] test(index): add IndexTest --- tests/Index/IndexTest.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/Index/IndexTest.php diff --git a/tests/Index/IndexTest.php b/tests/Index/IndexTest.php new file mode 100644 index 0000000..8236f8d --- /dev/null +++ b/tests/Index/IndexTest.php @@ -0,0 +1,30 @@ +setDefinition('SomeNamespace\SomeClass', new Definition); + $methodDefinition = new Definition; + $methodFqn = 'SomeNamespace\SomeClass->someMethod()'; + $index->setDefinition($methodFqn, $methodDefinition); + $index->setDefinition('SomeNamespace\SomeClass->someProperty', new Definition); + $this->assertSame($methodDefinition, $index->getDefinition($methodFqn)); + } + + public function testGetSetClassDefinition() + { + $index = new Index; + $definition = new Definition; + $fqn = 'SomeNamespace\SomeClass'; + $index->setDefinition($fqn, $definition); + $this->assertSame($definition, $index->getDefinition($fqn)); + } +} From 06747bb734ec2b40fb1d9edc238fe7ec3fb9363d Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 15 Nov 2017 13:14:08 -0800 Subject: [PATCH 080/117] ci(travis): don't release on PRs --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa4520e..06c4140 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ jobs: stages: - test - name: release - if: branch = master + if: branch = master AND type = push AND fork = false branches: except: From b1a1875070ea93fdcd5c70ffe7fef8ef25c9b3fb Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 15 Nov 2017 22:38:01 -0800 Subject: [PATCH 081/117] fix(completion): don't suggest characer (#527) closes #372 --- fixtures/completion/html_no_completion.php | 1 + src/CompletionProvider.php | 23 +++++++++-- src/Protocol/CompletionContext.php | 30 ++++++++++++++ src/Protocol/CompletionTriggerKind.php | 16 ++++++++ src/Server/TextDocument.php | 10 +++-- tests/Server/TextDocument/CompletionTest.php | 41 +++++++++++++++++++- 6 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 fixtures/completion/html_no_completion.php create mode 100644 src/Protocol/CompletionContext.php create mode 100644 src/Protocol/CompletionTriggerKind.php diff --git a/fixtures/completion/html_no_completion.php b/fixtures/completion/html_no_completion.php new file mode 100644 index 0000000..9318418 --- /dev/null +++ b/fixtures/completion/html_no_completion.php @@ -0,0 +1 @@ +< diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 68c5a0c..d774423 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -10,7 +10,9 @@ use LanguageServer\Protocol\{ Position, CompletionList, CompletionItem, - CompletionItemKind + CompletionItemKind, + CompletionContext, + CompletionTriggerKind }; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; @@ -122,9 +124,10 @@ class CompletionProvider * * @param PhpDocument $doc The opened document * @param Position $pos The cursor position + * @param CompletionContext $context The completion context * @return CompletionList */ - public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList + public function provideCompletion(PhpDocument $doc, Position $pos, CompletionContext $context = null): CompletionList { // This can be made much more performant if the tree follows specific invariants. $node = $doc->getNodeAtPosition($pos); @@ -152,7 +155,21 @@ class CompletionProvider // Inspect the type of expression under the cursor - if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) { + $content = $doc->getContent(); + $offset = $pos->toOffset($content); + if ( + $node === null + || ( + $node instanceof Node\Statement\InlineHtml + && ( + $context === null + // Make sure to not suggest on the > trigger character in HTML + || $context->triggerKind === CompletionTriggerKind::INVOKED + || $context->triggerCharacter === '<' + ) + ) + || $pos == new Position(0, 0) + ) { // HTML, beginning of file // Inside HTML and at the beginning of the file, propose triggerKind = $triggerKind; + $this->triggerCharacter = $triggerCharacter; + } +} diff --git a/src/Protocol/CompletionTriggerKind.php b/src/Protocol/CompletionTriggerKind.php new file mode 100644 index 0000000..d36129d --- /dev/null +++ b/src/Protocol/CompletionTriggerKind.php @@ -0,0 +1,16 @@ + */ - public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise + public function completion(TextDocumentIdentifier $textDocument, Position $position, CompletionContext $context = null): Promise { - return coroutine(function () use ($textDocument, $position) { + return coroutine(function () use ($textDocument, $position, $context) { $document = yield $this->documentLoader->getOrLoad($textDocument->uri); - return $this->completionProvider->provideCompletion($document, $position); + return $this->completionProvider->provideCompletion($document, $position, $context); }); } diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 0d68ec3..424dc3c 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -17,7 +17,9 @@ use LanguageServer\Protocol\{ Position, CompletionList, CompletionItem, - CompletionItemKind + CompletionItemKind, + CompletionContext, + CompletionTriggerKind }; use function LanguageServer\pathToUri; @@ -464,6 +466,41 @@ class CompletionTest extends TestCase ], true), $items); } + public function testHtmlPrefixShouldNotTriggerCompletion() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '>') + )->wait(); + $this->assertEquals(new CompletionList([], true), $items); + } + + public function testHtmlPrefixShouldTriggerCompletionIfManuallyInvoked() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::INVOKED) + )->wait(); + $this->assertEquals(new CompletionList([ + new CompletionItem( + ' Date: Sat, 18 Nov 2017 16:59:57 -0800 Subject: [PATCH 082/117] fix(indexing): properly resolve self, static and parent keywords (#532) Previously we would dump static, self and parent as literal FQNs into the index. --- .travis.yml | 2 +- .vscode/launch.json | 52 +++++----- src/DefinitionResolver.php | 28 +++++- src/TreeAnalyzer.php | 94 ++++++++++++------- tests/Server/ServerTestCase.php | 36 +++---- .../TextDocument/Definition/GlobalTest.php | 24 +++-- .../Definition/NamespacedTest.php | 4 +- tests/Server/TextDocument/HoverTest.php | 2 +- .../TextDocument/References/GlobalTest.php | 2 +- .../WithReturnTypehints.php.expected.json | 5 +- .../cases/nameToken.php.expected.json | 6 +- .../cases/parent1.php.expected.json | 3 - .../cases/parent3.php.expected.json | 3 - .../Validation/cases/self1.php.expected.json | 2 +- .../Validation/cases/self2.php.expected.json | 2 +- .../Validation/cases/self3.php.expected.json | 2 +- .../Validation/cases/self4.php.expected.json | 2 +- .../cases/static1.php.expected.json | 2 +- .../cases/static2.php.expected.json | 2 +- .../cases/static3.php.expected.json | 4 +- .../cases/static4.php.expected.json | 3 + 21 files changed, 172 insertions(+), 108 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06c4140..9b247b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - composer install --prefer-dist --no-interaction script: - vendor/bin/phpcs -n - - vendor/bin/phpunit --coverage-clover=coverage.xml + - vendor/bin/phpunit --coverage-clover=coverage.xml --colors=always - bash <(curl -s https://codecov.io/bash) jobs: diff --git a/.vscode/launch.json b/.vscode/launch.json index 0f77709..1e4b234 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,29 +1,27 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Listen for XDebug", - "type": "php", - "request": "launch", - "port": 9000 - }, - { - "name": "Launch currently open script", - "type": "php", - "request": "launch", - "program": "${file}", - "cwd": "${fileDirname}", - "port": 9000 - }, - { - "name": "PHPUnit", - "type": "php", - "request": "launch", - "program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit", - "cwd": "${workspaceRoot}", - "args": [ - // "--filter", "CompletionTest" - ] - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "PHPUnit", + "type": "php", + "request": "launch", + "program": "${workspaceRoot}/vendor/phpunit/phpunit/phpunit", + // "args": ["--filter", "testDefinitionForSelfKeyword"], + "cwd": "${workspaceRoot}" + }, + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + } + ] } diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 620527e..c9d1400 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -264,13 +264,38 @@ class DefinitionResolver // Other references are references to a global symbol that have an FQN // Find out the FQN $fqn = $this->resolveReferenceNodeToFqn($node); - if ($fqn === null) { + if (!$fqn) { return null; } + + if ($fqn === 'self' || $fqn === 'static') { + // Resolve self and static keywords to the containing class + // (This is not 100% correct for static but better than nothing) + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode) { + return; + } + $fqn = (string)$classNode->getNamespacedName(); + if (!$fqn) { + return; + } + } else if ($fqn === 'parent') { + // Resolve parent keyword to the base class FQN + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) { + return; + } + $fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName(); + if (!$fqn) { + return; + } + } + // If the node is a function or constant, it could be namespaced, but PHP falls back to global // http://php.net/manual/en/language.namespaces.fallback.php // TODO - verify that this is not a method $globalFallback = ParserHelpers\isConstantFetch($node) || $parent instanceof Node\Expression\CallExpression; + // Return the Definition object from the index index return $this->index->getDefinition($fqn, $globalFallback); } @@ -278,6 +303,7 @@ class DefinitionResolver /** * Given any node, returns the FQN of the symbol that is referenced * Returns null if the FQN could not be resolved or the reference node references a variable + * May also return "static", "self" or "parent" * * @param Node $node * @return string|null diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 58de8db..1daabe7 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -140,8 +140,9 @@ class TreeAnalyzer $this->definitionNodes[$fqn] = $node; $this->definitions[$fqn] = $this->definitionResolver->createDefinitionFromNode($node, $fqn); } else { + $parent = $node->parent; - if (!( + if ( ( // $node->parent instanceof Node\Expression\ScopedPropertyAccessExpression || ($node instanceof Node\Expression\ScopedPropertyAccessExpression || @@ -150,41 +151,68 @@ class TreeAnalyzer $node->parent instanceof Node\Expression\CallExpression || $node->memberName instanceof PhpParser\Token )) - || ($parent instanceof Node\Statement\NamespaceDefinition && $parent->name !== null && $parent->name->getStart() === $node->getStart())) + || ($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); + return; + } - 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); - } - } + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($node); + if (!$fqn) { + return; + } - // 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); - } - } + if ($fqn === 'self' || $fqn === 'static') { + // Resolve self and static keywords to the containing class + // (This is not 100% correct for static but better than nothing) + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode) { + return; + } + $fqn = (string)$classNode->getNamespacedName(); + if (!$fqn) { + return; + } + } else if ($fqn === 'parent') { + // Resolve parent keyword to the base class FQN + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if (!$classNode || !$classNode->classBaseClause || !$classNode->classBaseClause->baseClass) { + return; + } + $fqn = (string)$classNode->classBaseClause->baseClass->getResolvedName(); + if (!$fqn) { + return; + } + } + + $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); } } } diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 04e5976..45d949f 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -122,15 +122,16 @@ abstract class ServerTestCase extends TestCase 0 => new Location($referencesUri, new Range(new Position(29, 5), new Position(29, 15))) ], 'TestNamespace\\TestClass' => [ - 0 => new Location($symbolsUri , new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} - 1 => new Location($referencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); - 2 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); - 3 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; - 4 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; - 5 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) - 6 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass - 7 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; - 8 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass; + 0 => new Location($symbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; + 1 => new Location($symbolsUri , new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} + 2 => new Location($referencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); + 3 => new Location($referencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); + 4 => new Location($referencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; + 5 => new Location($referencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; + 6 => new Location($referencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) + 7 => new Location($referencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass + 8 => new Location($referencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; + 9 => new Location($useUri, new Range(new Position( 4, 4), new Position( 4, 27))), // use TestNamespace\TestClass; ], 'TestNamespace\\TestChild' => [ 0 => new Location($referencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; @@ -176,14 +177,15 @@ abstract class ServerTestCase extends TestCase 1 => new Location($globalReferencesUri, new Range(new Position(29, 5), new Position(29, 15))) ], 'TestClass' => [ - 0 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} - 1 => new Location($globalReferencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); - 2 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); - 3 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; - 4 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; - 5 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) - 6 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass - 7 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; + 0 => new Location($globalSymbolsUri, new Range(new Position(48, 13), new Position(48, 17))), // echo self::TEST_CLASS_CONST; + 1 => new Location($globalSymbolsUri, new Range(new Position(99, 25), new Position(99, 34))), // class ChildClass extends TestClass {} + 2 => new Location($globalReferencesUri, new Range(new Position( 4, 11), new Position( 4, 20))), // $obj = new TestClass(); + 3 => new Location($globalReferencesUri, new Range(new Position( 7, 0), new Position( 7, 9))), // TestClass::staticTestMethod(); + 4 => new Location($globalReferencesUri, new Range(new Position( 8, 5), new Position( 8, 14))), // echo TestClass::$staticTestProperty; + 5 => new Location($globalReferencesUri, new Range(new Position( 9, 5), new Position( 9, 14))), // TestClass::TEST_CLASS_CONST; + 6 => new Location($globalReferencesUri, new Range(new Position(21, 18), new Position(21, 27))), // function whatever(TestClass $param) + 7 => new Location($globalReferencesUri, new Range(new Position(21, 37), new Position(21, 46))), // function whatever(TestClass $param): TestClass + 8 => new Location($globalReferencesUri, new Range(new Position(39, 0), new Position(39, 9))), // TestClass::$staticTestProperty[123]->testProperty; ], 'TestChild' => [ 0 => new Location($globalReferencesUri, new Range(new Position(42, 5), new Position(42, 25))), // echo $child->testProperty; diff --git a/tests/Server/TextDocument/Definition/GlobalTest.php b/tests/Server/TextDocument/Definition/GlobalTest.php index 0988e2a..2a80873 100644 --- a/tests/Server/TextDocument/Definition/GlobalTest.php +++ b/tests/Server/TextDocument/Definition/GlobalTest.php @@ -29,11 +29,23 @@ class GlobalTest extends ServerTestCase $this->assertEquals([], $result); } + public function testDefinitionForSelfKeyword() + { + // echo self::TEST_CLASS_CONST; + // Get definition for self + $reference = $this->getReferenceLocations('TestClass')[0]; + $result = $this->textDocument->definition( + new TextDocumentIdentifier($reference->uri), + $reference->range->start + )->wait(); + $this->assertEquals($this->getDefinitionLocation('TestClass'), $result); + } + public function testDefinitionForClassLike() { // $obj = new TestClass(); // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -45,7 +57,7 @@ class GlobalTest extends ServerTestCase { // TestClass::staticTestMethod(); // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[1]; + $reference = $this->getReferenceLocations('TestClass')[2]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -57,7 +69,7 @@ class GlobalTest extends ServerTestCase { // echo TestClass::$staticTestProperty; // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[2]; + $reference = $this->getReferenceLocations('TestClass')[3]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -69,7 +81,7 @@ class GlobalTest extends ServerTestCase { // TestClass::TEST_CLASS_CONST; // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[3]; + $reference = $this->getReferenceLocations('TestClass')[4]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -213,7 +225,7 @@ class GlobalTest extends ServerTestCase { // function whatever(TestClass $param) { // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[4]; + $reference = $this->getReferenceLocations('TestClass')[5]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -225,7 +237,7 @@ class GlobalTest extends ServerTestCase { // function whatever(TestClass $param): TestClass { // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[5]; + $reference = $this->getReferenceLocations('TestClass')[6]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start diff --git a/tests/Server/TextDocument/Definition/NamespacedTest.php b/tests/Server/TextDocument/Definition/NamespacedTest.php index ef66624..395881b 100644 --- a/tests/Server/TextDocument/Definition/NamespacedTest.php +++ b/tests/Server/TextDocument/Definition/NamespacedTest.php @@ -34,7 +34,7 @@ class NamespacedTest extends GlobalTest { // use TestNamespace\TestClass; // Get definition for TestClass - $reference = $this->getReferenceLocations('TestClass')[6]; + $reference = $this->getReferenceLocations('TestClass')[7]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start @@ -46,7 +46,7 @@ class NamespacedTest extends GlobalTest { // use TestNamespace\{TestTrait, TestInterface}; // Get definition for TestInterface - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->definition( new TextDocumentIdentifier($reference->uri), $reference->range->start diff --git a/tests/Server/TextDocument/HoverTest.php b/tests/Server/TextDocument/HoverTest.php index 5010360..2ba49e1 100644 --- a/tests/Server/TextDocument/HoverTest.php +++ b/tests/Server/TextDocument/HoverTest.php @@ -15,7 +15,7 @@ class HoverTest extends ServerTestCase { // $obj = new TestClass(); // Get hover for TestClass - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->hover( new TextDocumentIdentifier($reference->uri), $reference->range->start diff --git a/tests/Server/TextDocument/References/GlobalTest.php b/tests/Server/TextDocument/References/GlobalTest.php index fcb6e71..105dfef 100644 --- a/tests/Server/TextDocument/References/GlobalTest.php +++ b/tests/Server/TextDocument/References/GlobalTest.php @@ -151,7 +151,7 @@ class GlobalTest extends ServerTestCase { // $obj = new TestClass(); // Get references for TestClass - $reference = $this->getReferenceLocations('TestClass')[0]; + $reference = $this->getReferenceLocations('TestClass')[1]; $result = $this->textDocument->references( new ReferenceContext, new TextDocumentIdentifier($reference->uri), diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index d11f271..8c6b092 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -3,7 +3,7 @@ "Fixtures\\Prophecy\\EmptyClass": [ "./WithReturnTypehints.php" ], - "self": [ + "Fixtures\\Prophecy\\WithReturnTypehints": [ "./WithReturnTypehints.php" ], "Fixtures\\Prophecy\\__CLASS__": [ @@ -11,9 +11,6 @@ ], "__CLASS__": [ "./WithReturnTypehints.php" - ], - "parent": [ - "./WithReturnTypehints.php" ] }, "definitions": { diff --git a/tests/Validation/cases/nameToken.php.expected.json b/tests/Validation/cases/nameToken.php.expected.json index c06217b..37cb4ca 100644 --- a/tests/Validation/cases/nameToken.php.expected.json +++ b/tests/Validation/cases/nameToken.php.expected.json @@ -1,5 +1,9 @@ { - "references": [], + "references": { + "A": [ + "./nameToken.php" + ] + }, "definitions": { "A": { "fqn": "A", diff --git a/tests/Validation/cases/parent1.php.expected.json b/tests/Validation/cases/parent1.php.expected.json index 8b29461..56d7e61 100644 --- a/tests/Validation/cases/parent1.php.expected.json +++ b/tests/Validation/cases/parent1.php.expected.json @@ -2,9 +2,6 @@ "references": { "MyNamespace\\B": [ "./parent1.php" - ], - "parent": [ - "./parent1.php" ] }, "definitions": { diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json index 1e51b84..3954754 100644 --- a/tests/Validation/cases/parent3.php.expected.json +++ b/tests/Validation/cases/parent3.php.expected.json @@ -5,9 +5,6 @@ ], "MyNamespace\\B->b()": [ "./parent3.php" - ], - "parent": [ - "./parent3.php" ] }, "definitions": { diff --git a/tests/Validation/cases/self1.php.expected.json b/tests/Validation/cases/self1.php.expected.json index 2ce8e43..fe2bc63 100644 --- a/tests/Validation/cases/self1.php.expected.json +++ b/tests/Validation/cases/self1.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./self1.php" ], - "self": [ + "MyNamespace\\A": [ "./self1.php" ] }, diff --git a/tests/Validation/cases/self2.php.expected.json b/tests/Validation/cases/self2.php.expected.json index cc00e3a..60ab062 100644 --- a/tests/Validation/cases/self2.php.expected.json +++ b/tests/Validation/cases/self2.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./self2.php" ], - "self": [ + "MyNamespace\\A": [ "./self2.php" ] }, diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json index 9f25ba1..7d66c2b 100644 --- a/tests/Validation/cases/self3.php.expected.json +++ b/tests/Validation/cases/self3.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A->b()": [ "./self3.php" ], - "self": [ + "MyNamespace\\A": [ "./self3.php" ] }, diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index 7f7b25e..64765b7 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -1,6 +1,6 @@ { "references": { - "self": [ + "MyNamespace\\A": [ "./self4.php" ], "MyNamespace\\A->addTestFile()": [ diff --git a/tests/Validation/cases/static1.php.expected.json b/tests/Validation/cases/static1.php.expected.json index 453edc8..db1a0d2 100644 --- a/tests/Validation/cases/static1.php.expected.json +++ b/tests/Validation/cases/static1.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./static1.php" ], - "static": [ + "MyNamespace\\A": [ "./static1.php" ] }, diff --git a/tests/Validation/cases/static2.php.expected.json b/tests/Validation/cases/static2.php.expected.json index 87e8e9a..bb662cb 100644 --- a/tests/Validation/cases/static2.php.expected.json +++ b/tests/Validation/cases/static2.php.expected.json @@ -6,7 +6,7 @@ "MyNamespace\\A::b()": [ "./static2.php" ], - "static": [ + "MyNamespace\\A": [ "./static2.php" ] }, diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json index 37524ea..34d3851 100644 --- a/tests/Validation/cases/static3.php.expected.json +++ b/tests/Validation/cases/static3.php.expected.json @@ -3,10 +3,10 @@ "MyNamespace\\B": [ "./static3.php" ], - "MyNamespace\\b()": [ + "static->b()": [ "./static3.php" ], - "b()": [ + "MyNamespace\\A": [ "./static3.php" ] }, diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index 4bcdd9d..de71d41 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -2,6 +2,9 @@ "references": { "MyNamespace\\B": [ "./static4.php" + ], + "MyNamespace\\A": [ + "./static4.php" ] }, "definitions": { From 4f672c24d8c127d88d51eefb32973668e0cc54de Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Sun, 19 Nov 2017 02:41:37 +0100 Subject: [PATCH 083/117] feat(diagnostics): report error when $this is used in a static method or outside a class method (#528) --- .../diagnostics/baselines/this_in_method.php | 9 ++ .../diagnostics/errors/this_in_function.php | 6 + fixtures/diagnostics/errors/this_in_root.php | 3 + .../errors/this_in_static_method.php | 9 ++ src/TreeAnalyzer.php | 19 +++ tests/Diagnostics/InvalidThisUsageTest.php | 118 ++++++++++++++++++ 6 files changed, 164 insertions(+) create mode 100644 fixtures/diagnostics/baselines/this_in_method.php create mode 100644 fixtures/diagnostics/errors/this_in_function.php create mode 100644 fixtures/diagnostics/errors/this_in_root.php create mode 100644 fixtures/diagnostics/errors/this_in_static_method.php create mode 100644 tests/Diagnostics/InvalidThisUsageTest.php diff --git a/fixtures/diagnostics/baselines/this_in_method.php b/fixtures/diagnostics/baselines/this_in_method.php new file mode 100644 index 0000000..0463b79 --- /dev/null +++ b/fixtures/diagnostics/baselines/this_in_method.php @@ -0,0 +1,9 @@ +start, $error->length, $this->sourceFileNode->fileContents); @@ -92,6 +93,24 @@ class TreeAnalyzer 'php' ); } + + // Check for invalid usage of $this. + if ($node instanceof Node\Expression\Variable && $node->getName() === 'this') { + // Find the first ancestor that's a class method. Return an error + // if there is none, or if the method is static. + $method = $node->getFirstAncestor(Node\MethodDeclaration::class); + if ($method === null || $method->isStatic()) { + $this->diagnostics[] = new Diagnostic( + $method === null + ? "\$this can only be used in an object context." + : "\$this can not be used in static methods.", + Range::fromNode($node), + null, + DiagnosticSeverity::ERROR, + 'php' + ); + } + } } /** diff --git a/tests/Diagnostics/InvalidThisUsageTest.php b/tests/Diagnostics/InvalidThisUsageTest.php new file mode 100644 index 0000000..7542918 --- /dev/null +++ b/tests/Diagnostics/InvalidThisUsageTest.php @@ -0,0 +1,118 @@ +getDiagnostics(); + } + + /** + * Assertions about a diagnostic. + * + * @param Diagnostic|null $diagnostic + * @param int $message + * @param string $severity + * @param Range $range + */ + private function assertDiagnostic($diagnostic, $message, $severity, $range) + { + $this->assertInstanceOf(Diagnostic::class, $diagnostic); + $this->assertEquals($message, $diagnostic->message); + $this->assertEquals($severity, $diagnostic->severity); + $this->assertEquals($range, $diagnostic->range); + } + + public function testThisInStaticMethodProducesError() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/errors/this_in_static_method.php' + ); + + $this->assertCount(1, $diagnostics); + $this->assertDiagnostic( + $diagnostics[0], + '$this can not be used in static methods.', + DiagnosticSeverity::ERROR, + new Range( + new Position(6, 15), + new Position(6, 20) + ) + ); + } + + public function testThisInFunctionProducesError() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/errors/this_in_function.php' + ); + + $this->assertCount(1, $diagnostics); + $this->assertDiagnostic( + $diagnostics[0], + '$this can only be used in an object context.', + DiagnosticSeverity::ERROR, + new Range( + new Position(4, 11), + new Position(4, 16) + ) + ); + } + + public function testThisInRoot() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/errors/this_in_root.php' + ); + + $this->assertCount(1, $diagnostics); + $this->assertDiagnostic( + $diagnostics[0], + '$this can only be used in an object context.', + DiagnosticSeverity::ERROR, + new Range( + new Position(2, 5), + new Position(2, 10) + ) + ); + } + + public function testThisInMethodProducesNoError() + { + $diagnostics = $this->collectDiagnostics( + __DIR__ . '/../../fixtures/diagnostics/baselines/this_in_method.php' + ); + + $this->assertCount(0, $diagnostics); + } +} From 724eb6f1dced0248e86c0496e301936506d5b73e Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Tue, 21 Nov 2017 03:41:03 -0800 Subject: [PATCH 084/117] ci(appveyor): update image --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index fa6d26a..7d0525c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,6 @@ version: '{build}' +image: Visual Studio 2017 platform: - x64 From 31bae23912a54ced808d9f4df525fcba3f7c929a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 22 Nov 2017 03:24:24 -0800 Subject: [PATCH 085/117] ci(release): use semantic-release v10 --- .travis.yml | 10 ++-------- dependencies.yml | 12 +++++++++++- package.json | 20 +++++++++++++++++--- release-docker.php | 12 ------------ 4 files changed, 30 insertions(+), 24 deletions(-) delete mode 100755 release-docker.php diff --git a/.travis.yml b/.travis.yml index 9b247b3..81d6911 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,6 @@ git: depth: 10 submodules: false -env: - global: - - BUILD_LEADER_ID=4 - cache: directories: - $HOME/Library/Caches/Homebrew @@ -45,14 +41,12 @@ jobs: services: - docker install: - - git config --replace-all remote.origin.fetch +refs/heads/*:refs/remotes/origin/* - - git fetch --tags - composer install --prefer-dist --no-interaction - nvm install 8 - nvm use 8 - npm install - script: skip - after_success: + script: + - docker build -t felixfbecker/php-language-server . - npm run semantic-release stages: diff --git a/dependencies.yml b/dependencies.yml index 320ae87..bf6bd77 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -1,10 +1,20 @@ collectors: +# pull requests for new major versions - type: php-composer path: / actors: - # pull requests for new major versions - type: php-composer versions: "Y.0.0" settings: commit_message_prefix: "chore: " +- type: js-npm + path: / + settings: + dist_tags: + semantic-release: next + actors: + - type: js-npm + versions: "Y.0.0" + settings: + commit_message_prefix: "chore: " diff --git a/package.json b/package.json index 75f02e1..6d6fe31 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,16 @@ "private": true, "scripts": { "commitmsg": "validate-commit-msg", - "semantic-release": "semantic-release pre && php release-docker.php && semantic-release post" + "semantic-release": "semantic-release" }, "devDependencies": { + "@semantic-release/github": "^1.0.0", + "@semantic-release/last-release-git-tag": "^1.0.0", "cz-conventional-changelog": "^2.0.0", "husky": "^0.14.3", "last-release-git": "0.0.3", - "semantic-release": "^8.2.0", + "semantic-release": "^10.0.0", + "semantic-release-docker": "^1.0.0", "validate-commit-msg": "^2.14.0" }, "config": { @@ -19,7 +22,18 @@ } }, "release": { - "getLastRelease": "last-release-git" + "verifyConditions": [ + "@semantic-release/github", + "semantic-release-docker" + ], + "getLastRelease": "@semantic-release/last-release-git-tag", + "publish": [ + "@semantic-release/github", + { + "path": "semantic-release-docker", + "name": "felixfbecker/php-language-server" + } + ] }, "repository": { "type": "git", diff --git a/release-docker.php b/release-docker.php deleted file mode 100755 index f833d60..0000000 --- a/release-docker.php +++ /dev/null @@ -1,12 +0,0 @@ -version; - -system("docker login -e=$dockerEmail -u=$dockerUsername -p=$dockerPassword"); -system("docker build -t felixfbecker/php-language-server:latest ."); -system("docker tag felixfbecker/php-language-server:latest felixfbecker/php-language-server:$version ."); -system("docker push felixfbecker/php-language-server:$version"); -system("docker push felixfbecker/php-language-server:latest"); From ff746a836d5d7d9a05e39afa32c9a4e8600ce7c3 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 25 Nov 2017 10:52:21 -0800 Subject: [PATCH 086/117] chore: update semantic-release to v11 --- package.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6d6fe31..828931f 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,12 @@ "semantic-release": "semantic-release" }, "devDependencies": { - "@semantic-release/github": "^1.0.0", - "@semantic-release/last-release-git-tag": "^1.0.0", + "@semantic-release/github": "^2.0.0", + "@semantic-release/last-release-git-tag": "^2.0.0", "cz-conventional-changelog": "^2.0.0", "husky": "^0.14.3", - "last-release-git": "0.0.3", - "semantic-release": "^10.0.0", - "semantic-release-docker": "^1.0.0", + "semantic-release": "^11.0.0", + "semantic-release-docker": "^2.0.0", "validate-commit-msg": "^2.14.0" }, "config": { From 9b1fafae58b6c596d2627002f032ef5e87605ace Mon Sep 17 00:00:00 2001 From: Maarten Staa Date: Sun, 3 Dec 2017 22:42:01 +0100 Subject: [PATCH 087/117] fix(diagnostics): update checking of $this usage to only error in static methods (#545) --- .../diagnostics/errors/this_in_function.php | 6 ---- fixtures/diagnostics/errors/this_in_root.php | 3 -- src/TreeAnalyzer.php | 6 ++-- tests/Diagnostics/InvalidThisUsageTest.php | 36 ------------------- 4 files changed, 2 insertions(+), 49 deletions(-) delete mode 100644 fixtures/diagnostics/errors/this_in_function.php delete mode 100644 fixtures/diagnostics/errors/this_in_root.php diff --git a/fixtures/diagnostics/errors/this_in_function.php b/fixtures/diagnostics/errors/this_in_function.php deleted file mode 100644 index db6139d..0000000 --- a/fixtures/diagnostics/errors/this_in_function.php +++ /dev/null @@ -1,6 +0,0 @@ -getFirstAncestor(Node\MethodDeclaration::class); - if ($method === null || $method->isStatic()) { + if ($method->isStatic()) { $this->diagnostics[] = new Diagnostic( - $method === null - ? "\$this can only be used in an object context." - : "\$this can not be used in static methods.", + "\$this can not be used in static methods.", Range::fromNode($node), null, DiagnosticSeverity::ERROR, diff --git a/tests/Diagnostics/InvalidThisUsageTest.php b/tests/Diagnostics/InvalidThisUsageTest.php index 7542918..b7e8196 100644 --- a/tests/Diagnostics/InvalidThisUsageTest.php +++ b/tests/Diagnostics/InvalidThisUsageTest.php @@ -71,42 +71,6 @@ class InvalidThisUsageTest extends TestCase ); } - public function testThisInFunctionProducesError() - { - $diagnostics = $this->collectDiagnostics( - __DIR__ . '/../../fixtures/diagnostics/errors/this_in_function.php' - ); - - $this->assertCount(1, $diagnostics); - $this->assertDiagnostic( - $diagnostics[0], - '$this can only be used in an object context.', - DiagnosticSeverity::ERROR, - new Range( - new Position(4, 11), - new Position(4, 16) - ) - ); - } - - public function testThisInRoot() - { - $diagnostics = $this->collectDiagnostics( - __DIR__ . '/../../fixtures/diagnostics/errors/this_in_root.php' - ); - - $this->assertCount(1, $diagnostics); - $this->assertDiagnostic( - $diagnostics[0], - '$this can only be used in an object context.', - DiagnosticSeverity::ERROR, - new Range( - new Position(2, 5), - new Position(2, 10) - ) - ); - } - public function testThisInMethodProducesNoError() { $diagnostics = $this->collectDiagnostics( From 09477b747efc254aedfd4485a79f084a7fc4904a Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 3 Dec 2017 15:49:43 -0800 Subject: [PATCH 088/117] fix(diagnostics): handle null case --- src/TreeAnalyzer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index c6bd6d7..465f5bb 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -99,7 +99,7 @@ class TreeAnalyzer // Find the first ancestor that's a class method. Return an error // if there is none, or if the method is static. $method = $node->getFirstAncestor(Node\MethodDeclaration::class); - if ($method->isStatic()) { + if ($method && $method->isStatic()) { $this->diagnostics[] = new Diagnostic( "\$this can not be used in static methods.", Range::fromNode($node), From 78316545a8158b24052721ca451f735552291ff4 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sun, 3 Dec 2017 16:23:14 -0800 Subject: [PATCH 089/117] ci(macos): try alternative method to download composer --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 81d6911..1301d4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,8 @@ jobs: - brew tap homebrew/homebrew-php - brew install php71 - brew install homebrew/php/php71-xdebug - - curl https://getcomposer.org/installer | php + - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" + - php composer-setup.php - ln -s "`pwd`/composer.phar" /usr/local/bin/composer - stage: release php: '7.0' From a40cf731f791788c9179d6771cb59f47122994ad Mon Sep 17 00:00:00 2001 From: phil-nelson Date: Sun, 10 Dec 2017 16:10:43 +1100 Subject: [PATCH 090/117] feat: Signature help (#547) closes #18 --- fixtures/signature_help/calls.php | 66 ++++++ src/Definition.php | 7 + src/DefinitionResolver.php | 13 ++ src/LanguageServer.php | 7 +- src/Protocol/ParameterInformation.php | 13 ++ src/Protocol/SignatureHelp.php | 14 ++ src/Protocol/SignatureInformation.php | 15 ++ src/Server/TextDocument.php | 25 ++- src/SignatureHelpProvider.php | 189 +++++++++++++++++ src/SignatureInformationFactory.php | 91 ++++++++ tests/LanguageServerTest.php | 5 +- .../Server/TextDocument/SignatureHelpTest.php | 199 ++++++++++++++++++ .../WithReturnTypehints.php.expected.json | 27 ++- ...embersShouldNotBeSymbols.php.expected.json | 3 +- ...rrayValueShouldBeBoolean.php.expected.json | 6 +- .../cases/caseStatement1.php.expected.json | 3 +- .../cases/classDefinition1.php.expected.json | 9 +- .../cases/classProperty1.php.expected.json | 21 +- .../cases/constants.php.expected.json | 13 +- .../cases/constants2.php.expected.json | 13 +- .../cases/constants3.php.expected.json | 13 +- .../cases/constants4.php.expected.json | 13 +- .../cases/constants5.php.expected.json | 9 +- ...tsInFunctionParamDefault.php.expected.json | 15 +- ...cksOnNamespaceDefinition.php.expected.json | 3 +- .../cases/exceptions1.php.expected.json | 3 +- .../cases/ifStatement1.php.expected.json | 3 +- .../cases/interfaceProperty.php.expected.json | 3 +- ...cConstantsShouldBeGlobal.php.expected.json | 3 +- .../cases/magicConsts.php.expected.json | 6 +- .../cases/memberAccess1.php.expected.json | 13 +- .../cases/memberAccess2.php.expected.json | 13 +- .../cases/memberAccess3.php.expected.json | 18 +- .../cases/memberAccess4.php.expected.json | 13 +- .../cases/memberAccess5.php.expected.json | 13 +- .../cases/memberCall1.php.expected.json | 18 +- .../cases/methodReturnType.php.expected.json | 10 +- .../multipleNamespaces.php.expected.json | 26 ++- ...ltiplePreceedingComments.php.expected.json | 10 +- .../cases/nameToken.php.expected.json | 10 +- .../cases/namespaces2.php.expected.json | 3 +- .../cases/namespaces5.php.expected.json | 3 +- .../cases/namespaces6.php.expected.json | 3 +- .../cases/namespaces8.php.expected.json | 3 +- .../cases/objectCreation.php.expected.json | 13 +- .../cases/objectCreation2.php.expected.json | 16 +- .../cases/objectCreation3.php.expected.json | 10 +- .../Validation/cases/param1.php.expected.json | 15 +- .../cases/parent1.php.expected.json | 23 +- .../cases/parent3.php.expected.json | 23 +- .../cases/propertyName1.php.expected.json | 6 +- .../cases/propertyName2.php.expected.json | 6 +- .../cases/returnType.php.expected.json | 15 +- .../scopedPropertyAccess.php.expected.json | 13 +- .../scopedPropertyAccess2.php.expected.json | 3 +- .../scopedPropertyAccess3.php.expected.json | 6 +- .../scopedPropertyAccess5.php.expected.json | 6 +- .../Validation/cases/self1.php.expected.json | 23 +- .../Validation/cases/self2.php.expected.json | 23 +- .../Validation/cases/self3.php.expected.json | 23 +- .../Validation/cases/self4.php.expected.json | 13 +- .../Validation/cases/self5.php.expected.json | 13 +- .../cases/static1.php.expected.json | 23 +- .../cases/static2.php.expected.json | 23 +- .../cases/static3.php.expected.json | 23 +- .../cases/static4.php.expected.json | 13 +- .../staticMethodReturnType.php.expected.json | 17 +- .../cases/stringVariable.php.expected.json | 13 +- ...edNameOutsideOfNamespace.php.expected.json | 3 +- ...rifyFqsenOnClassProperty.php.expected.json | 13 +- 70 files changed, 1183 insertions(+), 161 deletions(-) create mode 100644 fixtures/signature_help/calls.php create mode 100644 src/SignatureHelpProvider.php create mode 100644 src/SignatureInformationFactory.php create mode 100644 tests/Server/TextDocument/SignatureHelpTest.php diff --git a/fixtures/signature_help/calls.php b/fixtures/signature_help/calls.php new file mode 100644 index 0000000..312b4d2 --- /dev/null +++ b/fixtures/signature_help/calls.php @@ -0,0 +1,66 @@ +foo(); +$t->foo(1, +$t->foo(1,); +$t->baz(); + +foo( + 1, + foo(1, 2, +); + +Test::bar(); + +new $foo(); +new $foo(1, ); + +new NotExist(); diff --git a/src/Definition.php b/src/Definition.php index c27f871..0d157cb 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -99,6 +99,13 @@ class Definition */ public $documentation; + /** + * Signature information if this definition is for a FunctionLike, for use in textDocument/signatureHelp + * + * @var SignatureInformation + */ + public $signatureInformation; + /** * Yields the definitons of all ancestor classes (the Definition fqn is yielded as key) * diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index c9d1400..2c1f67d 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -7,6 +7,7 @@ use LanguageServer\Index\ReadableIndex; use LanguageServer\Protocol\SymbolInformation; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; +use Microsoft\PhpParser\FunctionLike; use phpDocumentor\Reflection\{ DocBlock, DocBlockFactory, Fqsen, Type, TypeResolver, Types }; @@ -34,6 +35,13 @@ class DefinitionResolver */ private $docBlockFactory; + /** + * Creates SignatureInformation + * + * @var SignatureInformationFactory + */ + private $signatureInformationFactory; + /** * @param ReadableIndex $index */ @@ -42,6 +50,7 @@ class DefinitionResolver $this->index = $index; $this->typeResolver = new TypeResolver; $this->docBlockFactory = DocBlockFactory::createInstance(); + $this->signatureInformationFactory = new SignatureInformationFactory($this); } /** @@ -232,6 +241,10 @@ class DefinitionResolver $def->documentation = $this->getDocumentationFromNode($node); } + if ($node instanceof FunctionLike) { + $def->signatureInformation = $this->signatureInformationFactory->create($node); + } + return $def; } diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 73560f4..46281f5 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -9,7 +9,8 @@ use LanguageServer\Protocol\{ TextDocumentSyncKind, Message, InitializeResult, - CompletionOptions + CompletionOptions, + SignatureHelpOptions }; use LanguageServer\FilesFinder\{FilesFinder, ClientFilesFinder, FileSystemFilesFinder}; use LanguageServer\ContentRetriever\{ContentRetriever, ClientContentRetriever, FileSystemContentRetriever}; @@ -275,6 +276,10 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $serverCapabilities->completionProvider = new CompletionOptions; $serverCapabilities->completionProvider->resolveProvider = false; $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; + + $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions(); + $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(', ',']; + // Support global references $serverCapabilities->xworkspaceReferencesProvider = true; $serverCapabilities->xdefinitionProvider = true; diff --git a/src/Protocol/ParameterInformation.php b/src/Protocol/ParameterInformation.php index 89b5e53..fa9b7bf 100644 --- a/src/Protocol/ParameterInformation.php +++ b/src/Protocol/ParameterInformation.php @@ -23,4 +23,17 @@ class ParameterInformation * @var string|null */ public $documentation; + + /** + * Create ParameterInformation + * + * @param string $label The label of this signature. Will be shown in the UI. + * @param string $documentation The human-readable doc-comment of this signature. Will be shown in the UI but can + * be omitted. + */ + public function __construct(string $label, string $documentation = null) + { + $this->label = $label; + $this->documentation = $documentation; + } } diff --git a/src/Protocol/SignatureHelp.php b/src/Protocol/SignatureHelp.php index 407b25a..f7aec6f 100644 --- a/src/Protocol/SignatureHelp.php +++ b/src/Protocol/SignatureHelp.php @@ -29,4 +29,18 @@ class SignatureHelp * @var int|null */ public $activeParameter; + + /** + * Create a SignatureHelp + * + * @param SignatureInformation[] $signatures List of signature information + * @param int|null $activeSignature The active signature, zero based + * @param int|null $activeParameter The active parameter, zero based + */ + public function __construct(array $signatures = [], $activeSignature = null, int $activeParameter = null) + { + $this->signatures = $signatures; + $this->activeSignature = $activeSignature; + $this->activeParameter = $activeParameter; + } } diff --git a/src/Protocol/SignatureInformation.php b/src/Protocol/SignatureInformation.php index 77e152c..d545f65 100644 --- a/src/Protocol/SignatureInformation.php +++ b/src/Protocol/SignatureInformation.php @@ -31,4 +31,19 @@ class SignatureInformation * @var ParameterInformation[]|null */ public $parameters; + + /** + * Create a SignatureInformation + * + * @param string $label The label of this signature. Will be shown in the UI. + * @param ParameterInformation[]|null The parameters of this signature + * @param string|null The human-readable doc-comment of this signature. Will be shown in the UI + * but can be omitted. + */ + public function __construct(string $label, array $parameters = null, string $documentation = null) + { + $this->label = $label; + $this->parameters = $parameters; + $this->documentation = $documentation; + } } diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 3209438..704ceb8 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -4,7 +4,7 @@ declare(strict_types = 1); namespace LanguageServer\Server; use LanguageServer\{ - CompletionProvider, LanguageClient, PhpDocument, PhpDocumentLoader, DefinitionResolver + CompletionProvider, SignatureHelpProvider, LanguageClient, PhpDocument, PhpDocumentLoader, DefinitionResolver }; use LanguageServer\Index\ReadableIndex; use LanguageServer\Protocol\{ @@ -59,6 +59,11 @@ class TextDocument */ protected $completionProvider; + /** + * @var SignatureHelpProvider + */ + protected $signatureHelpProvider; + /** * @var ReadableIndex */ @@ -94,6 +99,7 @@ class TextDocument $this->client = $client; $this->definitionResolver = $definitionResolver; $this->completionProvider = new CompletionProvider($this->definitionResolver, $index); + $this->signatureHelpProvider = new SignatureHelpProvider($this->definitionResolver, $index, $documentLoader); $this->index = $index; $this->composerJson = $composerJson; $this->composerLock = $composerLock; @@ -237,6 +243,23 @@ class TextDocument }); } + /** + * The signature help request is sent from the client to the server to request signature information at a given + * cursor position. + * + * @param TextDocumentIdentifier $textDocument The text document + * @param Position $position The position inside the text document + * + * @return Promise + */ + public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise + { + return coroutine(function () use ($textDocument, $position) { + $document = yield $this->documentLoader->getOrLoad($textDocument->uri); + return $this->signatureHelpProvider->getSignatureHelp($document, $position); + }); + } + /** * The goto definition request is sent from the client to the server to resolve the definition location of a symbol * at a given text document position. diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php new file mode 100644 index 0000000..aba02b4 --- /dev/null +++ b/src/SignatureHelpProvider.php @@ -0,0 +1,189 @@ +definitionResolver = $definitionResolver; + $this->index = $index; + $this->documentLoader = $documentLoader; + } + + /** + * Finds signature help for a callable position + * + * @param PhpDocument $doc The document the position belongs to + * @param Position $position The position to detect a call from + * + * @return Promise + */ + public function getSignatureHelp(PhpDocument $doc, Position $position): Promise + { + return coroutine(function () use ($doc, $position) { + // Find the node under the cursor + $node = $doc->getNodeAtPosition($position); + + // Find the definition of the item being called + list($def, $argumentExpressionList) = yield $this->getCallingInfo($node); + + if (!$def || !$def->signatureInformation) { + return new SignatureHelp(); + } + + // Find the active parameter + $activeParam = $argumentExpressionList + ? $this->findActiveParameter($argumentExpressionList, $position, $doc) + : 0; + + return new SignatureHelp([$def->signatureInformation], 0, $activeParam); + }); + } + + /** + * Given a node that could be a callable, finds the definition of the call and the argument expression list of + * the node + * + * @param Node $node The node to find calling information from + * + * @return Promise + */ + private function getCallingInfo(Node $node) + { + return coroutine(function () use ($node) { + $fqn = null; + $callingNode = null; + if ($node instanceof Node\DelimitedList\ArgumentExpressionList) { + // Cursor is already inside a ( + $argumentExpressionList = $node; + if ($node->parent instanceof Node\Expression\ObjectCreationExpression) { + // Constructing something + $callingNode = $node->parent->classTypeDesignator; + if (!$callingNode instanceof Node\QualifiedName) { + // We only support constructing from a QualifiedName + return null; + } + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($callingNode); + $fqn = "{$fqn}->__construct()"; + } else { + $callingNode = $node->parent->getFirstChildNode( + Node\Expression\MemberAccessExpression::class, + Node\Expression\ScopedPropertyAccessExpression::class, + Node\QualifiedName::class + ); + } + } elseif ($node instanceof Node\Expression\CallExpression) { + $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class); + $callingNode = $node->getFirstChildNode( + Node\Expression\MemberAccessExpression::class, + Node\Expression\ScopedPropertyAccessExpression::class, + Node\QualifiedName::class + ); + } elseif ($node instanceof Node\Expression\ObjectCreationExpression) { + $argumentExpressionList = $node->getFirstChildNode(Node\DelimitedList\ArgumentExpressionList::class); + $callingNode = $node->classTypeDesignator; + if (!$callingNode instanceof Node\QualifiedName) { + // We only support constructing from a QualifiedName + return null; + } + // Manually build the __construct fqn + $fqn = $this->definitionResolver->resolveReferenceNodeToFqn($callingNode); + $fqn = "{$fqn}->__construct()"; + } + + if (!$callingNode) { + return null; + } + + // Now find the definition of the call + $fqn = $fqn ?: DefinitionResolver::getDefinedFqn($callingNode); + while (true) { + if ($fqn) { + $def = $this->index->getDefinition($fqn); + } else { + $def = $this->definitionResolver->resolveReferenceNodeToDefinition($callingNode); + } + if ($def !== null || $this->index->isComplete()) { + break; + } + yield waitForEvent($this->index, 'definition-added'); + } + + if (!$def) { + return null; + } + + return [$def, $argumentExpressionList]; + }); + } + + /** + * Given a position and arguments, finds the "active" argument at the position + * + * @param Node\DelimitedList\ArgumentExpressionList $argumentExpressionList The argument expression list + * @param Position $position The position to detect the active argument from + * @param PhpDocument $doc The document that contains the expression + * + * @return int + */ + private function findActiveParameter( + Node\DelimitedList\ArgumentExpressionList $argumentExpressionList, + Position $position, + PhpDocument $doc + ): int { + $args = $argumentExpressionList->children; + $i = 0; + $found = null; + foreach ($args as $arg) { + if ($arg instanceof Node) { + $start = $arg->getFullStart(); + $end = $arg->getEndPosition(); + } else { + $start = $arg->fullStart; + $end = $start + $arg->length; + } + $offset = $position->toOffset($doc->getContent()); + if ($offset >= $start && $offset <= $end) { + $found = $i; + break; + } + if ($arg instanceof Node) { + ++$i; + } + } + if ($found === null) { + $found = $i; + } + return $found; + } +} diff --git a/src/SignatureInformationFactory.php b/src/SignatureInformationFactory.php new file mode 100644 index 0000000..6b8a1f0 --- /dev/null +++ b/src/SignatureInformationFactory.php @@ -0,0 +1,91 @@ +definitionResolver = $definitionResolver; + } + + /** + * Create a SignatureInformation from a FunctionLike node + * + * @param FunctionLike $node Node to create signature information from + * + * @return SignatureInformation + */ + public function create(FunctionLike $node): SignatureInformation + { + $params = $this->createParameters($node); + $label = $this->createLabel($params); + return new SignatureInformation( + $label, + $params, + $this->definitionResolver->getDocumentationFromNode($node) + ); + } + + /** + * Gets parameters from a FunctionLike node + * + * @param FunctionLike $node Node to get parameters from + * + * @return ParameterInformation[] + */ + private function createParameters(FunctionLike $node): array + { + $params = []; + if ($node->parameters) { + foreach ($node->parameters->getElements() as $element) { + $param = (string) $this->definitionResolver->getTypeFromNode($element); + $param .= ' '; + if ($element->dotDotDotToken) { + $param .= '...'; + } + $param .= '$' . $element->getName(); + if ($element->default) { + $param .= ' = ' . $element->default->getText(); + } + $params[] = new ParameterInformation( + $param, + $this->definitionResolver->getDocumentationFromNode($element) + ); + } + } + return $params; + } + + /** + * Creates a signature information label from parameters + * + * @param ParameterInformation[] $params Parameters to create the label from + * + * @return string + */ + private function createLabel(array $params): string + { + $label = '('; + if ($params) { + foreach ($params as $param) { + $label .= $param->label . ', '; + } + $label = substr($label, 0, -2); + } + $label .= ')'; + return $label; + } +} diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 5d58172..52963e6 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -14,7 +14,8 @@ use LanguageServer\Protocol\{ TextDocumentIdentifier, InitializeResult, ServerCapabilities, - CompletionOptions + CompletionOptions, + SignatureHelpOptions }; use AdvancedJsonRpc; use Webmozart\Glob\Glob; @@ -40,6 +41,8 @@ class LanguageServerTest extends TestCase $serverCapabilities->completionProvider = new CompletionOptions; $serverCapabilities->completionProvider->resolveProvider = false; $serverCapabilities->completionProvider->triggerCharacters = ['$', '>']; + $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions; + $serverCapabilities->signatureHelpProvider->triggerCharacters = ['(', ',']; $serverCapabilities->xworkspaceReferencesProvider = true; $serverCapabilities->xdefinitionProvider = true; $serverCapabilities->xdependenciesProvider = true; diff --git a/tests/Server/TextDocument/SignatureHelpTest.php b/tests/Server/TextDocument/SignatureHelpTest.php new file mode 100644 index 0000000..2004aae --- /dev/null +++ b/tests/Server/TextDocument/SignatureHelpTest.php @@ -0,0 +1,199 @@ +loader = new PhpDocumentLoader($contentRetriever, $projectIndex, $definitionResolver); + $this->textDocument = new Server\TextDocument($this->loader, $definitionResolver, $client, $projectIndex); + $index->setComplete(); + } + + /** + * @dataProvider signatureHelpProvider + */ + public function testSignatureHelp(Position $position, SignatureHelp $expectedSignature) + { + $callsUri = pathToUri(__DIR__ . '/../../../fixtures/signature_help/calls.php'); + $this->loader->open($callsUri, file_get_contents($callsUri)); + $signatureHelp = $this->textDocument->signatureHelp( + new TextDocumentIdentifier($callsUri), + $position + )->wait(); + $this->assertEquals($expectedSignature, $signatureHelp); + } + + public function signatureHelpProvider(): array + { + return [ + 'member call' => [ + new Position(50, 9), + new SignatureHelp( + [ + new SignatureInformation( + '(\\Foo\\SomethingElse $a, int|null $b = null)', + [ + new ParameterInformation('\\Foo\\SomethingElse $a', 'A param with a different doc type'), + new ParameterInformation('int|null $b = null', 'Param with default value'), + ], + 'Function doc' + ) + ], + 0, + 0 + ), + ], + 'member call 2nd param active' => [ + new Position(51, 12), + new SignatureHelp( + [ + new SignatureInformation( + '(\\Foo\\SomethingElse $a, int|null $b = null)', + [ + new ParameterInformation('\\Foo\\SomethingElse $a', 'A param with a different doc type'), + new ParameterInformation('int|null $b = null', 'Param with default value'), + ], + 'Function doc' + ) + ], + 0, + 1 + ), + ], + 'member call 2nd param active and closing )' => [ + new Position(52, 11), + new SignatureHelp( + [ + new SignatureInformation( + '(\\Foo\\SomethingElse $a, int|null $b = null)', + [ + new ParameterInformation('\\Foo\\SomethingElse $a', 'A param with a different doc type'), + new ParameterInformation('int|null $b = null', 'Param with default value'), + ], + 'Function doc' + ) + ], + 0, + 1 + ), + ], + 'method with no params' => [ + new Position(53, 9), + new SignatureHelp([new SignatureInformation('()', [], 'Method with no params', 0, 0)]), + ], + 'constructor' => [ + new Position(48, 14), + new SignatureHelp( + [ + new SignatureInformation( + '(string $first, int $second, \Foo\Test $third)', + [ + new ParameterInformation('string $first', 'First param'), + new ParameterInformation('int $second', 'Second param'), + new ParameterInformation('\Foo\Test $third', 'Third param with a longer description'), + ], + 'Constructor comment goes here' + ) + ], + 0, + 0 + ), + ], + 'constructor argument expression list' => [ + new Position(49, 16), + new SignatureHelp( + [ + new SignatureInformation( + '(string $first, int $second, \Foo\Test $third)', + [ + new ParameterInformation('string $first', 'First param'), + new ParameterInformation('int $second', 'Second param'), + new ParameterInformation('\Foo\Test $third', 'Third param with a longer description'), + ], + 'Constructor comment goes here' + ) + ], + 0, + 1 + ), + ], + 'global function' => [ + new Position(57, 15), + new SignatureHelp( + [ + new SignatureInformation( + '(int $i, bool $b = false, \Foo\Test|null ...$things = null)', + [ + new ParameterInformation('int $i', 'Global function param one'), + new ParameterInformation('bool $b = false', 'Default false param'), + new ParameterInformation('\Foo\Test|null ...$things = null', 'Test things'), + ] + ), + ], + 0, + 2 + ) + ], + 'static method' => [ + new Position(60, 10), + new SignatureHelp( + [new SignatureInformation('(mixed $a)', [new ParameterInformation('mixed $a')])], + 0, + 0 + ), + ], + 'no signature help' => [ + new Position(0, 0), + new SignatureHelp([]), + ], + 'construct from non fqn (not supported)' => [ + new Position(62, 9), + new SignatureHelp([]), + ], + 'construct from non fqn (not supported) argument expression' => [ + new Position(63, 11), + new SignatureHelp([]), + ], + 'invalid var' => [ + new Position(65, 13), + new SignatureHelp([]), + ], + ]; + } +} diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index 8c6b092..a037d6c 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -31,7 +31,8 @@ }, "type": null, "declarationLine": "namespace Fixtures\\Prophecy;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "Fixtures\\Prophecy\\WithReturnTypehints": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints", @@ -52,7 +53,8 @@ }, "type": null, "declarationLine": "class WithReturnTypehints extends EmptyClass", - "documentation": null + "documentation": null, + "signatureInformation": null }, "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getSelf()", @@ -72,7 +74,12 @@ "type__tostring": "\\self", "type": {}, "declarationLine": "public function getSelf(): self {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "Fixtures\\Prophecy\\WithReturnTypehints->getName()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getName()", @@ -92,7 +99,12 @@ "type__tostring": "string", "type": {}, "declarationLine": "public function getName(): string {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "Fixtures\\Prophecy\\WithReturnTypehints->getParent()": { "fqn": "Fixtures\\Prophecy\\WithReturnTypehints->getParent()", @@ -112,7 +124,12 @@ "type__tostring": "\\parent", "type": {}, "declarationLine": "public function getParent(): parent {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json index 51343f1..f5fbba9 100644 --- a/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json +++ b/tests/Validation/cases/anonymousClassMembersShouldNotBeSymbols.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json index 107877e..1f40635 100644 --- a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json +++ b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "A->foo": { "fqn": "A->foo", @@ -38,7 +39,8 @@ "type__tostring": "string[]", "type": {}, "declarationLine": "protected $foo;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/caseStatement1.php.expected.json b/tests/Validation/cases/caseStatement1.php.expected.json index 9746749..4b2f07b 100644 --- a/tests/Validation/cases/caseStatement1.php.expected.json +++ b/tests/Validation/cases/caseStatement1.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/classDefinition1.php.expected.json b/tests/Validation/cases/classDefinition1.php.expected.json index 670b5de..211a12f 100644 --- a/tests/Validation/cases/classDefinition1.php.expected.json +++ b/tests/Validation/cases/classDefinition1.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace TestNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "TestNamespace\\A": { "fqn": "TestNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "TestNamespace\\A->a": { "fqn": "TestNamespace\\A->a", @@ -64,7 +66,8 @@ "type__tostring": "int", "type": {}, "declarationLine": "public $a;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/classProperty1.php.expected.json b/tests/Validation/cases/classProperty1.php.expected.json index 921bf0b..f5ae22a 100644 --- a/tests/Validation/cases/classProperty1.php.expected.json +++ b/tests/Validation/cases/classProperty1.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace TestNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "TestNamespace\\TestClass": { "fqn": "TestNamespace\\TestClass", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class TestClass", - "documentation": null + "documentation": null, + "signatureInformation": null }, "TestNamespace\\TestClass->testProperty": { "fqn": "TestNamespace\\TestClass->testProperty", @@ -64,7 +66,8 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public $testProperty;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "TestNamespace\\TestClass->testMethod()": { "fqn": "TestNamespace\\TestClass->testMethod()", @@ -84,7 +87,17 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function testMethod($testParameter)", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "(mixed $testParameter)", + "documentation": null, + "parameters": [ + { + "label": "mixed $testParameter", + "documentation": null + } + ] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/constants.php.expected.json b/tests/Validation/cases/constants.php.expected.json index a76c059..3fcc367 100644 --- a/tests/Validation/cases/constants.php.expected.json +++ b/tests/Validation/cases/constants.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/constants2.php.expected.json b/tests/Validation/cases/constants2.php.expected.json index ae5a2ce..0837b67 100644 --- a/tests/Validation/cases/constants2.php.expected.json +++ b/tests/Validation/cases/constants2.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/constants3.php.expected.json b/tests/Validation/cases/constants3.php.expected.json index c6ad922..4b780a0 100644 --- a/tests/Validation/cases/constants3.php.expected.json +++ b/tests/Validation/cases/constants3.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/constants4.php.expected.json b/tests/Validation/cases/constants4.php.expected.json index bc46cf1..1f2bcd5 100644 --- a/tests/Validation/cases/constants4.php.expected.json +++ b/tests/Validation/cases/constants4.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->suite()": { "fqn": "MyNamespace\\A->suite()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function suite()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/constants5.php.expected.json b/tests/Validation/cases/constants5.php.expected.json index 6bd7b8e..6b93043 100644 --- a/tests/Validation/cases/constants5.php.expected.json +++ b/tests/Validation/cases/constants5.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\Mbstring": { "fqn": "MyNamespace\\Mbstring", @@ -41,7 +42,8 @@ }, "type": null, "declarationLine": "class Mbstring", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\Mbstring::MB_CASE_FOLD": { "fqn": "MyNamespace\\Mbstring::MB_CASE_FOLD", @@ -61,7 +63,8 @@ "type__tostring": "\\MyNamespace\\PHP_INT_MAX", "type": {}, "declarationLine": "const MB_CASE_FOLD = PHP_INT_MAX;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json index b49f5a9..0a34c36 100644 --- a/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json +++ b/tests/Validation/cases/constantsInFunctionParamDefault.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "interface A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "A->b()": { "fqn": "A->b()", @@ -42,7 +43,17 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b ($a = MY_CONSTANT);", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "(\\MY_CONSTANT $a = MY_CONSTANT)", + "documentation": null, + "parameters": [ + { + "label": "\\MY_CONSTANT $a = MY_CONSTANT", + "documentation": null + } + ] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json index dd1737a..27f5df9 100644 --- a/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json +++ b/tests/Validation/cases/docBlocksOnNamespaceDefinition.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/exceptions1.php.expected.json b/tests/Validation/cases/exceptions1.php.expected.json index c1ee1bd..2dc57b3 100644 --- a/tests/Validation/cases/exceptions1.php.expected.json +++ b/tests/Validation/cases/exceptions1.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/ifStatement1.php.expected.json b/tests/Validation/cases/ifStatement1.php.expected.json index 98e6572..e422270 100644 --- a/tests/Validation/cases/ifStatement1.php.expected.json +++ b/tests/Validation/cases/ifStatement1.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/interfaceProperty.php.expected.json b/tests/Validation/cases/interfaceProperty.php.expected.json index 10c49f0..d22d028 100644 --- a/tests/Validation/cases/interfaceProperty.php.expected.json +++ b/tests/Validation/cases/interfaceProperty.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "interface A {", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json index a9c2162..5b04b54 100644 --- a/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json +++ b/tests/Validation/cases/magicConstantsShouldBeGlobal.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace B;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json index 6f863b9..545f1cb 100644 --- a/tests/Validation/cases/magicConsts.php.expected.json +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "A::$deprecationsTriggered": { "fqn": "A::$deprecationsTriggered", @@ -42,7 +43,8 @@ "type__tostring": "\\__CLASS__[]", "type": {}, "declarationLine": "private static $deprecationsTriggered;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess1.php.expected.json b/tests/Validation/cases/memberAccess1.php.expected.json index c039e5e..f0286c3 100644 --- a/tests/Validation/cases/memberAccess1.php.expected.json +++ b/tests/Validation/cases/memberAccess1.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "static function a() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess2.php.expected.json b/tests/Validation/cases/memberAccess2.php.expected.json index 50902a1..6060e8e 100644 --- a/tests/Validation/cases/memberAccess2.php.expected.json +++ b/tests/Validation/cases/memberAccess2.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "static function a() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess3.php.expected.json b/tests/Validation/cases/memberAccess3.php.expected.json index d91b27d..b0af5aa 100644 --- a/tests/Validation/cases/memberAccess3.php.expected.json +++ b/tests/Validation/cases/memberAccess3.php.expected.json @@ -40,7 +40,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -59,7 +60,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::getInitializer()": { "fqn": "MyNamespace\\A::getInitializer()", @@ -79,7 +81,17 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public static function getInitializer(ClassLoader $loader)", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "(\\MyNamespace\\ClassLoader $loader)", + "documentation": null, + "parameters": [ + { + "label": "\\MyNamespace\\ClassLoader $loader", + "documentation": null + } + ] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess4.php.expected.json b/tests/Validation/cases/memberAccess4.php.expected.json index 0b7e1bf..951cacd 100644 --- a/tests/Validation/cases/memberAccess4.php.expected.json +++ b/tests/Validation/cases/memberAccess4.php.expected.json @@ -31,7 +31,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -50,7 +51,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->testRequest()": { "fqn": "MyNamespace\\A->testRequest()", @@ -70,7 +72,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function testRequest()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/memberAccess5.php.expected.json b/tests/Validation/cases/memberAccess5.php.expected.json index 050cf3b..d4e9104 100644 --- a/tests/Validation/cases/memberAccess5.php.expected.json +++ b/tests/Validation/cases/memberAccess5.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\ParseErrorsTest": { "fqn": "MyNamespace\\ParseErrorsTest", @@ -41,7 +42,8 @@ }, "type": null, "declarationLine": "class ParseErrorsTest {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\ParseErrorsTest->setUp()": { "fqn": "MyNamespace\\ParseErrorsTest->setUp()", @@ -61,7 +63,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function setUp()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/memberCall1.php.expected.json b/tests/Validation/cases/memberCall1.php.expected.json index d02d7e3..50e91d1 100644 --- a/tests/Validation/cases/memberCall1.php.expected.json +++ b/tests/Validation/cases/memberCall1.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\ParseErrorsTest": { "fqn": "MyNamespace\\ParseErrorsTest", @@ -47,7 +48,8 @@ }, "type": null, "declarationLine": "class ParseErrorsTest", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\ParseErrorsTest->setAccount()": { "fqn": "MyNamespace\\ParseErrorsTest->setAccount()", @@ -67,7 +69,17 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function setAccount(AccountInterface $account)", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "(\\MyNamespace\\AccountInterface $account)", + "documentation": null, + "parameters": [ + { + "label": "\\MyNamespace\\AccountInterface $account", + "documentation": null + } + ] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/methodReturnType.php.expected.json b/tests/Validation/cases/methodReturnType.php.expected.json index 2c89994..0d537c5 100644 --- a/tests/Validation/cases/methodReturnType.php.expected.json +++ b/tests/Validation/cases/methodReturnType.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "class FooClass {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "FooClass->foo()": { "fqn": "FooClass->foo()", @@ -42,7 +43,12 @@ "type__tostring": "\\FooClass", "type": {}, "declarationLine": "public function foo(): FooClass {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/multipleNamespaces.php.expected.json b/tests/Validation/cases/multipleNamespaces.php.expected.json index fa51f2d..c308cf9 100644 --- a/tests/Validation/cases/multipleNamespaces.php.expected.json +++ b/tests/Validation/cases/multipleNamespaces.php.expected.json @@ -31,7 +31,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace1;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace1\\B": { "fqn": "MyNamespace1\\B", @@ -50,7 +51,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace1\\B->b()": { "fqn": "MyNamespace1\\B->b()", @@ -70,7 +72,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace2": { "fqn": "MyNamespace2", @@ -89,7 +96,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace2;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace2\\A": { "fqn": "MyNamespace2\\A", @@ -110,7 +118,8 @@ }, "type": null, "declarationLine": "class A extends MyNamespace1\\B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace2\\A->a()": { "fqn": "MyNamespace2\\A->a()", @@ -130,7 +139,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/multiplePreceedingComments.php.expected.json b/tests/Validation/cases/multiplePreceedingComments.php.expected.json index 96cbb4a..2bca2b6 100644 --- a/tests/Validation/cases/multiplePreceedingComments.php.expected.json +++ b/tests/Validation/cases/multiplePreceedingComments.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "class Foo", - "documentation": null + "documentation": null, + "signatureInformation": null }, "Foo->fn()": { "fqn": "Foo->fn()", @@ -38,7 +39,12 @@ "type__tostring": "\\Iterator", "type": {}, "declarationLine": "public function fn()", - "documentation": "Foo" + "documentation": "Foo", + "signatureInformation": { + "label": "()", + "documentation": "Foo", + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/nameToken.php.expected.json b/tests/Validation/cases/nameToken.php.expected.json index 37cb4ca..c52d483 100644 --- a/tests/Validation/cases/nameToken.php.expected.json +++ b/tests/Validation/cases/nameToken.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "A->b()": { "fqn": "A->b()", @@ -42,7 +43,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/namespaces2.php.expected.json b/tests/Validation/cases/namespaces2.php.expected.json index 0dffc9f..cd0ade5 100644 --- a/tests/Validation/cases/namespaces2.php.expected.json +++ b/tests/Validation/cases/namespaces2.php.expected.json @@ -31,7 +31,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace1;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/namespaces5.php.expected.json b/tests/Validation/cases/namespaces5.php.expected.json index e609ca2..7ad96f3 100644 --- a/tests/Validation/cases/namespaces5.php.expected.json +++ b/tests/Validation/cases/namespaces5.php.expected.json @@ -40,7 +40,8 @@ }, "type": null, "declarationLine": "namespace B;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/namespaces6.php.expected.json b/tests/Validation/cases/namespaces6.php.expected.json index bf5a045..6c559a3 100644 --- a/tests/Validation/cases/namespaces6.php.expected.json +++ b/tests/Validation/cases/namespaces6.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "namespace A \\ B;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/namespaces8.php.expected.json b/tests/Validation/cases/namespaces8.php.expected.json index d303647..95c5019 100644 --- a/tests/Validation/cases/namespaces8.php.expected.json +++ b/tests/Validation/cases/namespaces8.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace LanguageServer\\Tests\\Utils;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/objectCreation.php.expected.json b/tests/Validation/cases/objectCreation.php.expected.json index 8fec38f..90ad0f5 100644 --- a/tests/Validation/cases/objectCreation.php.expected.json +++ b/tests/Validation/cases/objectCreation.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -41,7 +42,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -61,7 +63,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/objectCreation2.php.expected.json b/tests/Validation/cases/objectCreation2.php.expected.json index e546528..b6f69a3 100644 --- a/tests/Validation/cases/objectCreation2.php.expected.json +++ b/tests/Validation/cases/objectCreation2.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -63,7 +65,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -83,7 +86,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/objectCreation3.php.expected.json b/tests/Validation/cases/objectCreation3.php.expected.json index d2d3e2f..a5f389b 100644 --- a/tests/Validation/cases/objectCreation3.php.expected.json +++ b/tests/Validation/cases/objectCreation3.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "A->a()": { "fqn": "A->a()", @@ -42,7 +43,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/param1.php.expected.json b/tests/Validation/cases/param1.php.expected.json index 40cfbd1..dd7c2a6 100644 --- a/tests/Validation/cases/param1.php.expected.json +++ b/tests/Validation/cases/param1.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\init()": { "fqn": "MyNamespace\\init()", @@ -42,7 +43,17 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function init(Hi $view)", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "(\\MyNamespace\\Hi $view)", + "documentation": null, + "parameters": [ + { + "label": "\\MyNamespace\\Hi $view", + "documentation": null + } + ] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/parent1.php.expected.json b/tests/Validation/cases/parent1.php.expected.json index 56d7e61..661f631 100644 --- a/tests/Validation/cases/parent1.php.expected.json +++ b/tests/Validation/cases/parent1.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -41,7 +42,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -61,7 +63,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -82,7 +89,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -102,7 +110,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/parent3.php.expected.json b/tests/Validation/cases/parent3.php.expected.json index 3954754..9ad39f8 100644 --- a/tests/Validation/cases/parent3.php.expected.json +++ b/tests/Validation/cases/parent3.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -85,7 +92,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -105,7 +113,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/propertyName1.php.expected.json b/tests/Validation/cases/propertyName1.php.expected.json index cc31ed6..d43cabf 100644 --- a/tests/Validation/cases/propertyName1.php.expected.json +++ b/tests/Validation/cases/propertyName1.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "class MyClass", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyClass->mainPropertyName": { "fqn": "MyClass->mainPropertyName", @@ -38,7 +39,8 @@ "type__tostring": "string", "type": {}, "declarationLine": "protected $mainPropertyName;", - "documentation": "The name of the main property, or NULL if there is none." + "documentation": "The name of the main property, or NULL if there is none.", + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/propertyName2.php.expected.json b/tests/Validation/cases/propertyName2.php.expected.json index d4efb42..4b5bd9b 100644 --- a/tests/Validation/cases/propertyName2.php.expected.json +++ b/tests/Validation/cases/propertyName2.php.expected.json @@ -18,7 +18,8 @@ }, "type": null, "declarationLine": "class MyClass", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyClass->mainPropertyName": { "fqn": "MyClass->mainPropertyName", @@ -38,7 +39,8 @@ "type__tostring": "string", "type": {}, "declarationLine": "protected $mainPropertyName;", - "documentation": "The name of the main property, or NULL if there is none." + "documentation": "The name of the main property, or NULL if there is none.", + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/returnType.php.expected.json b/tests/Validation/cases/returnType.php.expected.json index 20cdadf..17a4802 100644 --- a/tests/Validation/cases/returnType.php.expected.json +++ b/tests/Validation/cases/returnType.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace TestNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "TestNamespace\\whatever()": { "fqn": "TestNamespace\\whatever()", @@ -45,7 +46,17 @@ "type__tostring": "\\TestNamespace\\TestClass", "type": {}, "declarationLine": "function whatever(TestClass $param): TestClass2 {", - "documentation": "Aute duis elit reprehenderit tempor cillum proident anim laborum eu laboris reprehenderit ea incididunt." + "documentation": "Aute duis elit reprehenderit tempor cillum proident anim laborum eu laboris reprehenderit ea incididunt.", + "signatureInformation": { + "label": "(\\TestNamespace\\TestClass $param)", + "documentation": "Aute duis elit reprehenderit tempor cillum proident anim laborum eu laboris reprehenderit ea incididunt.", + "parameters": [ + { + "label": "\\TestNamespace\\TestClass $param", + "documentation": "Adipisicing non non cillum sint incididunt cillum enim mollit." + } + ] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/scopedPropertyAccess.php.expected.json b/tests/Validation/cases/scopedPropertyAccess.php.expected.json index ec50c7a..6020ee8 100644 --- a/tests/Validation/cases/scopedPropertyAccess.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -44,7 +45,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::a()": { "fqn": "MyNamespace\\A::a()", @@ -64,7 +66,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "static function a() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json index e1712cd..664d790 100644 --- a/tests/Validation/cases/scopedPropertyAccess2.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess2.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json index 913721b..54ca5f1 100644 --- a/tests/Validation/cases/scopedPropertyAccess3.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess3.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "class A {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "A::$a": { "fqn": "A::$a", @@ -45,7 +46,8 @@ "type__tostring": "string", "type": {}, "declarationLine": "static $a;", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json index 7e56123..ee944ed 100644 --- a/tests/Validation/cases/scopedPropertyAccess5.php.expected.json +++ b/tests/Validation/cases/scopedPropertyAccess5.php.expected.json @@ -31,7 +31,8 @@ }, "type": null, "declarationLine": "class TestClass implements TestInterface {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "TestClass::$testProperty": { "fqn": "TestClass::$testProperty", @@ -51,7 +52,8 @@ "type__tostring": "\\TestClass[]", "type": {}, "declarationLine": "public static $testProperty;", - "documentation": "Lorem excepteur officia sit anim velit veniam enim." + "documentation": "Lorem excepteur officia sit anim velit veniam enim.", + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/self1.php.expected.json b/tests/Validation/cases/self1.php.expected.json index fe2bc63..899df1c 100644 --- a/tests/Validation/cases/self1.php.expected.json +++ b/tests/Validation/cases/self1.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -47,7 +48,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -67,7 +69,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -88,7 +95,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -108,7 +116,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/self2.php.expected.json b/tests/Validation/cases/self2.php.expected.json index 60ab062..1c1ef87 100644 --- a/tests/Validation/cases/self2.php.expected.json +++ b/tests/Validation/cases/self2.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -47,7 +48,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -67,7 +69,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -88,7 +95,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -108,7 +116,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/self3.php.expected.json b/tests/Validation/cases/self3.php.expected.json index 7d66c2b..0a43146 100644 --- a/tests/Validation/cases/self3.php.expected.json +++ b/tests/Validation/cases/self3.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -47,7 +48,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -67,7 +69,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -88,7 +95,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -108,7 +116,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/self4.php.expected.json b/tests/Validation/cases/self4.php.expected.json index 64765b7..b925fdd 100644 --- a/tests/Validation/cases/self4.php.expected.json +++ b/tests/Validation/cases/self4.php.expected.json @@ -37,7 +37,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -56,7 +57,8 @@ }, "type": null, "declarationLine": "class A", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A::suite()": { "fqn": "MyNamespace\\A::suite()", @@ -76,7 +78,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public static function suite()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/self5.php.expected.json b/tests/Validation/cases/self5.php.expected.json index eb41832..10ba7fb 100644 --- a/tests/Validation/cases/self5.php.expected.json +++ b/tests/Validation/cases/self5.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -41,7 +42,8 @@ }, "type": null, "declarationLine": "class A", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->typesProvider()": { "fqn": "MyNamespace\\A->typesProvider()", @@ -61,7 +63,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function typesProvider()", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/static1.php.expected.json b/tests/Validation/cases/static1.php.expected.json index db1a0d2..c71c7c2 100644 --- a/tests/Validation/cases/static1.php.expected.json +++ b/tests/Validation/cases/static1.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -47,7 +48,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -67,7 +69,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -88,7 +95,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -108,7 +116,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/static2.php.expected.json b/tests/Validation/cases/static2.php.expected.json index bb662cb..5b5a706 100644 --- a/tests/Validation/cases/static2.php.expected.json +++ b/tests/Validation/cases/static2.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -47,7 +48,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -67,7 +69,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -88,7 +95,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -108,7 +116,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/static3.php.expected.json b/tests/Validation/cases/static3.php.expected.json index 34d3851..a88f554 100644 --- a/tests/Validation/cases/static3.php.expected.json +++ b/tests/Validation/cases/static3.php.expected.json @@ -28,7 +28,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B": { "fqn": "MyNamespace\\B", @@ -47,7 +48,8 @@ }, "type": null, "declarationLine": "class B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\B->b()": { "fqn": "MyNamespace\\B->b()", @@ -67,7 +69,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function b() {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -88,7 +95,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -108,7 +116,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/static4.php.expected.json b/tests/Validation/cases/static4.php.expected.json index de71d41..adc75a9 100644 --- a/tests/Validation/cases/static4.php.expected.json +++ b/tests/Validation/cases/static4.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "namespace MyNamespace;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A": { "fqn": "MyNamespace\\A", @@ -46,7 +47,8 @@ }, "type": null, "declarationLine": "class A extends B {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "MyNamespace\\A->a()": { "fqn": "MyNamespace\\A->a()", @@ -66,7 +68,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/staticMethodReturnType.php.expected.json b/tests/Validation/cases/staticMethodReturnType.php.expected.json index e6661ec..8de5673 100644 --- a/tests/Validation/cases/staticMethodReturnType.php.expected.json +++ b/tests/Validation/cases/staticMethodReturnType.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "class FooClass {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "FooClass::staticFoo()": { "fqn": "FooClass::staticFoo()", @@ -42,7 +43,12 @@ "type__tostring": "\\FooClass", "type": {}, "declarationLine": "public static function staticFoo(): FooClass {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } }, "FooClass->bar()": { "fqn": "FooClass->bar()", @@ -62,7 +68,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function bar() { }", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/stringVariable.php.expected.json b/tests/Validation/cases/stringVariable.php.expected.json index 1d4c7e3..6372c89 100644 --- a/tests/Validation/cases/stringVariable.php.expected.json +++ b/tests/Validation/cases/stringVariable.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "class B", - "documentation": null + "documentation": null, + "signatureInformation": null }, "B->hi": { "fqn": "B->hi", @@ -42,7 +43,8 @@ "type__tostring": "int", "type": {}, "declarationLine": "public $hi;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "B->a()": { "fqn": "B->a()", @@ -62,7 +64,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "function a () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file diff --git a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json index 509907c..7966afb 100644 --- a/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json +++ b/tests/Validation/cases/testQualifiedNameOutsideOfNamespace.php.expected.json @@ -22,7 +22,8 @@ }, "type": null, "declarationLine": "namespace SomeNamespace { }", - "documentation": null + "documentation": null, + "signatureInformation": null } } } \ No newline at end of file diff --git a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json index 662a7ed..a434cf2 100644 --- a/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json +++ b/tests/Validation/cases/verifyFqsenOnClassProperty.php.expected.json @@ -25,7 +25,8 @@ }, "type": null, "declarationLine": "class Foo {", - "documentation": null + "documentation": null, + "signatureInformation": null }, "Foo->bar": { "fqn": "Foo->bar", @@ -45,7 +46,8 @@ "type__tostring": "\\", "type": {}, "declarationLine": "protected $bar;", - "documentation": null + "documentation": null, + "signatureInformation": null }, "Foo->foo()": { "fqn": "Foo->foo()", @@ -65,7 +67,12 @@ "type__tostring": "mixed", "type": {}, "declarationLine": "public function foo () {", - "documentation": null + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } } } } \ No newline at end of file From 6d0a7ba7dfd1c8ee62618fa386c768c37d50aba5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 9 Dec 2017 21:40:01 -0800 Subject: [PATCH 091/117] docs: add signatureHelp demo --- README.md | 6 ++++++ images/signatureHelp.gif | Bin 0 -> 324136 bytes 2 files changed, 6 insertions(+) create mode 100644 images/signatureHelp.gif diff --git a/README.md b/README.md index b25680e..d3a4d46 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ and an [event loop](http://sabre.io/event/loop/) for concurrency. ## Features +### [Completion](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_completion) +![Completion search demo](images/completion.gif) + +### [Signature Help](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_signatureHelp) +![Signature help demo](images/signatureHelp.gif) + ### [Go To Definition](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#goto-definition-request) ![Go To Definition demo](images/definition.gif) diff --git a/images/signatureHelp.gif b/images/signatureHelp.gif new file mode 100644 index 0000000000000000000000000000000000000000..9f921ff67cbbbf07c6a6c35c8293748f69ad2eee GIT binary patch literal 324136 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�D7 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ΠYq^A2-SyCC;#6(3=q85;oVOHE8aBb4D*eH9!^Fkv4%L14>k z^TUB+qm-hGNwznGRx!)R6(1uX$siQL*UFB2KP|$o956sLn|T<;j`z5nN-Jjjd4P+10HylASk?`1!Zzy_Mgmko!7QVgjHpXRx0o0;`p*e8J;81jioSf5M?4JACsrL3Kukvs>2ehz#gJJP zoD8Vpjc(-0p&y5S#5kSfx+15+64w=ii+-xC(*fg$Tn5QbOWIC`u)An4=B#3E3d(>` z7A+~EMc z0>}UYFrgLU4vcuyty1`CR~a4kRy&%X$R*ZrE=X;lcC@M>~FVHZg z2#?odyhxm!SdM>XZt_UG@qs6QEL{esvnM^GDFcfTra$;kdIktit0#kBk0p%>C5VuR zv%6WP*&u~~rSxzuE4LaLqzy?Z^+~ZqI)YM6KF0Ae&)@T z?}B`agW)Z60~`$)SprRXp~IiVqEM%ULWlsZU*_%_(TS` zepF8;L>uzR$Q6Fork&OPHZNyMq5fRVPk14+EbM4nG{(y9!U~2@zQ&_Io7{o_7@C<( z+H1slKJWT863OpE__gkjS*@(h3Yw~%YS5% z_dh5W5r-(n=Z)pCw$g)6hQil}8RkdaBdkK57*oc<;_HvGIW{4D=OtttVh^?s9qVD_ zym7wQ*iKg?LvD9!|xfx;(Ez5Fvi4rF=EhCCw$|81t6bYPoXIQW4=` z;}4rA3|9V$PBoMLh?X-{gTq*C)RNm6)?SCm_0M+Gk%I2LD6wPWd(;|Z@-{0gFt0Po zX_5k+ZdFt7dXGRg@0Xsa?OGgYHa2KDPHd|!^199O)QuL3bhhAPDG0N(ZbLeN&Gi(f zIlWi5&CZx`99tm>F3A^0Ce6%u$Us#3C>Z1s+mtEX5g$~5L>h-0UoelNk)2!opv>;R zq<^u_@s61DmNpBl%&rp@9}dX?BkNQ7<6OdH-dY-nuDte`5?JEN|ISEK-7^g0<&k$MCNe>PNS++AT`Q_y37DLIGa?6ZGP|&^DdU$cdgH5c% zlQGxJzA&dh4!maiOb(ck=V9q0@Q7v$d8C8XO6>sV*38+P`uEC`94zLCbVcsB8Lk9% zZMaE3vcZTipZl8>ygzNxgI>`qI6Q26WS?DO^ejd`rRrPykJ0(1%%X7}4xDe{JwOJZ zd|A6|;2rQG>CN7>s?pi|FNHMXvrIm*$$jzr#7_FrHM9QBIcb+Nx9((B z6=R~c11IWT-V(6HjJmlM<$bq{U@$4~a*q4*St$om-6%M1RU}1MV};cY}AdYkTPqabS{vHLu4ec zc0J8cm|R}L6hB`UK^T}fm!L&dL=M$E)ftU0tBD2KMKzcCO`b`$6hg(=x?^!o27AdH zMCBKd*|#$S?S(c|t9SgUrRHx!t#x`?_8A9H1Yagg`br}M6wz1kquiQN#&HXJQ)Q!V zfw~F8*Z7SkRt#o%J(|tJF~MQ*h%B&y{Pts}x(1F{&`M^hWaYMRJ-*mRwGO8|-~kTX zon+L%-Sc%HA~3`nTM1~4G{rk)d~+LpI6(FVQ~c}9EW$VO)wB+m8Zij&ET$}0l%?Ty zocdP&%maGf50>mMk$0iX$V&cy_aPT6B{g6`F$uJzqYG1u;&mT_nudm5goc@uFkBT60 zi8&KwiL#wqu~ZPj)mNa|WvVGVXmhI*k+bMlg48!u7GcAg7=L4VUPNis;j9v5yX@qV}oytPH&M z*|jv76ay4G*Rv(!B#RkFZPru3$iLnW4RoYeba`O5&Txo?N_|fvpx;M$N= z!}HmXn6vKmOV0mRh~b0^-yRpa4Hi1W49vq% z$*h(Q%%0;pVa%1-1e}t(0fnaSm45Pfs&q00+(qAcwl-zXVaDLyJM%zVH}JEwl3ZOp z9MvvRCoW+5X_YQKrOzCzi0jiDacSp&-)n7_}JN9 zW>va&plvVjGqQQHBM1nvrkt#*Q}i{?Lcv_M~K7wTu(yclmT1w5jfHSifD? z@(%l1R6dPecZ}9e-PD)9kBedGwbRUzHepTkjFd-clV8GEb-UTSBi%Gr`Xn=s6)n3PCT;%Z9R>rjk~#~O|8=NhL+nej{Bd()sSYwGW5{<;X8 zpkRva^`>)l2p1M??Bm9_miQBtm%t6zi#`b@&ns}Dy@55*EdJqx)bMvqC`LsN!vv4x z^AC|DwX^yO5A!GLKm@6d&nJv&%&Eaxy_y$MpJ{B%nulW~zR5hGOEZ~KV{L$RWP_cN zvmPQ^9yUC&j_JBi47Arh@2xSJ!QvT-V-eR z(ay4fo%}&Rxb5zo(X8Y#=H<*TjCaHH$-{&R{P>9b1v%J4TRv~8SObG@__-($YlZuH zr0)Q3g1}GnF-&9fcf+Fx;Mv+zb2J4iFVa!Mknu>L@8J!jndoCn^ z5#!Lxu-T#8k(>_wprW}PKUSnDGWwU@kV(RmnsufFw z{$m@cqnaOz9YHCVuY0DQNjuuW_6$6mj?BKy5}(wq)p7atxEEZl_kBEvM#+3tQ2)ZX z6HhLETcNE{zmD$x4-Ef|sowP8+p|dI!l!ENAy=J{DIP_6Mj{hN@$LsUi9rY+e2cCJ z(?VowV`#sD(XGaWbxq>y)acfEO`IiLtM!NTTK4;wo{q61lLz;m{?HfqKX)#SY=*@9 z<^8R+LkXmO$mfOy++Pm|PS&!>SD*cUe5sDVh0e3}3yK#7-4vVu3A(@da&RBik(#@~ zX!aGx37fPY7jFb%9!j(*w;{hw@ANQ)wa@%GycI``Nh3hLfv zElMGVe|-%#bIsXVM*{CAb1x2C@T9D)c}0u-J8Q(UglpIKmImIrB?mIf7}ymf^L{uK zNc&Z#A$9cjn}-rhbw#T(+xrn-6^;nwrlez7z}zUuIDMNc-@GiJnt;d1d*Gi(I!85; zlZrNVv7aM4LIgZZim1ugi2Rz;C=BhI`~@McFD=4YcI{dUR8joeUwVJ@sw#1f^6RJx zt+qG)z9*wqR##x?(AClC_tqf%gO8_YU=bytZxjm7$ zG_*+(Sk)1d?k6yEco!wODWZ3Aqv7;^RM5mNr-5Kft_w-X)NB09k9Er!1|c)wbvu6! zyRVpcTYe{%UFN~kP}fmslm0G?w=Y?pCUS2WyDcLF1&CCwo+P@hVyHgen%N&kbz3KJ z&1Gy7@*KZLRsb9-JSHsv{Ee(s@mQ&7+4tDxe9a)9%WBpWzYzuO^ka4D_oeL2r~%lO&3vCDnK5z>P!<_gV2Hg;{BS2u8u zUbO$<=Gx*X=H63z7j)6{bIe4)wQcV2*hlZzXn}VhvRqD0J{?7n>eC0W@e`;?8RrLL z6b6r_o{+XL^nG*^@n|jaRH!>?9)a^=4dCIueDNL6ydDbfM?SDdfd5pjPV#q+1 zG(29@0z>zBw>TN}t=KbE3V=rRUKc*bTvz2ADLb&#Ut2Dmeln4j@?j*5&^;?^1*jcEN!?w z0b}aj@B86Wl=qmv$9QN2$6EO4`pxX!rwe9}(v(zt)|8a-8mZA`;?&HGfr23_(lGD{ z6#+fP3|pLJ(?hzU<^(%vLE)$~0N@~ON27Nb6SI4T#hHIh28x|GAYTDXw$22wS^+J7 z8<1McA||T3f=Txcfgz?yNy&yqgn9mQXgu1&uI)n_VV5s0GwlcAg=83_o6!~sgAnIw;MfPY&QHi^ZjnnNOCj*(^e%xc{E zIY3Q)NbYaq6Yqv~WN!X+s>1U-~=u2o2rU zl*B*_9ho*m5VFkVj~PXRohN2-KALipw-EKZOdQnhL}t4UregK%2gf>6-*hx!cf{^4L3V(J`3p3dK82J4UZO8cp*KOcXh6;9RdV z^JP>Za@m&`Lk5+#kgM7d!ImR&&$!VX=Dj#vt$mHRPeu$wVERQDd+mbuLHt|))& z)^JPxf`^vTgq!(T!Xzgnw2f;Z;+AE$x{{v%c~<#>;fETPFk~=^2HK8!YCV8VxDoknb0e6+vO&`1_b#CuUXS4_A})jqE{O3u&7NW0ZNqPq^e!;V_Xu z4tC0s-?{_TVu6d!BQQhSECVAu66+SFV}hO*sycP2N4T+f>apaNS8NK4ys9^xCljeG z6*0uM0L^diTovvhM2(4cr&KzMBJ32IHwt3bN0|J7H}5=PV6a7RuK_}5v5SjlHKdWMWHWY zeEvZzpVob94Zc7WyMoq~#vzUV6dsxnK^sT%zAZULZiYg^oAYD79bI3XE&YSHOXdA~ z)+L-AyMlM(#{BvpzSw&{1n)Y?`wtTBq!{kx>=9`2edOA?B0~6mzyRQ}u0LBuDkw$(?IxQk^qj*lEg_upwl1cBSQx(Of6qZgzaD}E5DNdTK16Y& z@yb$5MN!P)#{^a zD9Lj@*`)Ke|95R;TCgIE*Lt%rNzA~VBwsxA9Lcz?@$08>1PsboZDVhkVl+0QKKIIc z)Y_T0a7K^XaOAJHG1Jp_G9Pm`)WIg&q+SwygI+wpC^*=D*}jV$(HD|OHLVv20< zXmjA-LvZ(1nr}u*Si(c~`H9Rkr%PceR*zra zW+FN6#r)MaV$eB-HiPFX4O2tt{%RZC#nl$kDR)Ga9+YD>2g&01k4Df7jty$a(v#z1 znj&KlVKkrn23ddzE6Y))u0EPh-1pMlO$~NldSp2Z`z2i){HNE$vPxFOQqDIXK^)?< zg@f3v6E7wl&a;tPHD40-zwSl}i}pL*|LJbj7|Lm;UN0nef1aQ*I9O%SBGGM#)F{Jw z<5N>YG$ptAdRV4&meowunU|P@$ZpE6t-R1VC^E($0}1LV^OGCAG;iy`(|_HKMp6Fp zrV@0dL$GpUBtYtMR|g;rLPT2!XP*W+N=%Zj7R!EK^I=qFR6d+-y8Vu(*Mk#!!!CK~ zY0F`TB|V5Gn<-p@F7*3;dX``P&*qP|D{*{VS>sy_>|mTxc#if$C z?}vbPQyL=}Z)#0_OrCWsihyx%MnyrFU=3Nr8H9^zt{#y^Ay9Xlqio1`1h+x*BR@W= zk!aa+o36Q=hWTMawzLIoyVKETAJNP)$wxR2UH=pj`cZM!n}aq9kY{5R7(1pvWlf2bOnYwV z4FSv@%jnJ^X!T+Bu-y;4h#~n-Rs1-Z1E=~*Cvb*?5NA$DdM{E>C6~#`D+&-6pTL)* z_%p*867|7gOJ*}?aQ^aX#apyLo!iT6?6Q8cma^YkzuqbzUY&nr%<*PoUk0oF#J;-X z-Xc-sO8fB4{m)`6wp{vL&mHiBUv^5w)_`b$m{9n76=-#Z5F%5XW#<0C9vTiN-L4rw z$9Ig1(cCjV2_l3~)6Rb&@F2rSgc4ESbK(+W(V+y#5?FVpJ{nD1F(mE{bH~W2$c==9 zfaIgd$Q%m-*eY07d*ByGRK)4D%W$h$50pL$P|YHeO8-^HJbRu3_WtU$8!8;Xs+rqMyD z&~^taM5C%pfw*^<6qzOyXVQGFYj$LY20ILElCpnZ@6oXY4u^iHb6Ah3Un6(Ikxq}J z(HzJFL)U>W#6H<|s^~vS_oI9-%LA+nR%IyZdbN2Gqo2QRZP^lX$j(&--qEfCDOyoN zOm7N)@6xiC`@YERTvY=zo&kor$Z>>$pVcypqks zVM4Yv01RdZdh%wLa&yUO#<=bqfdWue0%HOG_ViHz971sJnV82MKMSB}TE-f=F)DYV z%=?OXwdS#jMctP+Yyrua_`cJI)3)|C7q89)4^|J<;-`Tx)K{oi+jdCCvoU~@{oeS-wSR}t< zqJF7}G*)RR7P?2qVctCp>!#Si_xdcbg^8P3EJklSF`nXI!f*Vu*`Ro-1Gi0@qiyLF zHuGa~!^N_JMptvRXK68Mo%1l`%1~9d!i$soJ7Fm1*+@Xo8;q2i8?9bMc#&NE=}I?e zo84|YVDc(ZHd`8_Epb8)5?HH zndOp3=VF^;f4b>dmKw+)Ny}PaYFf50%@wr(@RkOPr}x_URFzeV+hI60N;iKCs7Cbq z?}Sb{pO!ia1H6l36Mwi!)8HM;un}ipW|XCS46}adL&OPgXTr1Kr})G?ye-(fWg9Xncele$wa7|m85ST0j(6WK(L<*jL=d;xzS=c4b`|{h%5CV|6cg_qi^vs@^R&@S zQf5G{K@bh9dA(~iG0F*sP8NuA$te-vJBMze7v0pNnfTrts+&@6kJY{&(wE+iGGM9h zPz{k-uhPnNEa3$@`$Roo2noirCI@E{PdKMI@KyvFidAha6{uR2nSQe#juBkZ)v!tD zzFuKBu^M-pW$W6w0>xHI7n1}yU!}((~ zGcbL;9gM{3SiWkDSI%({uy#Dh!P%CvNBL>AR7~@^+&~`1B5BYoOW#xm5ho`ri2ekE z+M!r63{-9Nu^T2{st8on3GF6|aE#V`w*rp0vU^^!7{#;wh#)9cj^9#g&Nl8ZSpAfGg@SG z1HXAB2#XEJuUpP#crBoo@h4;*!4+w_6`5{54=ug7BFpa9FSc$Vkp82llUqrU!M*W9 zrxaF;&On(8pAGsEt%|O`Gr>9=3`HBG#n7)UK1t6)YgnrR?62$tz(hf80|S~b36*y} zaKJtEEfCu?r?8C&JE#Ptq0g z5m~X|LnP9x@DWErG_BMR3cv?+#I<;aVrn^*@d!8ysySx6YBHr+jjwzr=1SG>Fehkk}>4DLA19dxj@tk7%v(0bU^sf<|^4GOql^9@} zLI++{>Y!7G4Bqkv_riWkojnVS04c^Bu!@1SGk+ZKYAViP3?Kqy2O^(spOCYR^CL2= zj23x`UW(n;4V~0&37;!)1aPHI6q_YSIVy!d0({}a?$_5Fx=$cdC}eAi#@^G;eDqp? zF~!honer3K`hvny5v?-4js3AGV7d>@JahYH#EZHhZX)t7Pex;8aT0KS9!ryMu<$Cf zJOtnup{o*91+w%~SFTOmypcctyG!pN43swc|t;Y}lYY0U=LBjuWgWdTeKj!ts(5#UkWfh+gU$k<17TD07m=eb>O`8S$%_pQ7++<{i3}?9=Q!0f z{Yd}LBHfR!CQH^{dHey0y2e|FP*yrFWDv+NmaAA?Zu|+)?KX}v8;@(t3N(!t5VAP#0umd!%{CHCnopcb0)`nR%IZGb(M9dPc9#}X+S zt5$eeB#n@F)Qfhfqy%_z)_;K1xP`McMT7XaWai)^GEhkbyL6vcIoLW&NUkGrq}8%5 zRdFA~O6deAZVL%>0}F%{NdshlJx3IILp`@MjXWq!QiuP+ph*@3Rswz(Cu?Q+ zSfWH+YPO+eQ||0&qh!a7oDPkKD~o1i#R

^9YDX3`{p5DziS)e#heHDT*T^>H!sk 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 Q{(Jp@x+Cn5o;CLW3Iq69F#rGn literal 0 HcmV?d00001 From f46fccd0d3b87470b572a941cdceb68cfeb3da60 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 9 Dec 2017 21:44:03 -0800 Subject: [PATCH 092/117] docs: add missing completion gif --- images/completion.gif | Bin 0 -> 161421 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/completion.gif diff --git a/images/completion.gif b/images/completion.gif new file mode 100644 index 0000000000000000000000000000000000000000..b43d2f9bf9912d43f44d37d0069e2b22a69b47d2 GIT binary patch literal 161421 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

pMV-9~>o_+P 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 Date: Mon, 18 Dec 2017 12:55:12 +1100 Subject: [PATCH 093/117] feat: foreach completion (#551) --- fixtures/completion/foreach.php | 37 +++++ src/CompletionProvider.php | 8 + src/DefinitionResolver.php | 40 ++++- src/Server/TextDocument.php | 1 + tests/Server/TextDocument/CompletionTest.php | 140 ++++++++++++++++++ ...rrayValueShouldBeBoolean.php.expected.json | 2 +- .../cases/magicConsts.php.expected.json | 2 +- 7 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 fixtures/completion/foreach.php diff --git a/fixtures/completion/foreach.php b/fixtures/completion/foreach.php new file mode 100644 index 0000000..df8b6df --- /dev/null +++ b/fixtures/completion/foreach.php @@ -0,0 +1,37 @@ +test(); +$array1 = [new Bar(), new \stdClass()]; +$array2 = ['foo' => $bar, $bar]; +$array3 = ['foo' => $bar, 'baz' => $bar]; + +foreach ($bars as $value) { + $v + $value-> +} + +foreach ($array1 as $key => $value) { + $ +} + +foreach ($array2 as $key => $value) { + $ +} + +foreach ($array3 as $key => $value) { + $ +} + +foreach ($bar->test() as $value) { + $ +} diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index d774423..fd89568 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -486,6 +486,14 @@ class CompletionProvider if ($this->isAssignmentToVariableWithPrefix($node, $namePrefix)) { $vars[] = $node->leftOperand; + } elseif ($node instanceof Node\ForeachKey || $node instanceof Node\ForeachValue) { + foreach ($node->getDescendantNodes() as $descendantNode) { + if ($descendantNode instanceof Node\Expression\Variable + && ($namePrefix === '' || strpos($descendantNode->getName(), $namePrefix) !== false) + ) { + $vars[] = $descendantNode; + } + } } else { // Get all descendent variables, then filter to ones that start with $namePrefix. // Avoiding closure usage in tight loop diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 2c1f67d..f36588d 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -568,6 +568,20 @@ class DefinitionResolver } break; } + + // If we get to a ForeachStatement, check the keys and values + if ($n instanceof Node\Statement\ForeachStatement) { + if ($n->foreachKey && $n->foreachKey->expression->getName() === $name) { + return $n->foreachKey; + } + if ($n->foreachValue + && $n->foreachValue->expression instanceof Node\Expression\Variable + && $n->foreachValue->expression->getName() === $name + ) { + return $n->foreachValue; + } + } + // Check each previous sibling node for a variable assignment to that variable while (($prevSibling = $n->getPreviousSibling()) !== null && $n = $prevSibling) { if ($n instanceof Node\Statement\ExpressionStatement) { @@ -619,6 +633,9 @@ class DefinitionResolver if ($defNode instanceof Node\Expression\AssignmentExpression || $defNode instanceof Node\UseVariableName) { return $this->resolveExpressionNodeToType($defNode); } + if ($defNode instanceof Node\ForeachKey || $defNode instanceof Node\ForeachValue) { + return $this->getTypeFromNode($defNode); + } if ($defNode instanceof Node\Parameter) { return $this->getTypeFromNode($defNode); } @@ -900,7 +917,7 @@ class DefinitionResolver $keyTypes[] = $item->elementKey ? $this->resolveExpressionNodeToType($item->elementKey) : new Types\Integer; } } - $valueTypes = array_unique($keyTypes); + $valueTypes = array_unique($valueTypes); $keyTypes = array_unique($keyTypes); if (empty($valueTypes)) { $valueType = null; @@ -1080,6 +1097,27 @@ class DefinitionResolver return new Types\Mixed_; } + // FOREACH KEY/VARIABLE + if ($node instanceof Node\ForeachKey || $node->parent instanceof Node\ForeachKey) { + $foreach = $node->getFirstAncestor(Node\Statement\ForeachStatement::class); + $collectionType = $this->resolveExpressionNodeToType($foreach->forEachCollectionName); + if ($collectionType instanceof Types\Array_) { + return $collectionType->getKeyType(); + } + return new Types\Mixed_(); + } + + // FOREACH VALUE/VARIABLE + if ($node instanceof Node\ForeachValue + || ($node instanceof Node\Expression\Variable && $node->parent instanceof Node\ForeachValue) + ) { + $foreach = $node->getFirstAncestor(Node\Statement\ForeachStatement::class); + $collectionType = $this->resolveExpressionNodeToType($foreach->forEachCollectionName); + if ($collectionType instanceof Types\Array_) { + return $collectionType->getValueType(); + } + } + // PROPERTIES, CONSTS, CLASS CONSTS, ASSIGNMENT EXPRESSIONS // Get the documented type the assignment resolves to. if ( diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 704ceb8..371ad36 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -337,6 +337,7 @@ class TextDocument if ($def === null) { return new Hover([], $range); } + $contents = []; if ($def->declarationLine) { $contents[] = new MarkedString('php', "declarationLine); } diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 424dc3c..ded5941 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -554,6 +554,146 @@ class CompletionTest extends TestCase ], true), $items); } + /** + * @dataProvider foreachProvider + */ + public function testForeach(Position $position, array $expectedItems) + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/foreach.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + $position + )->wait(); + $this->assertCompletionsListSubset(new CompletionList($expectedItems, true), $items); + } + + public function foreachProvider(): array + { + return [ + 'foreach value' => [ + new Position(18, 6), + [ + new CompletionItem( + '$value', + CompletionItemKind::VARIABLE, + '\\Foo\\Bar', + null, + null, + null, + null, + new TextEdit(new Range(new Position(18, 6), new Position(18, 6)), 'alue') + ), + ] + ], + 'foreach value resolved' => [ + new Position(19, 12), + [ + new CompletionItem( + 'foo', + CompletionItemKind::PROPERTY, + 'mixed' + ), + new CompletionItem( + 'test', + CompletionItemKind::METHOD, + '\\Foo\\Bar[]' + ), + ] + ], + 'array creation with multiple objects' => [ + new Position(23, 5), + [ + new CompletionItem( + '$value', + CompletionItemKind::VARIABLE, + '\\Foo\\Bar|\\stdClass', + null, + null, + null, + null, + new TextEdit(new Range(new Position(23, 5), new Position(23, 5)), 'value') + ), + new CompletionItem( + '$key', + CompletionItemKind::VARIABLE, + 'int', + null, + null, + null, + null, + new TextEdit(new Range(new Position(23, 5), new Position(23, 5)), 'key') + ), + ] + ], + 'array creation with string/int keys and object values' => [ + new Position(27, 5), + [ + new CompletionItem( + '$value', + CompletionItemKind::VARIABLE, + '\\Foo\\Bar', + null, + null, + null, + null, + new TextEdit(new Range(new Position(27, 5), new Position(27, 5)), 'value') + ), + new CompletionItem( + '$key', + CompletionItemKind::VARIABLE, + 'string|int', + null, + null, + null, + null, + new TextEdit(new Range(new Position(27, 5), new Position(27, 5)), 'key') + ), + ] + ], + 'array creation with only string keys' => [ + new Position(31, 5), + [ + new CompletionItem( + '$value', + CompletionItemKind::VARIABLE, + '\\Foo\\Bar', + null, + null, + null, + null, + new TextEdit(new Range(new Position(31, 5), new Position(31, 5)), 'value') + ), + new CompletionItem( + '$key', + CompletionItemKind::VARIABLE, + 'string', + null, + null, + null, + null, + new TextEdit(new Range(new Position(31, 5), new Position(31, 5)), 'key') + ), + ] + ], + 'foreach function call' => [ + new Position(35, 5), + [ + new CompletionItem( + '$value', + CompletionItemKind::VARIABLE, + '\\Foo\\Bar', + null, + null, + null, + null, + new TextEdit(new Range(new Position(35, 5), new Position(35, 5)), 'value') + ), + ] + ], + ]; + } + public function testMethodReturnType() { $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/method_return_type.php'); diff --git a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json index 1f40635..f0cdb24 100644 --- a/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json +++ b/tests/Validation/cases/arrayValueShouldBeBoolean.php.expected.json @@ -36,7 +36,7 @@ }, "containerName": "A" }, - "type__tostring": "string[]", + "type__tostring": "bool[]", "type": {}, "declarationLine": "protected $foo;", "documentation": null, diff --git a/tests/Validation/cases/magicConsts.php.expected.json b/tests/Validation/cases/magicConsts.php.expected.json index 545f1cb..27608e5 100644 --- a/tests/Validation/cases/magicConsts.php.expected.json +++ b/tests/Validation/cases/magicConsts.php.expected.json @@ -40,7 +40,7 @@ }, "containerName": "A" }, - "type__tostring": "\\__CLASS__[]", + "type__tostring": "bool[]", "type": {}, "declarationLine": "private static $deprecationsTriggered;", "documentation": null, From 63da051e72554d41a123b7cb667601ab1f696dd1 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sat, 23 Dec 2017 13:02:37 +1100 Subject: [PATCH 094/117] fix(DefinitionResolver): fix methods with self return type (#550) --- src/DefinitionResolver.php | 8 +++++- .../WithReturnTypehints.php.expected.json | 2 +- .../cases/staticMethodReturnType.php | 2 ++ .../staticMethodReturnType.php.expected.json | 25 +++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index f36588d..905f9d6 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -729,7 +729,7 @@ class DefinitionResolver foreach ($classDef->getAncestorDefinitions($this->index, true) as $fqn => $def) { $def = $this->index->getDefinition($fqn . $add); if ($def !== null) { - if ($def->type instanceof Types\This) { + if ($def->type instanceof Types\This || $def->type instanceof Types\Self_) { return new Types\Object_(new Fqsen('\\' . $classFqn)); } return $def->type; @@ -1090,6 +1090,12 @@ class DefinitionResolver if ($node->returnType instanceof PhpParser\Token) { // Resolve a string like "bool" to a type object return $this->typeResolver->resolve($node->returnType->getText($node->getFileContents())); + } elseif ($node->returnType->getResolvedName() === 'self') { + $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); + if ($classNode) { + $classFqn = (string)$classNode->getNamespacedName(); + return new Types\Object_(new Fqsen('\\' . $classFqn)); + } } return new Types\Object_(new Fqsen('\\' . (string)$node->returnType->getResolvedName())); } diff --git a/tests/Validation/cases/WithReturnTypehints.php.expected.json b/tests/Validation/cases/WithReturnTypehints.php.expected.json index a037d6c..2eb8218 100644 --- a/tests/Validation/cases/WithReturnTypehints.php.expected.json +++ b/tests/Validation/cases/WithReturnTypehints.php.expected.json @@ -71,7 +71,7 @@ }, "containerName": "Fixtures\\Prophecy\\WithReturnTypehints" }, - "type__tostring": "\\self", + "type__tostring": "\\Fixtures\\Prophecy\\WithReturnTypehints", "type": {}, "declarationLine": "public function getSelf(): self {", "documentation": null, diff --git a/tests/Validation/cases/staticMethodReturnType.php b/tests/Validation/cases/staticMethodReturnType.php index 325738a..0ce2654 100644 --- a/tests/Validation/cases/staticMethodReturnType.php +++ b/tests/Validation/cases/staticMethodReturnType.php @@ -5,5 +5,7 @@ class FooClass { return new FooClass(); } + public static function staticSelf(): self { } + public function bar() { } } diff --git a/tests/Validation/cases/staticMethodReturnType.php.expected.json b/tests/Validation/cases/staticMethodReturnType.php.expected.json index 8de5673..041042a 100644 --- a/tests/Validation/cases/staticMethodReturnType.php.expected.json +++ b/tests/Validation/cases/staticMethodReturnType.php.expected.json @@ -50,6 +50,31 @@ "parameters": [] } }, + "FooClass::staticSelf()": { + "fqn": "FooClass::staticSelf()", + "extends": [], + "isMember": true, + "roamed": false, + "isStatic": true, + "canBeInstantiated": false, + "symbolInformation": { + "name": "staticSelf", + "kind": 6, + "location": { + "uri": "./staticMethodReturnType.php" + }, + "containerName": "FooClass" + }, + "type__tostring": "\\FooClass", + "type": {}, + "declarationLine": "public static function staticSelf(): self { }", + "documentation": null, + "signatureInformation": { + "label": "()", + "documentation": null, + "parameters": [] + } + }, "FooClass->bar()": { "fqn": "FooClass->bar()", "extends": [], From a0caf8d18f85cffba816ced7ae1318d22ff63515 Mon Sep 17 00:00:00 2001 From: Jannik Vieten Date: Sat, 23 Dec 2017 03:03:24 +0100 Subject: [PATCH 095/117] docs(used-by): mention Atom's ide-php in README (#559) adds Atom's ide-php package to "used by" section in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d3a4d46..5d95d61 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ Example: - [Eclipse Che](https://eclipse.org/che/) - [Eclipse IDE (LSP4E-PHP)](https://github.com/eclipselabs/lsp4e-php) - NeoVim: [LanguageServer-php-neovim](https://github.com/roxma/LanguageServer-php-neovim) with [LanguageClient neovim](https://github.com/autozimu/LanguageClient-neovim) + - Atom: [ide-php](https://github.com/atom/ide-php) ## Contributing From 425b2390b5b7fc9a54c5913ed7a30ebf6e681ff3 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sun, 24 Dec 2017 20:52:49 +1100 Subject: [PATCH 096/117] fix(DefinitionResolver): fix crash on unknown foreach type (#562) Fix when unknown type is found in foreach expression --- fixtures/completion/foreach.php | 3 +++ src/DefinitionResolver.php | 1 + tests/Server/TextDocument/CompletionTest.php | 15 +++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/fixtures/completion/foreach.php b/fixtures/completion/foreach.php index df8b6df..b6fa5be 100644 --- a/fixtures/completion/foreach.php +++ b/fixtures/completion/foreach.php @@ -35,3 +35,6 @@ foreach ($array3 as $key => $value) { foreach ($bar->test() as $value) { $ } + +foreach ($unknownArray as $unknown) { + $unkno diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 905f9d6..a408dd4 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -1122,6 +1122,7 @@ class DefinitionResolver if ($collectionType instanceof Types\Array_) { return $collectionType->getValueType(); } + return new Types\Mixed_(); } // PROPERTIES, CONSTS, CLASS CONSTS, ASSIGNMENT EXPRESSIONS diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index ded5941..80d3026 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -691,6 +691,21 @@ class CompletionTest extends TestCase ), ] ], + 'foreach unknown type' => [ + new Position(39, 10), + [ + new CompletionItem( + '$unknown', + CompletionItemKind::VARIABLE, + 'mixed', + null, + null, + null, + null, + new TextEdit(new Range(new Position(39, 10), new Position(39, 10)), 'wn') + ), + ] + ], ]; } From 1cfba8b6bb5d65f49fc059097bb347d1c31189b1 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Mon, 25 Dec 2017 12:55:48 +1100 Subject: [PATCH 097/117] fix(DefinitionResolver): don't crash if foreach key isn't a variable (#564) --- fixtures/completion/foreach.php | 2 +- src/DefinitionResolver.php | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fixtures/completion/foreach.php b/fixtures/completion/foreach.php index b6fa5be..4278f00 100644 --- a/fixtures/completion/foreach.php +++ b/fixtures/completion/foreach.php @@ -36,5 +36,5 @@ foreach ($bar->test() as $value) { $ } -foreach ($unknownArray as $unknown) { +foreach ($unknownArray as $member->access => $unknown) { $unkno diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index a408dd4..72bdce4 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -571,7 +571,9 @@ class DefinitionResolver // If we get to a ForeachStatement, check the keys and values if ($n instanceof Node\Statement\ForeachStatement) { - if ($n->foreachKey && $n->foreachKey->expression->getName() === $name) { + if ($n->foreachKey instanceof Node\Expression\Variable + && $n->foreachKey->expression->getName() === $name + ) { return $n->foreachKey; } if ($n->foreachValue From 8439da999a09f3f05988d0210e6018000df434da Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 28 Dec 2017 15:34:11 -0800 Subject: [PATCH 098/117] ci(travis): only build master and PRs --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1301d4a..cb4bcbf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,5 +56,5 @@ stages: if: branch = master AND type = push AND fork = false branches: - except: - - /^v\d+\.\d+\.\d+$/ + only: + - master From 20960a8b9f6fb96d8ec0fb632f5b0d629a44da21 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 30 Dec 2017 22:26:51 -0800 Subject: [PATCH 099/117] fix(DefinitionResolver): find variables in sibling children (#568) Fixes #566 --- fixtures/completion/foreach.php | 6 +++ src/DefinitionResolver.php | 73 ++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/fixtures/completion/foreach.php b/fixtures/completion/foreach.php index 4278f00..84bafd1 100644 --- a/fixtures/completion/foreach.php +++ b/fixtures/completion/foreach.php @@ -38,3 +38,9 @@ foreach ($bar->test() as $value) { foreach ($unknownArray as $member->access => $unknown) { $unkno + +foreach ($loop as $loop) { +} + +foreach ($loop->getArray() as $loop) { +} diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index 72bdce4..f5e5f7b 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -545,6 +545,15 @@ class DefinitionResolver } else { throw new \InvalidArgumentException('$var must be Variable, Param or ClosureUse, not ' . get_class($var)); } + if (empty($name)) { + return null; + } + + $shouldDescend = function ($nodeToDescand) { + // Make sure not to decend into functions or classes (they represent a scope boundary) + return !($nodeToDescand instanceof PhpParser\FunctionLike || $nodeToDescand instanceof PhpParser\ClassLike); + }; + // Traverse the AST up do { // If a function is met, check the parameters and use statements @@ -569,39 +578,55 @@ class DefinitionResolver break; } - // If we get to a ForeachStatement, check the keys and values - if ($n instanceof Node\Statement\ForeachStatement) { - if ($n->foreachKey instanceof Node\Expression\Variable - && $n->foreachKey->expression->getName() === $name - ) { - return $n->foreachKey; - } - if ($n->foreachValue - && $n->foreachValue->expression instanceof Node\Expression\Variable - && $n->foreachValue->expression->getName() === $name - ) { - return $n->foreachValue; - } - } - - // Check each previous sibling node for a variable assignment to that variable + // Check each previous sibling node and their descendents for a variable assignment to that variable + // Each previous sibling could contain a declaration of the variable while (($prevSibling = $n->getPreviousSibling()) !== null && $n = $prevSibling) { - if ($n instanceof Node\Statement\ExpressionStatement) { - $n = $n->expression; - } - if ( - // TODO - clean this up - ($n instanceof Node\Expression\AssignmentExpression && $n->operator->kind === PhpParser\TokenKind::EqualsToken) - && $n->leftOperand instanceof Node\Expression\Variable && $n->leftOperand->getName() === $name - ) { + + // Check the sibling itself + if (self::isVariableDeclaration($n, $name)) { return $n; } + + // Check descendant of this sibling (e.g. the children of a previous if block) + foreach ($n->getDescendantNodes($shouldDescend) as $descendant) { + if (self::isVariableDeclaration($descendant, $name)) { + return $descendant; + } + } } } while (isset($n) && $n = $n->parent); // Return null if nothing was found return null; } + /** + * Checks whether the given Node declares the given variable name + * + * @param Node $n The Node to check + * @param string $name The name of the wanted variable + * @return bool + */ + private static function isVariableDeclaration(Node $n, string $name) + { + if ( + // TODO - clean this up + ($n instanceof Node\Expression\AssignmentExpression && $n->operator->kind === PhpParser\TokenKind::EqualsToken) + && $n->leftOperand instanceof Node\Expression\Variable && $n->leftOperand->getName() === $name + ) { + return true; + } + + if ( + ($n instanceof Node\ForeachValue || $n instanceof Node\ForeachKey) + && $n->expression instanceof Node\Expression\Variable + && $n->expression->getName() === $name + ) { + return true; + } + + return false; + } + /** * Given an expression node, resolves that expression recursively to a type. * If the type could not be resolved, returns Types\Mixed_. From c48ee5580878b73d3f0e9c61b2198ec97df3dee6 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Mon, 1 Jan 2018 18:31:55 -0800 Subject: [PATCH 100/117] tests: fix benchmark on case sensitive filesystems (#573) On case insensitive file systems, such as the defaults for Mac OS/Windows, this works, but it doesn't work for ext4, etc. The folder being checked out is `validation/frameworks/codeigniter`, this searched for `validation/frameworks/CodeIgniter` --- Performance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Performance.php b/Performance.php index 5022f84..6fc1c28 100644 --- a/Performance.php +++ b/Performance.php @@ -14,7 +14,7 @@ use RecursiveIteratorIterator; $totalSize = 0; -$frameworks = ["drupal", "wordpress", "php-language-server", "tolerant-php-parser", "math-php", "symfony", "CodeIgniter", "cakephp"]; +$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"); From 6894d85aaf2907d72db443f1a8b0a8d52f640900 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Tue, 9 Jan 2018 20:38:18 +1100 Subject: [PATCH 101/117] fix(DefinitionResolver): resolve self correctly for docblock @return self (#576) --- src/DefinitionResolver.php | 30 +++++++++++++++---- tests/Validation/cases/methodReturnType.php | 3 ++ .../cases/methodReturnType.php.expected.json | 25 ++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/DefinitionResolver.php b/src/DefinitionResolver.php index f5e5f7b..990c196 100644 --- a/src/DefinitionResolver.php +++ b/src/DefinitionResolver.php @@ -526,6 +526,20 @@ class DefinitionResolver return (string)$classNode->getNamespacedName(); } + /** + * Returns the type of the class a node is contained in + * Returns null if the class is anonymous or the node is not contained in a class + * + * @param Node $node The node used to find the containing class + * + * @return Types\Object_|null + */ + private function getContainingClassType(Node $node) + { + $classFqn = $this->getContainingClassFqn($node); + return $classFqn ? new Types\Object_(new Fqsen('\\' . $classFqn)) : null; + } + /** * Returns the assignment or parameter node where a variable was defined * @@ -1110,7 +1124,14 @@ class DefinitionResolver && $returnTags[0]->getType() !== null ) { // Use @return tag - return $returnTags[0]->getType(); + $returnType = $returnTags[0]->getType(); + if ($returnType instanceof Types\Self_) { + $selfType = $this->getContainingClassType($node); + if ($selfType) { + return $selfType; + } + } + return $returnType; } if ($node->returnType !== null && !($node->returnType instanceof PhpParser\MissingToken)) { // Use PHP7 return type hint @@ -1118,10 +1139,9 @@ class DefinitionResolver // Resolve a string like "bool" to a type object return $this->typeResolver->resolve($node->returnType->getText($node->getFileContents())); } elseif ($node->returnType->getResolvedName() === 'self') { - $classNode = $node->getFirstAncestor(Node\Statement\ClassDeclaration::class); - if ($classNode) { - $classFqn = (string)$classNode->getNamespacedName(); - return new Types\Object_(new Fqsen('\\' . $classFqn)); + $selfType = $this->getContainingClassType($node); + if ($selfType !== null) { + return $selfType; } } return new Types\Object_(new Fqsen('\\' . (string)$node->returnType->getResolvedName())); diff --git a/tests/Validation/cases/methodReturnType.php b/tests/Validation/cases/methodReturnType.php index b4b937d..824314a 100644 --- a/tests/Validation/cases/methodReturnType.php +++ b/tests/Validation/cases/methodReturnType.php @@ -4,4 +4,7 @@ class FooClass { public function foo(): FooClass { return $this; } + + /** @return self */ + public function bar() { } } diff --git a/tests/Validation/cases/methodReturnType.php.expected.json b/tests/Validation/cases/methodReturnType.php.expected.json index 0d537c5..4abbb07 100644 --- a/tests/Validation/cases/methodReturnType.php.expected.json +++ b/tests/Validation/cases/methodReturnType.php.expected.json @@ -49,6 +49,31 @@ "documentation": null, "parameters": [] } + }, + "FooClass->bar()": { + "fqn": "FooClass->bar()", + "extends": [], + "isMember": true, + "roamed": false, + "isStatic": false, + "canBeInstantiated": false, + "symbolInformation": { + "name": "bar", + "kind": 6, + "location": { + "uri": "./methodReturnType.php" + }, + "containerName": "FooClass" + }, + "type__tostring": "\\FooClass", + "type": {}, + "declarationLine": "public function bar() { }", + "documentation": "", + "signatureInformation": { + "label": "()", + "documentation": "", + "parameters": [] + } } } } \ No newline at end of file From d9bc0b0285db5aecc7fdd3b2e64ce28a9a2c0fb8 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 2 Feb 2018 12:09:25 -0800 Subject: [PATCH 102/117] fix(completion): don't require constructor parameter for protocol DTO (#592) --- src/Protocol/CompletionContext.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/CompletionContext.php b/src/Protocol/CompletionContext.php index ff18bd1..f538867 100644 --- a/src/Protocol/CompletionContext.php +++ b/src/Protocol/CompletionContext.php @@ -22,7 +22,7 @@ class CompletionContext */ public $triggerCharacter; - public function __construct(int $triggerKind, string $triggerCharacter = null) + public function __construct(int $triggerKind = null, string $triggerCharacter = null) { $this->triggerKind = $triggerKind; $this->triggerCharacter = $triggerCharacter; From a8f60c9cf69f9c28b5c4dfd6a2313b9158302003 Mon Sep 17 00:00:00 2001 From: Jens Hausdorf Date: Wed, 7 Feb 2018 20:55:25 +0100 Subject: [PATCH 103/117] fix(completion): do not propose trigger character in HTML - || $context->triggerKind === CompletionTriggerKind::INVOKED - || $context->triggerCharacter === '<' + && ( + $context->triggerKind === CompletionTriggerKind::INVOKED + || $context->triggerCharacter === '<' + ) ) ) || $pos == new Position(0, 0) diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index 80d3026..2b4e9ff 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -444,7 +444,7 @@ class CompletionTest extends TestCase ], true), $items); } - public function testHtmlWithPrefix() + public function testHtmlWontBeProposedWithoutCompletionContext() { $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_with_prefix.php'); $this->loader->open($completionUri, file_get_contents($completionUri)); @@ -452,7 +452,21 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(0, 1) )->wait(); - $this->assertCompletionsListSubset(new CompletionList([ + + $this->assertEquals(new CompletionList([], true), $items); + } + + public function testHtmlWontBeProposedWithPrefixWithCompletionContext() + { + $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_with_prefix.php'); + $this->loader->open($completionUri, file_get_contents($completionUri)); + $items = $this->textDocument->completion( + new TextDocumentIdentifier($completionUri), + new Position(0, 1), + new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '<') + )->wait(); + + $this->assertEquals(new CompletionList([ new CompletionItem( ' Date: Wed, 28 Feb 2018 06:03:30 +0100 Subject: [PATCH 104/117] ci(travis): update travis php versions (#601) --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cb4bcbf..44641af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,8 @@ - language: php php: - '7.0' - - '7.2.0RC5' + - '7.2' git: depth: 10 From fc6b06942520e699cba0cb25ba33b328399a2588 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Tue, 27 Feb 2018 21:05:16 -0800 Subject: [PATCH 105/117] ci(dependencies.io): track stable semantic-release --- dependencies.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/dependencies.yml b/dependencies.yml index bf6bd77..9df6f74 100644 --- a/dependencies.yml +++ b/dependencies.yml @@ -10,9 +10,6 @@ collectors: commit_message_prefix: "chore: " - type: js-npm path: / - settings: - dist_tags: - semantic-release: next actors: - type: js-npm versions: "Y.0.0" From 8adcf92c2f54fdb8dd43fef925cf4e3d03107656 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Wed, 28 Feb 2018 10:05:22 -0800 Subject: [PATCH 106/117] chore: remove unused 'use' statements (#612) detected via static analysis and manually checked --- src/Cache/FileSystemCache.php | 1 - src/Client/TextDocument.php | 2 +- src/Client/Window.php | 1 - src/Client/XCache.php | 1 - src/Definition.php | 3 +-- src/FqnUtilities.php | 1 - src/Indexer.php | 2 -- src/Protocol/SymbolInformation.php | 1 - src/ProtocolStreamWriter.php | 1 - src/Server/TextDocument.php | 1 - src/Server/Workspace.php | 6 ++---- src/SignatureHelpProvider.php | 2 -- src/TreeAnalyzer.php | 4 +--- 13 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/Cache/FileSystemCache.php b/src/Cache/FileSystemCache.php index 5e9f523..d6d4410 100644 --- a/src/Cache/FileSystemCache.php +++ b/src/Cache/FileSystemCache.php @@ -3,7 +3,6 @@ declare(strict_types = 1); namespace LanguageServer\Cache; -use LanguageServer\LanguageClient; use Sabre\Event\Promise; /** diff --git a/src/Client/TextDocument.php b/src/Client/TextDocument.php index 176c4fd..f163d60 100644 --- a/src/Client/TextDocument.php +++ b/src/Client/TextDocument.php @@ -4,7 +4,7 @@ declare(strict_types = 1); namespace LanguageServer\Client; use LanguageServer\ClientHandler; -use LanguageServer\Protocol\{Message, TextDocumentItem, TextDocumentIdentifier}; +use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier}; use Sabre\Event\Promise; use JsonMapper; diff --git a/src/Client/Window.php b/src/Client/Window.php index 053f306..c3558f5 100644 --- a/src/Client/Window.php +++ b/src/Client/Window.php @@ -4,7 +4,6 @@ declare(strict_types = 1); namespace LanguageServer\Client; use LanguageServer\ClientHandler; -use LanguageServer\Protocol\Message; use Sabre\Event\Promise; /** diff --git a/src/Client/XCache.php b/src/Client/XCache.php index 3004e58..b3ce5a5 100644 --- a/src/Client/XCache.php +++ b/src/Client/XCache.php @@ -4,7 +4,6 @@ declare(strict_types = 1); namespace LanguageServer\Client; use LanguageServer\ClientHandler; -use LanguageServer\Protocol\Message; use Sabre\Event\Promise; /** diff --git a/src/Definition.php b/src/Definition.php index 0d157cb..1600408 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -6,7 +6,6 @@ namespace LanguageServer; use LanguageServer\Index\ReadableIndex; use phpDocumentor\Reflection\{Types, Type, Fqsen, TypeResolver}; use LanguageServer\Protocol\SymbolInformation; -use Exception; use Generator; /** @@ -69,7 +68,7 @@ class Definition public $canBeInstantiated; /** - * @var Protocol\SymbolInformation + * @var SymbolInformation */ public $symbolInformation; diff --git a/src/FqnUtilities.php b/src/FqnUtilities.php index b5d01a9..fcd6027 100644 --- a/src/FqnUtilities.php +++ b/src/FqnUtilities.php @@ -3,7 +3,6 @@ namespace LanguageServer\FqnUtilities; use phpDocumentor\Reflection\{Type, Types}; -use Microsoft\PhpParser; /** * Returns all possible FQNs in a type diff --git a/src/Indexer.php b/src/Indexer.php index 2618d43..a556d4d 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -6,10 +6,8 @@ namespace LanguageServer; use LanguageServer\Cache\Cache; use LanguageServer\FilesFinder\FilesFinder; use LanguageServer\Index\{DependenciesIndex, Index}; -use LanguageServer\Protocol\Message; use LanguageServer\Protocol\MessageType; use Webmozart\PathUtil\Path; -use Composer\Semver\VersionParser; use Sabre\Event\Promise; use function Sabre\Event\coroutine; diff --git a/src/Protocol/SymbolInformation.php b/src/Protocol/SymbolInformation.php index 6b4d39e..499e417 100644 --- a/src/Protocol/SymbolInformation.php +++ b/src/Protocol/SymbolInformation.php @@ -4,7 +4,6 @@ namespace LanguageServer\Protocol; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; -use Exception; /** * Represents information about programming constructs like variables, classes, diff --git a/src/ProtocolStreamWriter.php b/src/ProtocolStreamWriter.php index 3f51e14..f004a5f 100644 --- a/src/ProtocolStreamWriter.php +++ b/src/ProtocolStreamWriter.php @@ -8,7 +8,6 @@ use Sabre\Event\{ Loop, Promise }; -use RuntimeException; class ProtocolStreamWriter implements ProtocolWriter { diff --git a/src/Server/TextDocument.php b/src/Server/TextDocument.php index 371ad36..e5657b3 100644 --- a/src/Server/TextDocument.php +++ b/src/Server/TextDocument.php @@ -23,7 +23,6 @@ use LanguageServer\Protocol\{ VersionedTextDocumentIdentifier, CompletionContext }; -use Microsoft\PhpParser; use Microsoft\PhpParser\Node; use Sabre\Event\Promise; use Sabre\Uri; diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index fe1dda6..082c502 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -3,18 +3,16 @@ declare(strict_types = 1); namespace LanguageServer\Server; -use LanguageServer\{LanguageClient, Project, PhpDocumentLoader}; +use LanguageServer\{LanguageClient, PhpDocumentLoader}; use LanguageServer\Index\{ProjectIndex, DependenciesIndex, Index}; use LanguageServer\Protocol\{ FileChangeType, FileEvent, SymbolInformation, SymbolDescriptor, - PackageDescriptor, ReferenceInformation, DependencyReference, - Location, - MessageType + Location }; use Sabre\Event\Promise; use function Sabre\Event\coroutine; diff --git a/src/SignatureHelpProvider.php b/src/SignatureHelpProvider.php index aba02b4..439be9c 100644 --- a/src/SignatureHelpProvider.php +++ b/src/SignatureHelpProvider.php @@ -7,10 +7,8 @@ use LanguageServer\Index\ReadableIndex; use LanguageServer\Protocol\{ Position, SignatureHelp, - SignatureInformation, ParameterInformation }; -use Microsoft\PhpParser; use Microsoft\PhpParser\Node; use Sabre\Event\Promise; use function Sabre\Event\coroutine; diff --git a/src/TreeAnalyzer.php b/src/TreeAnalyzer.php index 465f5bb..03ad539 100644 --- a/src/TreeAnalyzer.php +++ b/src/TreeAnalyzer.php @@ -3,10 +3,8 @@ declare(strict_types = 1); namespace LanguageServer; -use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, TextEdit}; -use LanguageServer\Index\Index; +use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position}; use phpDocumentor\Reflection\DocBlockFactory; -use Sabre\Uri; use Microsoft\PhpParser; use Microsoft\PhpParser\Node; use Microsoft\PhpParser\Token; From b412c125a437852673106f232ea13afc2d76569c Mon Sep 17 00:00:00 2001 From: Declspeck Date: Sun, 25 Feb 2018 22:02:54 +0200 Subject: [PATCH 107/117] fix(indexing): handle integer FQNs --- src/PhpDocument.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PhpDocument.php b/src/PhpDocument.php index 0ac8421..805bc08 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -160,7 +160,9 @@ class PhpDocument // Register this document on the project for references foreach ($this->referenceNodes as $fqn => $nodes) { - $this->index->addReferenceUri($fqn, $this->uri); + // Cast the key to string. If (string)'2' is set as an array index, it will read out as (int)2. We must + // deal with incorrect code, so this is a valid scenario. + $this->index->addReferenceUri((string)$fqn, $this->uri); } $this->sourceFileNode = $treeAnalyzer->getSourceFileNode(); From e10896f90554c2f95b052c09fbc3685e84fb9a60 Mon Sep 17 00:00:00 2001 From: Declspeck Date: Sun, 25 Feb 2018 22:07:10 +0200 Subject: [PATCH 108/117] test(performance): don't eat exceptions during benchmark --- Performance.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Performance.php b/Performance.php index 6fc1c28..4d76d38 100644 --- a/Performance.php +++ b/Performance.php @@ -51,11 +51,7 @@ foreach($frameworks as $framework) { $definitionResolver = new DefinitionResolver($index); $parser = new PhpParser\Parser(); - try { - $document = new PhpDocument($testCaseFile, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver); - } catch (\Throwable $e) { - continue; - } + $document = new PhpDocument($testCaseFile, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver); } echo "------------------------------\n"; From de1af6a165960bf942e814a0e183e9e2ca45f6e5 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 8 Mar 2018 11:48:56 -0800 Subject: [PATCH 109/117] refactor: use composer/xdebug-handler (#616) --- README.md | 2 +- bin/php-language-server.php | 37 ++++++++++++++++++++++--------------- composer.json | 3 ++- src/StderrLogger.php | 25 +++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 src/StderrLogger.php diff --git a/README.md b/README.md index 5d95d61..1597536 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,6 @@ The project parses PHPStorm's PHP stubs to get support for PHP builtins. It re-p To debug with xDebug ensure that you have this set as an environment variable - COMPOSER_ALLOW_XDEBUG=1 + PHPLS_ALLOW_XDEBUG=1 This tells the Language Server to not restart without XDebug if it detects that XDebug is enabled (XDebug has a high performance impact). diff --git a/bin/php-language-server.php b/bin/php-language-server.php index 2a2ab69..8e5d348 100644 --- a/bin/php-language-server.php +++ b/bin/php-language-server.php @@ -1,8 +1,8 @@ critical((string)$e); }); @cli_set_process_title('PHP Language Server'); // If XDebug is enabled, restart without it -(new XdebugHandler(Factory::createOutput()))->check(); +$xdebugHandler = new XdebugHandler('PHPLS'); +$xdebugHandler->setLogger($logger); +$xdebugHandler->check(); +unset($xdebugHandler); if (!empty($options['tcp'])) { // Connect to a TCP server $address = $options['tcp']; $socket = stream_socket_client('tcp://' . $address, $errno, $errstr); if ($socket === false) { - fwrite(STDERR, "Could not connect to language client. Error $errno\n$errstr"); + $logger->critical("Could not connect to language client. Error $errno\n$errstr"); exit(1); } stream_set_blocking($socket, false); @@ -53,29 +58,30 @@ if (!empty($options['tcp'])) { $address = $options['tcp-server']; $tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr); if ($tcpServer === false) { - fwrite(STDERR, "Could not listen on $address. Error $errno\n$errstr"); + $logger->critical("Could not listen on $address. Error $errno\n$errstr"); exit(1); } - fwrite(STDOUT, "Server listening on $address\n"); - if (!extension_loaded('pcntl')) { - fwrite(STDERR, "PCNTL is not available. Only a single connection will be accepted\n"); + $logger->debug("Server listening on $address"); + $pcntlAvailable = extension_loaded('pcntl'); + if (!$pcntlAvailable) { + $logger->notice('PCNTL is not available. Only a single connection will be accepted'); } while ($socket = stream_socket_accept($tcpServer, -1)) { - fwrite(STDOUT, "Connection accepted\n"); + $logger->debug('Connection accepted'); stream_set_blocking($socket, false); - if (extension_loaded('pcntl')) { + if ($pcntlAvailable) { // If PCNTL is available, fork a child process for the connection // An exit notification will only terminate the child process $pid = pcntl_fork(); if ($pid === -1) { - fwrite(STDERR, "Could not fork\n"); + $logger->critical('Could not fork'); exit(1); } else if ($pid === 0) { // Child process $reader = new ProtocolStreamReader($socket); $writer = new ProtocolStreamWriter($socket); - $reader->on('close', function () { - fwrite(STDOUT, "Connection closed\n"); + $reader->on('close', function () use ($logger) { + $logger->debug('Connection closed'); }); $ls = new LanguageServer($reader, $writer); Loop\run(); @@ -94,6 +100,7 @@ if (!empty($options['tcp'])) { } } else { // Use STDIO + $logger->debug('Listening on STDIN'); stream_set_blocking(STDIN, false); $ls = new LanguageServer( new ProtocolStreamReader(STDIN), diff --git a/composer.json b/composer.json index a21535b..085f189 100644 --- a/composer.json +++ b/composer.json @@ -22,12 +22,13 @@ ], "require": { "php": "^7.0", - "composer/composer": "^1.3", + "composer/xdebug-handler": "^1.0", "felixfbecker/advanced-json-rpc": "^3.0.0", "jetbrains/phpstorm-stubs": "dev-master", "microsoft/tolerant-php-parser": "0.0.*", "netresearch/jsonmapper": "^1.0", "phpdocumentor/reflection-docblock": "^4.0.0", + "psr/log": "^1.0", "sabre/event": "^5.0", "sabre/uri": "^2.0", "webmozart/glob": "^4.1", diff --git a/src/StderrLogger.php b/src/StderrLogger.php new file mode 100644 index 0000000..6f3bb38 --- /dev/null +++ b/src/StderrLogger.php @@ -0,0 +1,25 @@ + Date: Sat, 24 Feb 2018 15:11:07 +0200 Subject: [PATCH 110/117] feat(completion): add pseudo-keywords like int, bool, strict_types to completion --- src/CompletionProvider.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index 8b5b5c0..d8fefa8 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -91,7 +91,23 @@ class CompletionProvider 'var', 'while', 'xor', - 'yield' + 'yield', + + // List of other reserved words (http://php.net/manual/en/reserved.other-reserved-words.php) + // (the ones which do not occur as actual keywords above.) + 'int', + 'float', + 'bool', + 'string', + 'void', + 'iterable', + 'object', + + // Pseudo keywords + 'from', // As in yield from + 'strict_types', + 'ticks', // As in declare(ticks=1) + 'encoding', // As in declare(encoding='EBCDIC') ]; /** From 49f1e8f04ae8e8c5faa1f151861ba2f1670cc5c2 Mon Sep 17 00:00:00 2001 From: Vincent Klaiber Date: Wed, 14 Mar 2018 17:49:58 +0100 Subject: [PATCH 111/117] chore: exclude appveyor.yml in production --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 688e780..da41831 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,6 +12,7 @@ /.gitmodules export-ignore /.npmrc export-ignore /.travis.yml export-ignore +/appveyor.yml export-ignore /codecov.yml export-ignore /dependencies.yml export-ignore /Dockerfile export-ignore From ebf4c096b3ae38b341fb396c35361e3a0b8beaf6 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Fri, 6 Apr 2018 11:07:48 -0700 Subject: [PATCH 112/117] ci(travis): use PECL to install XDebug --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44641af..4a6ae48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,8 +31,9 @@ jobs: # Fix ruby error https://github.com/Homebrew/brew/issues/3299 - brew update - brew tap homebrew/homebrew-php - - brew install php71 - - brew install homebrew/php/php71-xdebug + - brew install php@7.1 + - brew link --force --overwrite php@7.1 + - pecl install xdebug-2.6.0 - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - php composer-setup.php - ln -s "`pwd`/composer.phar" /usr/local/bin/composer From 7e1ca75863dfed0c782b5bfbec85c6d70401c355 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Sat, 21 Apr 2018 16:52:37 -0700 Subject: [PATCH 113/117] ci(travis): remove brew tap --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4a6ae48..95def1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,6 @@ jobs: before_install: # Fix ruby error https://github.com/Homebrew/brew/issues/3299 - brew update - - brew tap homebrew/homebrew-php - brew install php@7.1 - brew link --force --overwrite php@7.1 - pecl install xdebug-2.6.0 From fe33c8cd7fb962af6853a8e99aeb47d9881d13aa Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 26 Apr 2018 15:53:21 -0700 Subject: [PATCH 114/117] fix(package): include tests folder This is needed if you want to extend the buildserver and reuse tests --- .gitattributes | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index da41831..63babab 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,7 +3,6 @@ /.vscode export-ignore /fixtures export-ignore /images export-ignore -/tests export-ignore /validation export-ignore /.dockerignore export-ignore /.editorconfig export-ignore From 26e3451e6123aff9e40c8465d5d7d013ba2a2724 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 14 May 2018 02:26:56 +0200 Subject: [PATCH 115/117] docs: add a TOC to the README (#618) --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 1597536..e608a77 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,16 @@ Uses the great [Tolerant PHP Parser](https://github.com/Microsoft/tolerant-php-p [phpDocumentor's DocBlock reflection](https://github.com/phpDocumentor/ReflectionDocBlock) and an [event loop](http://sabre.io/event/loop/) for concurrency. +**Table of Contents** + - [Features](#features) + - [Performance](#performance) + - [Versioning](#versioning) + - [Installation](#installation) + - [Running](#running) + - [Used by](#used-by) + - [Contributing](#contributing) + + ## Features ### [Completion](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_completion) From 3931c8848ff4b5696501d26cad165520cca813a8 Mon Sep 17 00:00:00 2001 From: janekcz69 Date: Wed, 22 Aug 2018 20:48:14 +0200 Subject: [PATCH 116/117] fix: cast null to array before passing to array_merge() (#666) Fixes #595 --- src/Indexer.php | 2 +- src/Server/Workspace.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Indexer.php b/src/Indexer.php index a556d4d..85d1787 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -147,7 +147,7 @@ class Indexer $packageKey = null; $cacheKey = null; $index = null; - foreach (array_merge($this->composerLock->packages, $this->composerLock->{'packages-dev'}) as $package) { + foreach (array_merge($this->composerLock->packages, (array)$this->composerLock->{'packages-dev'}) as $package) { // Check if package name matches and version is absolute // Dynamic constraints are not cached, because they can change every time $packageVersion = ltrim($package->version, 'v'); diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 082c502..548197b 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -169,7 +169,7 @@ class Workspace return []; } $dependencyReferences = []; - foreach (array_merge($this->composerLock->packages, $this->composerLock->{'packages-dev'}) as $package) { + foreach (array_merge($this->composerLock->packages, (array)$this->composerLock->{'packages-dev'}) as $package) { $dependencyReferences[] = new DependencyReference($package); } return $dependencyReferences; From b4b4a2fff5904f4ce895587518642ef7c19771cc Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Wed, 22 Aug 2018 21:25:11 +0200 Subject: [PATCH 117/117] chore: update semantic-release dependencies --- .npmrc | 1 - .travis.yml | 4 +- package-lock.json | 6662 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 34 +- 4 files changed, 6675 insertions(+), 26 deletions(-) delete mode 100644 .npmrc create mode 100644 package-lock.json diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 43c97e7..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/.travis.yml b/.travis.yml index 95def1d..3d03dcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,13 +41,11 @@ jobs: services: - docker install: - - composer install --prefer-dist --no-interaction - nvm install 8 - nvm use 8 - npm install script: - - docker build -t felixfbecker/php-language-server . - - npm run semantic-release + - ./node_modules/.bin/semantic-release stages: - test diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fd26f34 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6662 @@ +{ + "name": "php-language-server", + "version": "0.0.0-development", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@gimenete/type-writer": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@gimenete/type-writer/-/type-writer-0.1.3.tgz", + "integrity": "sha512-vhpvVfM/fYqb1aAnkgOvtDKoOgU3ZYIvDnKSDAFSoBvallmGURMlHOE0/VG/gqunUZVXGCFBGHxI8swjBh+sIA==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "prettier": "^1.13.7" + }, + "dependencies": { + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + } + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.1.tgz", + "integrity": "sha512-KU/VDjC5RwtDUZiz3d+DHXJF2lp5hB9dn552TXIyptj8SH1vXmR40mG0JgGq03IlYsOgGfcv8xrLpSQ0YUMQdA==", + "dev": true + }, + "@octokit/rest": { + "version": "15.10.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.10.0.tgz", + "integrity": "sha512-xZ4ejCZoqvKrIN3tQOKZlJ6nDQxaOdLcjRsamDnbckU7V5YTn2xheIqFXnQ2vLvxqVwyI8+2dfsODYbHxtwtSw==", + "dev": true, + "requires": { + "@gimenete/type-writer": "^0.1.3", + "before-after-hook": "^1.1.0", + "btoa-lite": "^1.0.0", + "debug": "^3.1.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.0", + "lodash": "^4.17.4", + "node-fetch": "^2.1.1", + "url-template": "^2.0.8" + } + }, + "@semantic-release/commit-analyzer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-6.0.0.tgz", + "integrity": "sha512-LEBEg5Ek3PCVv8srHRA8uuwu0t9nAXuBQ9ixjBiMYCqCCUsCezUi5wRdmXnJkXs5/yQkd4Dzx8OJ1zIAL2Pqeg==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.0", + "debug": "^3.1.0", + "import-from": "^2.1.0", + "lodash": "^4.17.4" + } + }, + "@semantic-release/error": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-2.2.0.tgz", + "integrity": "sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==", + "dev": true + }, + "@semantic-release/exec": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@semantic-release/exec/-/exec-3.1.0.tgz", + "integrity": "sha512-m+opdWgZ7ro4j9BD4yL2UIRom3RznE3sbx2Wj8CSCOXoSJ97Ar8Ci8/ziR4QF1SJcKVlrUUhj+7TP5FgziSDgg==", + "dev": true, + "requires": { + "@semantic-release/error": "^2.1.0", + "debug": "^3.1.0", + "execa": "^0.10.0", + "lodash": "^4.17.4", + "parse-json": "^4.0.0" + } + }, + "@semantic-release/github": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-5.0.2.tgz", + "integrity": "sha512-YySh2iBDXcsU5Mtaseo9L3wsCgTNj2wDMxi8L6hLxFayJy3YqiZ9Yym/jf+Fh8J/Afy8IxyVaz8Re5AfjnoyOg==", + "dev": true, + "requires": { + "@octokit/rest": "^15.2.0", + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^1.0.0", + "bottleneck": "^2.0.1", + "debug": "^3.1.0", + "dir-glob": "^2.0.0", + "fs-extra": "^7.0.0", + "globby": "^8.0.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "issue-parser": "^2.2.0", + "lodash": "^4.17.4", + "mime": "^2.0.3", + "p-filter": "^1.0.0", + "p-retry": "^2.0.0", + "parse-github-url": "^1.0.1", + "url-join": "^4.0.0" + } + }, + "@semantic-release/npm": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-5.0.2.tgz", + "integrity": "sha512-h0tNQGGd4pXO9AUVa3LlVLafMF2CJU7TRwl0KTqWLfmb5sFOxNRK4yIkoD/TCmMUst4vxgG57H1fPaD7GE17Fg==", + "dev": true, + "requires": { + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^1.0.0", + "detect-indent": "^5.0.0", + "detect-newline": "^2.1.0", + "execa": "^0.10.0", + "fs-extra": "^7.0.0", + "lodash": "^4.17.4", + "nerf-dart": "^1.0.0", + "normalize-url": "^3.0.0", + "npm": "^6.3.0", + "parse-json": "^4.0.0", + "rc": "^1.2.8", + "read-pkg": "^4.0.0", + "registry-auth-token": "^3.3.1" + }, + "dependencies": { + "read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", + "dev": true, + "requires": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + } + } + } + }, + "@semantic-release/release-notes-generator": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-7.0.1.tgz", + "integrity": "sha512-zMX2t+v0hjr8f+hfqJgvZ/7m9Bjt4RJ6qk52oo+bxwqEnwCm4b4u8nwGLXFfBg/Q7ph/E0MuzYIFrhECICxzBw==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^4.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.0", + "debug": "^3.1.0", + "get-stream": "^4.0.0", + "git-url-parse": "^10.0.1", + "import-from": "^2.1.0", + "into-stream": "^3.1.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "get-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.0.0.tgz", + "integrity": "sha512-FneLKMENeOR7wOK0/ZXCh+lwqtnPwkeunJjRN28LPqzGvNAhYvrTAhXv6xDm4vsJ0M7lcRbIYHQudKsSy2RtSQ==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "JSONStream": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.4.tgz", + "integrity": "sha512-Y7vfi3I5oMOYIr+WxV8NZxDSwcbNgzdKYsTNInmycOq9bUYwGg9ryu57Wg5NLmCjqdFPNUmpMBo3kSJN9tCbXg==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "aggregate-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-1.0.0.tgz", + "integrity": "sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w=", + "dev": true, + "requires": { + "clean-stack": "^1.0.0", + "indent-string": "^3.0.0" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansicolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", + "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "before-after-hook": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.1.0.tgz", + "integrity": "sha512-VOMDtYPwLbIncTxNoSzRyvaMxtXmLWLUqr8k5AfC1BzLk34HvBXaQX8snOwQZ4c0aX8aSERqtJSiI9/m2u5kuA==", + "dev": true + }, + "bottleneck": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.8.0.tgz", + "integrity": "sha512-yHJ9OeOgDWoYLjGKjee8N5qSC72VB/N79H1TmUc00vr99e/SXvzfrxkowFYZgTLmjWlDzLFCWaZ9wbfZm5Xl2Q==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "cardinal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-1.0.0.tgz", + "integrity": "sha1-UOIcGwqjdyn5N33vGWtanOyTLuk=", + "dev": true, + "requires": { + "ansicolors": "~0.2.1", + "redeyed": "~1.0.0" + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "optional": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-stack": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-1.3.0.tgz", + "integrity": "sha1-noIVAa6XmYbEax1m0tQy2y/UrjE=", + "dev": true + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "dev": true, + "requires": { + "colors": "1.0.3" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "optional": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "optional": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "dev": true, + "requires": { + "color-name": "1.1.1" + } + }, + "color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", + "dev": true + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + }, + "compare-func": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", + "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^3.0.0" + } + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "conventional-changelog-angular": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.1.tgz", + "integrity": "sha512-q4ylJ68fWZDdrFC9z4zKcf97HW6hp7Mo2YlqD4owfXhecFKy/PJCU/1oVFF4TqochchChqmZ0Vb0e0g8/MKNlA==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-writer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.0.tgz", + "integrity": "sha512-hMZPe0AQ6Bi05epeK/7hz80xxk59nPA5z/b63TOHq2wigM0/akreOc8N4Jam5b9nFgKWX1e9PdPv2ewgW6bcfg==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "conventional-commits-filter": "^2.0.0", + "dateformat": "^3.0.0", + "handlebars": "^4.0.2", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "semver": "^5.5.0", + "split": "^1.0.0", + "through2": "^2.0.0" + } + }, + "conventional-commits-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.0.tgz", + "integrity": "sha512-Cfl0j1/NquB/TMVx7Wrmyq7uRM+/rPQbtVVGwzfkhZ6/yH6fcMmP0Q/9044TBZPTNdGzm46vXFXL14wbET0/Mg==", + "dev": true, + "requires": { + "is-subset": "^0.1.1", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.0.tgz", + "integrity": "sha512-GWh71U26BLWgMykCp+VghZ4s64wVbtseECcKQ/PvcPZR2cUnz+FUc2J9KjxNl7/ZbCxST8R03c9fc+Vi0umS9Q==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.0", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0", + "trim-off-newlines": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.6.tgz", + "integrity": "sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + } + }, + "dot-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-ci": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-2.1.1.tgz", + "integrity": "sha512-7KVsNirTANngtXZkKzbO/9xtHB51rNzauhuKRKBj2itgATsD/N9ZjyfWNmfiUF9aZIaoqrZ8x0HS8M7Bsk7dBQ==", + "dev": true, + "requires": { + "execa": "^0.10.0", + "java-properties": "^0.2.9" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es6-promise": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", + "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.2.tgz", + "integrity": "sha512-TR6zxCKftDQnUAPvkrCWdBgDq/gbqx8A3ApnBrR5rMvpp6+KMJI0Igw7fkWPgeVK0uhRXTXdvO3O+YP0CaUX2g==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.0.1", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.1", + "micromatch": "^3.1.10" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "find-versions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-2.0.0.tgz", + "integrity": "sha1-KtkNSQ9oKMGqQCks9wmsMxghDDw=", + "dev": true, + "requires": { + "array-uniq": "^1.0.0", + "semver-regex": "^1.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", + "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "git-log-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", + "integrity": "sha1-LmpMGxP8AAKCB7p5WnrDFme5/Uo=", + "dev": true, + "requires": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "~0.6.6" + }, + "dependencies": { + "split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha1-UuLiIdiMdfmnP5BVbiY/+WdysxQ=", + "dev": true, + "requires": { + "through2": "~2.0.0" + } + } + } + }, + "git-up": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-2.0.10.tgz", + "integrity": "sha512-2v4UN3qV2RGypD9QpmUjpk+4+RlYpW8GFuiZqQnKmvei08HsFPd0RfbDvEhnE4wBvnYs8ORVtYpOFuuCEmBVBw==", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "parse-url": "^1.3.0" + } + }, + "git-url-parse": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-10.0.1.tgz", + "integrity": "sha512-Tq2u8UPXc/FawC/dO8bvh8jcck0Lkor5OhuZvmVSeyJGRucDBfw9y2zy/GNCx28lMYh1N12IzPwDexjUNFyAeg==", + "dev": true, + "requires": { + "git-up": "^2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "globby": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", + "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "handlebars": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "dev": true, + "requires": { + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hook-std": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-1.1.0.tgz", + "integrity": "sha512-aIyBZbZl3NS8XoSwIDQ+ZaiBuPOhhPWoBFA3QX0Q8hOMO8Tx4xGRTDnn/nl/LAtZWdieXzFC9ohAtTSnWrlHCQ==", + "dev": true + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", + "dev": true, + "requires": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-ssh": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.0.tgz", + "integrity": "sha1-6+oRaaJhTaOSpjdANmw84EnY3/Y=", + "dev": true, + "requires": { + "protocols": "^1.1.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "dev": true + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "issue-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-2.2.0.tgz", + "integrity": "sha512-qBm9P//mcpIosDX0GU29TJkOcIIyF4PMnetfU6yfWsukLRQJPUWdJuYFjEkHlW5bxCbmEkpBnkaAPiTmCYCNDQ==", + "dev": true, + "requires": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1" + } + }, + "java-properties": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-0.2.10.tgz", + "integrity": "sha512-CpKJh9VRNhS+XqZtg1UMejETGEiqwCGDC/uwPEEQwc2nfdbSm73SIE29TplG2gLYuBOOTNDqxzG6A9NtEPLt0w==", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + } + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=", + "dev": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "marked": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.5.0.tgz", + "integrity": "sha512-UhjmkCWKu1SS/BIePL2a59BMJ7V42EYtTfksodPRXzPEGEph3Inp5dylseqt+KbU9Jglsx8xcMKmlumfJMBXAA==", + "dev": true + }, + "marked-terminal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-3.0.0.tgz", + "integrity": "sha512-7gWHPxQlWNeqjVgW72gwxLeJBj0T/RmurVs2qHPm90f7kuu7CMcZVTmtqk1dogourkAtopZNnp2DUpTIJZKZ4w==", + "dev": true, + "requires": { + "cardinal": "^1.0.0", + "chalk": "^1.1.3", + "cli-table": "^0.3.1", + "lodash.assign": "^4.2.0", + "node-emoji": "^1.4.1" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + }, + "dependencies": { + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, + "merge2": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.2.tgz", + "integrity": "sha512-bgM8twH86rWni21thii6WCMQMRMmwqqdW3sGWi9IipnVAszdLXRjwDwAnyrVXo6DuP3AjRMMttZKUB48QWIFGg==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", + "dev": true + }, + "nice-try": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", + "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "dev": true + }, + "node-emoji": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.8.1.tgz", + "integrity": "sha512-+ktMAh1Jwas+TnGodfCfjUbJKoANqPaJFN0z0iqh41eqD8dvguNzcitVSBSVK1pidz0AqGbLKcoVuVLRVZ/aVg==", + "dev": true, + "requires": { + "lodash.toarray": "^4.4.0" + } + }, + "node-fetch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", + "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==", + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-url": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.2.0.tgz", + "integrity": "sha512-WvF3Myk0NhXkG8S9bygFM4IC1KOvnVJGq0QoGeoqOYOBeinBZp5ybW3QuYbTc89lkWBMM9ZBO4QGRoc0353kKA==", + "dev": true + }, + "npm": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.4.0.tgz", + "integrity": "sha512-k0VteQaxRuI1mREBxCtLUksesD2ZmX5gxjXNEjTmTrxQ3SHW22InkCKyX4NzoeGAYtgmDg5MuE7rcXYod7xgug==", + "dev": true, + "requires": { + "JSONStream": "^1.3.3", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "~1.2.0", + "archy": "~1.0.0", + "bin-links": "^1.1.2", + "bluebird": "~3.5.1", + "byte-size": "^4.0.3", + "cacache": "^11.1.0", + "call-limit": "~1.1.0", + "chownr": "~1.0.1", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.0", + "cmd-shim": "~2.0.2", + "columnify": "~1.5.4", + "config-chain": "~1.1.11", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.2.0", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.0.1", + "glob": "~7.1.2", + "graceful-fs": "~4.1.11", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.7.1", + "iferr": "^1.0.2", + "imurmurhash": "*", + "inflight": "~1.0.6", + "inherits": "~2.0.3", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^2.0.6", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^2.0.1", + "libnpmhook": "^4.0.1", + "libnpx": "^10.2.0", + "lock-verify": "^2.0.2", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^4.1.3", + "meant": "~1.0.1", + "mississippi": "^3.0.0", + "mkdirp": "~0.5.1", + "move-concurrently": "^1.0.1", + "node-gyp": "^3.8.0", + "nopt": "~4.0.1", + "normalize-package-data": "~2.4.0", + "npm-audit-report": "^1.3.1", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "~3.0.0", + "npm-lifecycle": "^2.0.3", + "npm-package-arg": "^6.1.0", + "npm-packlist": "~1.1.10", + "npm-pick-manifest": "^2.1.0", + "npm-profile": "^3.0.2", + "npm-registry-client": "^8.5.1", + "npm-registry-fetch": "^1.1.0", + "npm-user-validate": "~1.0.0", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "~1.4.3", + "osenv": "^0.1.5", + "pacote": "^8.1.6", + "path-is-inside": "~1.0.2", + "promise-inflight": "~1.0.1", + "qrcode-terminal": "^0.12.0", + "query-string": "^6.1.0", + "qw": "~1.0.1", + "read": "~1.0.7", + "read-cmd-shim": "~1.0.1", + "read-installed": "~4.0.3", + "read-package-json": "^2.0.13", + "read-package-tree": "^5.2.1", + "readable-stream": "^2.3.6", + "readdir-scoped-modules": "*", + "request": "^2.87.0", + "retry": "^0.12.0", + "rimraf": "~2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.5.0", + "sha": "~2.0.1", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.0", + "stringify-package": "^1.0.0", + "tar": "^4.4.6", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "~1.1.0", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.2", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.6.0", + "write-file-atomic": "^2.3.0" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "agent-base": { + "version": "4.2.0", + "bundled": true, + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.4.1", + "bundled": true, + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "5.5.2", + "bundled": true, + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true, + "dev": true + }, + "aws4": { + "version": "1.7.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "cmd-shim": "^2.0.2", + "gentle-fs": "^2.0.0", + "graceful-fs": "^4.1.11", + "write-file-atomic": "^2.3.0" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.5.1", + "bundled": true, + "dev": true + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "byline": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "byte-size": { + "version": "4.0.3", + "bundled": true, + "dev": true + }, + "cacache": { + "version": "11.1.0", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "figgy-pudding": "^3.1.0", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.3", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.0", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "call-limit": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "ci-info": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, + "cidr-regex": { + "version": "2.0.9", + "bundled": true, + "dev": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.5.0", + "bundled": true, + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "cmd-shim": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, + "colors": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "dev": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "bundled": true, + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true, + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true, + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.0", + "bundled": true, + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "duplexify": { + "version": "3.6.0", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "err-code": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es6-promise": { + "version": "4.2.4", + "bundled": true, + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "figgy-pudding": { + "version": "3.2.0", + "bundled": true, + "dev": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true + }, + "form-data": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true, + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "4.0.1", + "bundled": true, + "dev": true + }, + "gentle-fs": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.2", + "fs-vacuum": "^1.2.10", + "graceful-fs": "^4.1.11", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true, + "dev": true + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "2.7.1", + "bundled": true, + "dev": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true, + "dev": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "ip": { + "version": "1.1.5", + "bundled": true, + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-ci": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "ci-info": "^1.0.0" + } + }, + "is-cidr": { + "version": "2.0.6", + "bundled": true, + "dev": true, + "requires": { + "cidr-regex": "^2.0.8" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "bundled": true, + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true, + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "libcipm": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "graceful-fs": "^4.1.11", + "lock-verify": "^2.0.2", + "mkdirp": "^0.5.1", + "npm-lifecycle": "^2.0.3", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "pacote": "^8.1.6", + "protoduck": "^5.0.0", + "read-package-json": "^2.0.13", + "rimraf": "^2.6.2", + "worker-farm": "^1.6.0" + } + }, + "libnpmhook": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "figgy-pudding": "^3.1.0", + "npm-registry-fetch": "^3.0.0" + }, + "dependencies": { + "npm-registry-fetch": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "figgy-pudding": "^3.1.0", + "lru-cache": "^4.1.2", + "make-fetch-happen": "^4.0.0", + "npm-package-arg": "^6.0.0" + } + } + } + }, + "libnpx": { + "version": "10.2.0", + "bundled": true, + "dev": true, + "requires": { + "dotenv": "^5.0.1", + "npm-package-arg": "^6.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.0", + "update-notifier": "^2.3.0", + "which": "^1.3.0", + "y18n": "^4.0.0", + "yargs": "^11.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lock-verify": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "npm-package-arg": "^5.1.2 || 6", + "semver": "^5.4.1" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true, + "dev": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true, + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true, + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true, + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true, + "dev": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true, + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true, + "dev": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true, + "dev": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "lru-cache": { + "version": "4.1.3", + "bundled": true, + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-fetch-happen": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "meant": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mime-db": { + "version": "1.33.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true, + "dev": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-gyp": { + "version": "3.8.0", + "bundled": true, + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "dev": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "npm-install-checks": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "npm-lifecycle": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.11", + "node-gyp": "^3.6.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.0" + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true, + "dev": true + }, + "npm-package-arg": { + "version": "6.1.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.2 || 2", + "make-fetch-happen": "^2.5.0 || 3 || 4" + } + }, + "npm-registry-client": { + "version": "8.5.1", + "bundled": true, + "dev": true, + "requires": { + "concat-stream": "^1.5.2", + "graceful-fs": "^4.1.6", + "normalize-package-data": "~1.0.1 || ^2.0.0", + "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "npmlog": "2 || ^3.1.0 || ^4.0.0", + "once": "^1.3.3", + "request": "^2.74.0", + "retry": "^0.10.0", + "safe-buffer": "^5.1.1", + "semver": "2 >=2.2.1 || 3.x || 4 || 5", + "slide": "^1.1.3", + "ssri": "^5.2.4" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true, + "dev": true + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-registry-fetch": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "figgy-pudding": "^2.0.1", + "lru-cache": "^4.1.2", + "make-fetch-happen": "^3.0.0", + "npm-package-arg": "^6.0.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "cacache": { + "version": "10.0.4", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + }, + "dependencies": { + "mississippi": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + } + } + }, + "figgy-pudding": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "make-fetch-happen": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^10.0.4", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.0", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^3.0.1", + "ssri": "^5.2.4" + } + }, + "pump": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "smart-buffer": { + "version": "1.1.15", + "bundled": true, + "dev": true + }, + "socks": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "requires": { + "ip": "^1.1.4", + "smart-buffer": "^1.0.13" + } + }, + "socks-proxy-agent": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "socks": "^1.1.10" + } + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.4.3", + "bundled": true, + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "8.1.6", + "bundled": true, + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "cacache": "^11.0.2", + "get-stream": "^3.0.0", + "glob": "^7.1.2", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.3", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.10", + "npm-pick-manifest": "^2.1.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.5.0", + "ssri": "^6.0.0", + "tar": "^4.4.3", + "unique-filename": "^1.1.0", + "which": "^1.3.0" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "pify": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true, + "dev": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true, + "dev": true + }, + "protoduck": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "genfun": "^4.0.1" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "qs": { + "version": "6.5.2", + "bundled": true, + "dev": true + }, + "query-string": { + "version": "6.1.0", + "bundled": true, + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true + } + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "^4.1.2", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + } + }, + "read-package-json": { + "version": "2.0.13", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "slash": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.2.1", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "once": "^1.3.0", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "bundled": true, + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "request": { + "version": "2.87.0", + "bundled": true, + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "retry": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "sha": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "readable-stream": "^2.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "slash": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "slide": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "smart-buffer": { + "version": "4.0.1", + "bundled": true, + "dev": true + }, + "socks": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "dev": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true, + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "ssri": { + "version": "6.0.0", + "bundled": true, + "dev": true + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-package": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.6", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "through": { + "version": "2.3.8", + "bundled": true, + "dev": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true, + "dev": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "tough-cookie": { + "version": "2.3.4", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true, + "dev": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true + }, + "umask": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "unique-filename": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "update-notifier": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.3.2", + "bundled": true, + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + } + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true, + "dev": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "yargs": { + "version": "11.0.0", + "bundled": true, + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "bundled": true, + "dev": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } + } + }, + "p-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-1.0.0.tgz", + "integrity": "sha1-Yp0xcVAgnI/VCLoTdxPvS7kg6ds=", + "dev": true, + "requires": { + "p-map": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + }, + "dependencies": { + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + } + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-retry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-2.0.0.tgz", + "integrity": "sha512-ZbCuzAmiwJ45q4evp/IG9D+5MUllGSUeCWwPt3j/tdYSi1KPkSD+46uqmAA1LhccDhOXv8kYZKNb8x78VflzfA==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-github-url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", + "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-url": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-1.3.11.tgz", + "integrity": "sha1-V8FUKKuKiSsfQ4aWRccR0OFEtVQ=", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prettier": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", + "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "protocols": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.6.tgz", + "integrity": "sha1-+LsmPqG1/Xp2BNJri+Ob13Z4v4o=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "redeyed": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz", + "integrity": "sha1-6WwZO0DAgWsArshCaY5hGF5VSYo=", + "dev": true, + "requires": { + "esprima": "~3.0.0" + }, + "dependencies": { + "esprima": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz", + "integrity": "sha1-U88kes2ncxPlUcOqLnM0LT+099k=", + "dev": true + } + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "optional": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "semantic-release": { + "version": "15.9.9", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-15.9.9.tgz", + "integrity": "sha512-d12aOwnpVNbI/G7ci63FWCmBlLsKHL5h7m0oWlc8ot4FIUslzhQtCtQ9nflWYhThhxy1taLsX+U+GRIkj72wuQ==", + "dev": true, + "requires": { + "@semantic-release/commit-analyzer": "^6.0.0", + "@semantic-release/error": "^2.2.0", + "@semantic-release/github": "^5.0.0", + "@semantic-release/npm": "^5.0.1", + "@semantic-release/release-notes-generator": "^7.0.0", + "aggregate-error": "^1.0.0", + "cosmiconfig": "^5.0.1", + "debug": "^3.1.0", + "env-ci": "^2.0.0", + "execa": "^0.10.0", + "figures": "^2.0.0", + "find-versions": "^2.0.0", + "get-stream": "^4.0.0", + "git-log-parser": "^1.2.0", + "git-url-parse": "^10.0.1", + "hook-std": "^1.1.0", + "hosted-git-info": "^2.7.1", + "lodash": "^4.17.4", + "marked": "^0.5.0", + "marked-terminal": "^3.0.0", + "p-locate": "^3.0.0", + "p-reduce": "^1.0.0", + "read-pkg-up": "^4.0.0", + "resolve-from": "^4.0.0", + "semver": "^5.4.1", + "signale": "^1.2.1", + "yargs": "^12.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.0.0.tgz", + "integrity": "sha512-FneLKMENeOR7wOK0/ZXCh+lwqtnPwkeunJjRN28LPqzGvNAhYvrTAhXv6xDm4vsJ0M7lcRbIYHQudKsSy2RtSQ==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "semantic-release-docker": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semantic-release-docker/-/semantic-release-docker-2.1.0.tgz", + "integrity": "sha512-jdXCfSuUVOH4GEYMyfmBTtH3VdJHEPUfhH4+1PI3rCplDbcfZ7pDmfuOySZhPlfkFHOJvOoVAHvXkdqOYoUxVg==", + "dev": true, + "requires": { + "@semantic-release/error": "^2.1.0", + "execa": "^0.10.0" + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true + }, + "semver-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", + "integrity": "sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "signale": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.2.1.tgz", + "integrity": "sha512-yY7GbeTGqDLC2ggcXR9hyzcgZnNT+cooPAizWRpUOHYd0DtNVRXhMqM3+F6ZbKav9oCg1r/YtJaB250IAhn/Hg==", + "dev": true, + "requires": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk=", + "dev": true + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "dev": true + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "dev": true, + "requires": { + "through2": "^2.0.2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "text-extensions": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.7.0.tgz", + "integrity": "sha512-AKXZeDq230UaSzaO5s3qQUZOaC7iKbzq0jOFL614R7d9R593HLqAOL0cYoqLdkNrjBSOdmoQI06yigq1TSBXAg==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "optional": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "optional": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url-join": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", + "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=", + "dev": true + }, + "url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", + "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } +} diff --git a/package.json b/package.json index 828931f..f4e461e 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,25 @@ { - "name": "php-language-server", - "version": "0.0.0-development", "private": true, - "scripts": { - "commitmsg": "validate-commit-msg", - "semantic-release": "semantic-release" + "repository": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server.git" }, "devDependencies": { - "@semantic-release/github": "^2.0.0", - "@semantic-release/last-release-git-tag": "^2.0.0", - "cz-conventional-changelog": "^2.0.0", - "husky": "^0.14.3", - "semantic-release": "^11.0.0", - "semantic-release-docker": "^2.0.0", - "validate-commit-msg": "^2.14.0" - }, - "config": { - "commitizen": { - "path": "./node_modules/cz-conventional-changelog" - } + "@semantic-release/exec": "^3.1.0", + "semantic-release": "^15.9.9", + "semantic-release-docker": "^2.1.0" }, "release": { "verifyConditions": [ "@semantic-release/github", "semantic-release-docker" ], - "getLastRelease": "@semantic-release/last-release-git-tag", + "prepare": [ + { + "path": "@semantic-release/exec", + "cmd": "composer install --prefer-dist --no-interaction && docker build -t felixfbecker/php-language-server ." + } + ], "publish": [ "@semantic-release/github", { @@ -33,9 +27,5 @@ "name": "felixfbecker/php-language-server" } ] - }, - "repository": { - "type": "git", - "url": "https://github.com/felixfbecker/php-language-server.git" } }