diff --git a/src/Psalm/Codebase.php b/src/Psalm/Codebase.php index 2390ff62b..1215654c2 100644 --- a/src/Psalm/Codebase.php +++ b/src/Psalm/Codebase.php @@ -1150,11 +1150,16 @@ class Codebase $gap = null; - foreach ($type_map as $start_pos => list($end_pos, $possible_type)) { + foreach ($type_map as $start_pos => list($end_pos_excluding_whitespace, $possible_type)) { if ($offset < $start_pos) { continue; } + $num_whitespace_bytes = preg_match('/\G\s+/', $file_contents, $matches, 0, $end_pos_excluding_whitespace) + ? strlen($matches[0]) + : 0; + $end_pos = $end_pos_excluding_whitespace + $num_whitespace_bytes; + if ($offset - $end_pos === 2 || $offset - $end_pos === 3) { $candidate_gap = substr($file_contents, $end_pos, 2); diff --git a/tests/LanguageServer/CompletionTest.php b/tests/LanguageServer/CompletionTest.php index 48f8f319a..8ce48c5c0 100644 --- a/tests/LanguageServer/CompletionTest.php +++ b/tests/LanguageServer/CompletionTest.php @@ -434,4 +434,78 @@ class CompletionTest extends \Psalm\Tests\TestCase $this->analyzeFile('somefile.php', new Context()); $this->assertSame(['B\A', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(11, 33))); } + + /** + * @return void + */ + public function testCompletionOnVariableWithWhitespace() + { + $codebase = $this->project_analyzer->getCodebase(); + $config = $codebase->config; + $config->throw_exception = false; + + $this->addFile( + 'somefile.php', + ' + } + + function (A $a) { + $a + -> + } + ' + ); + + $codebase->file_provider->openFile('somefile.php'); + $codebase->scanFiles(); + + $this->analyzeFile('somefile.php', new Context()); + $this->assertSame(['B\A', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(6, 25))); + $this->assertSame(['B\A', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(11, 26))); + } + + /** + * @return void + */ + public function testCompletionOnMethodReturnValueWithWhitespace() + { + $codebase = $this->project_analyzer->getCodebase(); + $config = $codebase->config; + $config->throw_exception = false; + + $this->addFile( + 'somefile.php', + 'foo() -> + } + + function (A $a) { + $a->foo() + -> + } + ' + ); + + $codebase->file_provider->openFile('somefile.php'); + $codebase->scanFiles(); + + $this->analyzeFile('somefile.php', new Context()); + $this->assertSame(['B\A', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(10, 32))); + $this->assertSame(['B\A', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(15, 26))); + } }