1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #1656 - allow chained call completion

This commit is contained in:
Brown 2019-05-17 12:38:29 -04:00
parent a1c9ad501b
commit 963d5bb901
5 changed files with 203 additions and 9 deletions

View File

@ -1091,15 +1091,22 @@ class Codebase
krsort($type_map);
$gap = null;
foreach ($type_map as $start_pos => list($end_pos, $possible_type)) {
if ($offset < $start_pos) {
continue;
}
if ($offset - $end_pos === 2 || $offset - $end_pos === 3) {
$recent_type = $possible_type;
$candidate_gap = substr($file_contents, $end_pos, 2);
break;
if ($candidate_gap === '->' || $candidate_gap === '::') {
$gap = $candidate_gap;
$recent_type = $possible_type;
break;
}
}
if ($offset - $end_pos > 3) {
@ -1109,12 +1116,11 @@ class Codebase
if (!$recent_type
|| $recent_type === 'mixed'
|| !$gap
) {
return null;
}
$gap = substr($file_contents, $end_pos, 2);
return [$recent_type, $gap];
}

View File

@ -325,7 +325,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
$codebase->analyzer->addNodeType(
$statements_analyzer->getFilePath(),
$stmt->name,
(string) $stmt->inferredType
(string) $stmt->inferredType,
$stmt
);
}

View File

@ -890,7 +890,8 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
$codebase->analyzer->addNodeType(
$statements_analyzer->getFilePath(),
$stmt->name,
(string) $stmt->inferredType
(string) $stmt->inferredType,
$stmt
);
}
}

View File

@ -902,10 +902,14 @@ class Analyzer
/**
* @return void
*/
public function addNodeType(string $file_path, PhpParser\Node $node, string $node_type)
{
public function addNodeType(
string $file_path,
PhpParser\Node $node,
string $node_type,
PhpParser\Node $parent_node = null
) {
$this->type_map[$file_path][(int)$node->getAttribute('startFilePos')] = [
(int)$node->getAttribute('endFilePos') + 1,
($parent_node ? (int)$parent_node->getAttribute('endFilePos') : (int)$node->getAttribute('endFilePos')) + 1,
$node_type
];
}

View File

@ -202,6 +202,88 @@ class CompletionTest extends \Psalm\Tests\TestCase
$this->assertSame(['B\C', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(16, 39)));
}
/**
* @return void
*/
public function testCompletionOnThisPropertyWithCharacter()
{
$codebase = $this->project_analyzer->getCodebase();
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class C {
public function otherFunction() : void
}
class A {
/** @var C */
protected $cee_me;
public function __construct() {
$this->cee_me = new C();
}
public function foo() : void {
$this->cee_me->o
}
}'
);
$codebase = $this->project_analyzer->getCodebase();
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\C', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(16, 40)));
}
/**
* @return void
*/
public function testCompletionOnThisPropertyWithAnotherCharacter()
{
$codebase = $this->project_analyzer->getCodebase();
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class C {
public function otherFunction() : void
}
class A {
/** @var C */
protected $cee_me;
public function __construct() {
$this->cee_me = new C();
}
public function foo() : void {
$this->cee_me->ot
}
}'
);
$codebase = $this->project_analyzer->getCodebase();
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(null, $codebase->getCompletionDataAtPosition('somefile.php', new Position(16, 41)));
}
/**
* @return void
*/
@ -257,4 +339,104 @@ class CompletionTest extends \Psalm\Tests\TestCase
$this->assertCount(3, $completion_items);
}
/**
* @return void
*/
public function testCompletionOnMethodReturnValue()
{
$codebase = $this->project_analyzer->getCodebase();
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
public function foo() : self {
return $this;
}
}
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(9, 31)));
}
/**
* @return void
*/
public function testCompletionOnMethodArgument()
{
$codebase = $this->project_analyzer->getCodebase();
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
public function foo(A $a) : self {
return $this;
}
}
class C {}
function (A $a, C $c) {
$a->foo($c->)
}
'
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\C', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(11, 32)));
}
/**
* @return void
*/
public function testCompletionOnMethodReturnValueWithArgument()
{
$codebase = $this->project_analyzer->getCodebase();
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
public function foo(A $a) : self {
return $this;
}
}
class C {}
function (A $a, C $c) {
$a->foo($c)->
}
'
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A', '->'], $codebase->getCompletionDataAtPosition('somefile.php', new Position(11, 33)));
}
}