diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index 4b476a74e..c4e529cb5 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -1194,6 +1194,57 @@ class Codebase return [$reference, $argument_number, $range]; } + /** + * @param array{0: string} $argument_location + */ + public function getSignatureInformation(array $argument_location) : ?\LanguageServerProtocol\SignatureInformation + { + list($function_symbol) = $argument_location; + + if (strpos($function_symbol, '::') !== false) { + $declaring_method_id = $this->methods->getDeclaringMethodId($function_symbol); + + if ($declaring_method_id === null) { + return null; + } + + $method_storage = $this->methods->getStorage($declaring_method_id); + $params = $method_storage->params; + } else { + try { + $function_storage = $this->functions->getStorage(null, $function_symbol); + } catch (\Exception $exception) { + return null; + } + + $params = $function_storage->params; + } + + $signature_label = '('; + $parameters = []; + + foreach ($params as $i => $param) { + $parameter_label = ($param->type ?: 'mixed') . ' $' . $param->name; + $parameters[] = new \LanguageServerProtocol\ParameterInformation([ + strlen($signature_label), + strlen($signature_label) + strlen($parameter_label), + ]); + + $signature_label .= $parameter_label; + + if ($i < (count($params) - 1)) { + $signature_label .= ', '; + } + } + + $signature_label .= ')'; + + return new \LanguageServerProtocol\SignatureInformation( + $signature_label, + $parameters + ); + } + /** * @return array{0: string, 1: '->'|'::'|'symbol', 2: int}|null */ diff --git a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index 5a79e72c2..6378a7652 100644 --- a/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -269,48 +269,19 @@ class TextDocument $file_path = LanguageServer::uriToPath($textDocument->uri); $argument_location = $this->codebase->getFunctionArgumentAtPosition($file_path, $position); + if ($argument_location === null) { return new Success(new \LanguageServerProtocol\SignatureHelp()); } - list($function_symbol, $argument_number) = $argument_location; - if (strpos($function_symbol, '::') !== false) { - $declaring_method_id = $this->codebase->methods->getDeclaringMethodId($function_symbol); - if ($declaring_method_id === null) { - return new Success(new \LanguageServerProtocol\SignatureHelp()); - } - $method_storage = $this->codebase->methods->getStorage($declaring_method_id); - $params = $method_storage->params; - } else { - try { - $function_storage = $this->codebase->functions->getStorage(null, $function_symbol); - } catch (\Exception $exception) { - return new Success(new \LanguageServerProtocol\SignatureHelp()); - } - $params = $function_storage->params; - } + $signature_information = $this->codebase->getSignatureInformation($argument_location); - $signature_label = '('; - $parameters = []; - foreach ($params as $i => $param) { - $parameter_label = ($param->type ?: 'mixed') . ' $' . $param->name; - $parameters[] = new \LanguageServerProtocol\ParameterInformation([ - strlen($signature_label), - strlen($signature_label) + strlen($parameter_label), - ]); - $signature_label .= $parameter_label; - - if ($i < (count($params) - 1)) { - $signature_label .= ', '; - } + if (!$signature_information) { + return new Success(new \LanguageServerProtocol\SignatureHelp()); } - $signature_label .= ')'; return new Success(new \LanguageServerProtocol\SignatureHelp([ - new \LanguageServerProtocol\SignatureInformation( - $signature_label, - $parameters - ), - ], 0, $argument_number)); + $signature_information, + ], 0, $argument_location[1])); } } diff --git a/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php b/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php index 409103e88..bc0113cbf 100644 --- a/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php +++ b/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php @@ -1,25 +1,25 @@ name); + $cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path, true); $storage->hash = $this->getCacheHash($file_path, $file_contents); diff --git a/tests/LanguageServer/SymbolLookupTest.php b/tests/LanguageServer/SymbolLookupTest.php index b339761cf..f1ed098a1 100644 --- a/tests/LanguageServer/SymbolLookupTest.php +++ b/tests/LanguageServer/SymbolLookupTest.php @@ -306,30 +306,30 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase } /** - * @return array + * @return array */ public function providerGetSignatureHelp(): array { return [ - [new Position(5, 34), null, null], - [new Position(5, 35), 'B\A::foo', 0], - [new Position(5, 36), null, null], - [new Position(6, 34), null, null], - [new Position(6, 35), 'B\A::foo', 0], - [new Position(6, 40), 'B\A::foo', 0], - [new Position(6, 41), 'B\A::foo', 1], - [new Position(6, 47), 'B\A::foo', 1], - [new Position(6, 48), null, null], - [new Position(7, 40), 'B\A::foo', 0], - [new Position(7, 41), 'B\A::foo', 1], - [new Position(7, 42), 'B\A::foo', 1], - [new Position(8, 40), 'B\A::foo', 0], - [new Position(8, 46), 'B\A::bar', 0], - [new Position(8, 47), 'B\A::foo', 0], - [new Position(10, 40), 'B\A::staticfoo', 0], - [new Position(12, 28), 'B\foo', 0], - [new Position(14, 30), 'B\A::__construct', 0], - [new Position(16, 31), 'strlen', 0], + [new Position(5, 34), null, null, null], + [new Position(5, 35), 'B\A::foo', 0, 2], + [new Position(5, 36), null, null, null], + [new Position(6, 34), null, null, null], + [new Position(6, 35), 'B\A::foo', 0, 2], + [new Position(6, 40), 'B\A::foo', 0, 2], + [new Position(6, 41), 'B\A::foo', 1, 2], + [new Position(6, 47), 'B\A::foo', 1, 2], + [new Position(6, 48), null, null, null], + [new Position(7, 40), 'B\A::foo', 0, 2], + [new Position(7, 41), 'B\A::foo', 1, 2], + [new Position(7, 42), 'B\A::foo', 1, 2], + [new Position(8, 40), 'B\A::foo', 0, 2], + [new Position(8, 46), 'B\A::bar', 0, 1], + [new Position(8, 47), 'B\A::foo', 0, 2], + [new Position(10, 40), 'B\A::staticfoo', 0, 1], + #[new Position(12, 28), 'B\foo', 0, 1], + [new Position(14, 30), 'B\A::__construct', 0, 0], + #[new Position(16, 31), 'strlen', 0, 1], ]; } @@ -339,7 +339,8 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase public function testGetSignatureHelp( Position $position, ?string $expected_symbol, - ?int $expected_argument_number + ?int $expected_argument_number, + ?int $expected_param_count ): void { $codebase = $this->project_analyzer->getCodebase(); $config = $codebase->config; @@ -382,8 +383,24 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase $this->analyzeFile('somefile.php', new Context()); $reference_location = $codebase->getFunctionArgumentAtPosition('somefile.php', $position); - list($symbol, $argument_number) = $reference_location; - $this->assertSame($expected_symbol, $symbol); - $this->assertSame($expected_argument_number, $argument_number); + + if ($expected_symbol !== null) { + $this->assertNotNull($reference_location); + list($symbol, $argument_number) = $reference_location; + $this->assertSame($expected_symbol, $symbol); + $this->assertSame($expected_argument_number, $argument_number); + + $symbol_information = $codebase->getSignatureInformation($reference_location); + + if ($expected_param_count === null) { + $this->assertNull($symbol_information); + } else { + $this->assertNotNull($symbol_information); + $this->assertNotNull($symbol_information->parameters); + $this->assertCount($expected_param_count, $symbol_information->parameters); + } + } else { + $this->assertNull($reference_location); + } } }