1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-02 17:52:45 +01:00
psalm/tests/LanguageServer/CompletionTest.php
Ivan Sidorov 10402c426b Partial revert "Fix auto completion by partial property or method"
Filtering is not necessary. Clients using LSP should filter the results themselves.
That's what it says in the documentation.

This reverts commit d6faff2844.
2024-01-26 12:23:36 +00:00

1559 lines
47 KiB
PHP

<?php
namespace Psalm\Tests\LanguageServer;
use LanguageServerProtocol\Position;
use Psalm\Codebase;
use Psalm\Context;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Internal\Provider\FakeFileProvider;
use Psalm\Internal\Provider\Providers;
use Psalm\Tests\Internal\Provider\FakeFileReferenceCacheProvider;
use Psalm\Tests\Internal\Provider\ParserInstanceCacheProvider;
use Psalm\Tests\Internal\Provider\ProjectCacheProvider;
use Psalm\Tests\TestCase;
use Psalm\Tests\TestConfig;
use Psalm\Type;
use function count;
class CompletionTest extends TestCase
{
protected Codebase $codebase;
public function setUp(): void
{
parent::setUp();
$this->file_provider = new FakeFileProvider();
$config = new TestConfig();
$providers = new Providers(
$this->file_provider,
new ParserInstanceCacheProvider(),
null,
null,
new FakeFileReferenceCacheProvider(),
new ProjectCacheProvider(),
);
$this->codebase = new Codebase($config, $providers);
$this->project_analyzer = new ProjectAnalyzer(
$config,
$providers,
null,
[],
1,
null,
$this->codebase,
);
$this->project_analyzer->setPhpVersion('7.3', 'tests');
$this->project_analyzer->getCodebase()->store_node_types = true;
}
public function testCompletionOnThisWithNoAssignment(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected $a;
public function foo() {
$this->
}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A&static', '->', 213], $codebase->getCompletionDataAtPosition('somefile.php', new Position(8, 31)));
}
public function testCompletionOnThisWithAssignmentBelow(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected $a;
public function foo() : self {
$this->
$a = "foo";
}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A&static', '->', 220], $codebase->getCompletionDataAtPosition('somefile.php', new Position(8, 31)));
}
public function testCompletionOnThisWithIfBelow(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected $a;
public function foo() : self {
$this
if(rand(0, 1)) {}
}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$codebase->addTemporaryFileChanges(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected $a;
public function foo() : self {
$this->
if(rand(0, 1)) {}
}
}',
);
$codebase->reloadFiles($this->project_analyzer, ['somefile.php']);
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
$this->assertSame(['B\A&static', '->', 220], $codebase->getCompletionDataAtPosition('somefile.php', new Position(8, 31)));
}
public function testCompletionOnSelfWithIfBelow(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected static $a;
public function foo() : self {
A
if(rand(0, 1)) {}
}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$codebase->addTemporaryFileChanges(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected static $a;
public function foo() : self {
A::
if(rand(0, 1)) {}
}
}',
);
$codebase->reloadFiles($this->project_analyzer, ['somefile.php']);
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
$this->assertSame(['B\A', '::', 223], $codebase->getCompletionDataAtPosition('somefile.php', new Position(8, 27)));
}
public function testCompletionOnSelfWithListBelow(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected static $a;
public function foo() : self {
A
list($a, $b) = $c;
}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$codebase->addTemporaryFileChanges(
'somefile.php',
'<?php
namespace B;
class A {
/** @var int|null */
protected static $a;
public function foo() : self {
A::
list($a, $b) = $c;
}
}',
);
$codebase->reloadFiles($this->project_analyzer, ['somefile.php']);
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
$this->assertSame(['B\A', '::', 223], $codebase->getCompletionDataAtPosition('somefile.php', new Position(8, 27)));
}
public function testCompletionOnThisProperty(): void
{
$codebase = $this->codebase;
$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->
}
}',
);
$codebase = $this->codebase;
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\C', '->', 454], $codebase->getCompletionDataAtPosition('somefile.php', new Position(16, 39)));
}
public function testCompletionOnThisPropertyWithCharacter(): void
{
$codebase = $this->codebase;
$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->codebase;
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\C', '->', 455], $codebase->getCompletionDataAtPosition('somefile.php', new Position(16, 40)));
}
public function testCompletionOnThisPropertyWithAnotherCharacter(): void
{
$codebase = $this->codebase;
$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->codebase;
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\C', '->', 456], $codebase->getCompletionDataAtPosition('somefile.php', new Position(16, 41)));
}
public function testCompletionOnTemplatedThisProperty(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
/** @template T */
class C {
/** @var T */
private $t;
/** @param T $t */
public function __construct($t) {
$this->t = $t;
}
public function otherFunction() : void
}
class A {
/** @var C<string> */
protected $cee_me;
public function __construct() {
$this->cee_me = new C("hello");
}
public function foo() : void {
$this->cee_me->
}
}',
);
$codebase = $this->codebase;
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(25, 39));
$this->assertSame(['B\C<string>', '->', 726], $completion_data);
$completion_items = $codebase->getCompletionItemsForClassishThing($completion_data[0], $completion_data[1], true);
$this->assertCount(3, $completion_items);
}
public function testCompletionOnMethodReturnValue(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
public function foo() : self {
return $this;
}
}
function foo(A $a) {
$a->foo()->
}
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A', '->', 259], $codebase->getCompletionDataAtPosition('somefile.php', new Position(9, 31)));
}
public function testCompletionOnMethodArgument(): void
{
$codebase = $this->codebase;
$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 bar(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', '->', 298], $codebase->getCompletionDataAtPosition('somefile.php', new Position(11, 32)));
}
public function testCompletionOnMethodReturnValueWithArgument(): void
{
$codebase = $this->codebase;
$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 bar(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', '->', 299], $codebase->getCompletionDataAtPosition('somefile.php', new Position(11, 33)));
}
public function testCompletionOnVariableWithWhitespace(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {}
function bar(A $a) {
$a ->
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A', '->', 126], $codebase->getCompletionDataAtPosition('somefile.php', new Position(6, 25)));
}
public function testCompletionOnVariableWithWhitespaceAndReturn(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {}
function baz(A $a) {
$a
->
}
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A', '->', 150], $codebase->getCompletionDataAtPosition('somefile.php', new Position(7, 26)));
}
public function testCompletionOnMethodReturnValueWithWhitespace(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
public function foo() : self {
return $this;
}
}
function bar(A $a) {
$a->foo() ->
}
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A', '->', 261], $codebase->getCompletionDataAtPosition('somefile.php', new Position(10, 32)));
}
public function testCompletionOnMethodReturnValueWithWhitespaceAndReturn(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
public function foo() : self {
return $this;
}
}
function baz(A $a) {
$a->foo()
->
}
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\A', '->', 285], $codebase->getCompletionDataAtPosition('somefile.php', new Position(11, 26)));
}
public function testCompletionOnMethodReturnValueWhereParamIsClosure(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class Collection {
public function map(callable $mapper) : self {
return $this;
}
}
function bar(Collection $a) {
$a->map(function ($foo) {})->
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\Collection', '->', 312], $codebase->getCompletionDataAtPosition('somefile.php', new Position(10, 49)));
}
public function testCompletionOnMethodReturnValueWhereParamIsClosureWithStmt(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class Collection {
public function map(callable $mapper) : self {
return $this;
}
}
function baz(Collection $a) {
$a->map(function ($foo) {return $foo;})->
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['B\Collection', '->', 324], $codebase->getCompletionDataAtPosition('somefile.php', new Position(10, 61)));
}
public function testCursorPositionOnMethodCompletion(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace B;
class A {
public function bar(string $a) {
$this->
}
public function baz() {}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(5, 31));
$this->assertSame(['B\A&static', '->', 146], $completion_data);
$completion_items = $codebase->getCompletionItemsForClassishThing($completion_data[0], $completion_data[1], true);
$this->assertCount(2, $completion_items);
$this->assertSame('bar($0)', $completion_items[0]->insertText);
$this->assertSame('baz()', $completion_items[1]->insertText);
}
public function testCompletionOnNewExceptionWithoutNamespace(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
function foo() : void {
throw new Ex
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$this->assertSame(['*-Ex', 'symbol', 78], $codebase->getCompletionDataAtPosition('somefile.php', new Position(2, 32)));
}
public function testCompletionOnNewExceptionWithNamespaceNoUse(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
function foo() : void {
throw new Ex
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(4, 32));
$this->assertSame(
[
'*Bar-Ex',
'symbol',
110,
],
$completion_data,
);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertNotEmpty($completion_items);
$this->assertSame('Exception', $completion_items[0]->label);
$this->assertSame('Exception', $completion_items[0]->insertText);
$this->assertNotNull($completion_items[0]->additionalTextEdits);
$this->assertCount(1, $completion_items[0]->additionalTextEdits);
$this->assertSame('use Exception;' . "\n" . "\n", $completion_items[0]->additionalTextEdits[0]->newText);
$this->assertSame(3, $completion_items[0]->additionalTextEdits[0]->range->start->line);
$this->assertSame(16, $completion_items[0]->additionalTextEdits[0]->range->start->character);
$this->assertSame(3, $completion_items[0]->additionalTextEdits[0]->range->end->line);
$this->assertSame(16, $completion_items[0]->additionalTextEdits[0]->range->end->character);
}
public function testCompletionOnNewExceptionWithNamespaceAndUse(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
use LogicException as LogEx;
class Alpha {}
class Antelope {}
function foo() : void {
new ArrayO
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(9, 30));
$this->assertSame(
[
'*Bar-ArrayO',
'symbol',
220,
],
$completion_data,
);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertCount(1, $completion_items);
$this->assertNotNull($completion_items[0]->additionalTextEdits);
$this->assertCount(1, $completion_items[0]->additionalTextEdits);
$this->assertSame("\n" . 'use ArrayObject;', $completion_items[0]->additionalTextEdits[0]->newText);
$this->assertSame(3, $completion_items[0]->additionalTextEdits[0]->range->start->line);
$this->assertSame(44, $completion_items[0]->additionalTextEdits[0]->range->start->character);
$this->assertSame(3, $completion_items[0]->additionalTextEdits[0]->range->end->line);
$this->assertSame(44, $completion_items[0]->additionalTextEdits[0]->range->end->character);
}
public function testCompletionOnNamespaceWithFullyQualified(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar\Baz\Bat;
class B {
public function foo() : void {
\Ex
}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(5, 27));
$this->assertSame(
[
'*\Ex',
'symbol',
150,
],
$completion_data,
);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertNotEmpty($completion_items);
$this->assertSame('Exception', $completion_items[0]->label);
$this->assertSame('\Exception', $completion_items[0]->insertText);
$this->assertEmpty($completion_items[0]->additionalTextEdits);
}
public function testCompletionOnExceptionWithNamespaceAndUseInClass(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar\Baz\Bat;
class B {
public function foo() : void {
Ex
}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(5, 26));
$this->assertSame(
[
'*Bar\Baz\Bat-Ex',
'symbol',
149,
],
$completion_data,
);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertNotEmpty($completion_items);
$this->assertSame('Exception', $completion_items[0]->label);
$this->assertSame('Exception', $completion_items[0]->insertText);
$this->assertNotNull($completion_items[0]->additionalTextEdits);
$this->assertCount(1, $completion_items[0]->additionalTextEdits);
}
public function testCompletionForFunctionNames(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
/**
* My Function in a Bar
*
* @return void
*/
function my_function_in_bar() : void {
}
my_function_in',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(12, 30));
$this->assertNotNull($completion_data);
$this->assertSame('*Bar-my_function_in', $completion_data[0]);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertSame(1, count($completion_items));
$this->assertEquals('My Function in a Bar', $completion_items[0]->documentation);
}
public function testCompletionForNamespacedOverriddenFunctionNames(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
function strlen() : void {
}
strlen',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(7, 22));
$this->assertNotNull($completion_data);
$this->assertSame('*Bar-strlen', $completion_data[0]);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertSame(2, count($completion_items));
}
public function testCompletionForFunctionNamesRespectUsedNamespaces(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
use phpunit\framework as phpf;
atleaston',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(3, 25));
$this->assertNotNull($completion_data);
$this->assertSame('*Bar-atleaston', $completion_data[0]);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertSame(1, count($completion_items));
$this->assertSame('phpf\\atLeastOnce', $completion_items[0]->label);
}
public function testCompletionForFunctionNamesRespectCase(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
use phpunit\framework as phpf;
Atleaston',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(3, 25));
$this->assertNotNull($completion_data);
$this->assertSame('*Bar-Atleaston', $completion_data[0]);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertSame(0, count($completion_items));
}
public function testGetMatchingFunctionNames(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
function my_function() {
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$functions = $codebase->functions->getMatchingFunctionNames('*-array_su', 0, 'somefile.php', $codebase);
$this->assertSame(1, count($functions));
$functions = $codebase->functions->getMatchingFunctionNames('*-my_funct', 0, 'somefile.php', $codebase);
$this->assertSame(1, count($functions));
}
public function testGetMatchingFunctionNamesFromPredefinedFunctions(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$functions = $codebase->functions->getMatchingFunctionNames('*-urlencod', 0, 'somefile.php', $codebase);
$this->assertSame(1, count($functions));
}
public function testGetMatchingFunctionNamesFromUsedFunction(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Foo;
use function phpunit\framework\atleastonce;
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$functions = $codebase->functions->getMatchingFunctionNames('*Foo-atleaston', 81, 'somefile.php', $codebase);
$this->assertSame(1, count($functions));
}
public function testGetMatchingFunctionNamesFromUsedNamespace(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Foo;
use phpunit\framework;
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$functions = $codebase->functions->getMatchingFunctionNames('*Foo-atleaston', 81, 'somefile.php', $codebase);
$this->assertSame(1, count($functions));
}
public function testGetMatchingFunctionNamesFromUsedNamespaceRespectFirstCharCase(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Foo;
use phpunit\framework;
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$functions = $codebase->functions->getMatchingFunctionNames('*Foo-Atleaston', 81, 'somefile.php', $codebase);
$this->assertSame(0, count($functions));
}
public function testGetMatchingFunctionNamesWithNamespace(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Foo;
function my_function() {
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$functions = $codebase->functions->getMatchingFunctionNames('*Foo-array_su', 45, 'somefile.php', $codebase);
$this->assertSame(1, count($functions));
$functions = $codebase->functions->getMatchingFunctionNames('Foo-my_funct', 45, 'somefile.php', $codebase);
$this->assertSame(1, count($functions));
}
public function testCompletionOnInstanceofWithNamespaceAndUse(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
use LogicException as LogEx;
class Alpha {}
class Antelope {}
class Anteater {}
function foo($a) : void {
if ($a instanceof Ant) {}
}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(10, 41));
$this->assertSame(
[
'*Bar-Ant',
'symbol',
267,
],
$completion_data,
);
$completion_items = $codebase->getCompletionItemsForPartialSymbol($completion_data[0], $completion_data[2], 'somefile.php');
$this->assertCount(2, $completion_items);
}
public function testCompletionOnClassReference(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
class Alpha {
const FOO = "123";
static function add() : void {
}
}
Alpha::',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(8, 23));
$this->assertSame(['Bar\Alpha', '::', 221], $completion_data);
$completion_items = $codebase->getCompletionItemsForClassishThing($completion_data[0], $completion_data[1], true);
$this->assertCount(2, $completion_items);
}
public function testCompletionStaticMethodOnDocBlock(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
/**
* @method static void foo()
*/
class Alpha {}
Alpha::',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$position = new Position(7, 23);
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', $position);
$this->assertSame(['Bar\Alpha', '::', 177], $completion_data);
$completion_items = $codebase->getCompletionItemsForClassishThing($completion_data[0], $completion_data[1], true);
$this->assertCount(1, $completion_items);
$this->assertSame('foo()', $completion_items[0]->insertText);
}
public function testCompletionOnClassInstanceReferenceWithAssignmentAfter(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
class Alpha {
public function add() : void {}
}
$alpha = new Alpha;
$alpha->
$a = 5;',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(9, 24));
$this->assertSame(['Bar\Alpha', '->', 200], $completion_data);
$completion_items = $codebase->getCompletionItemsForClassishThing($completion_data[0], $completion_data[1], true);
$this->assertCount(1, $completion_items);
}
public function testCompletionOnClassStaticReferenceWithAssignmentAfter(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
class Alpha {
const FOO = "123";
static function add() : void {}
}
Alpha::
$a = 5;',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(8, 23));
$this->assertSame(['Bar\Alpha', '::', 201], $completion_data);
$completion_items = $codebase->getCompletionItemsForClassishThing($completion_data[0], $completion_data[1], true);
$this->assertCount(2, $completion_items);
}
public function testNoCrashOnLoopId(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
for ($x = 0; $x <= 10; $x++) {}',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
}
public function testCompletionOnArrayKey(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
$my_array = ["foo" => 1, "bar" => 2];
$my_array[]
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(2, 26));
$this->assertSame(
[
'array{bar: 2, foo: 1}',
'[',
86,
],
$completion_data,
);
$completion_items = $codebase->getCompletionItemsForArrayKeys($completion_data[0]);
$this->assertCount(2, $completion_items);
}
public function testCompletionOnNestedArrayKey(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
$my_array = ["foo" => ["bar" => 1]];
$my_array["foo"][]
',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(2, 33));
$this->assertSame(
[
'array{bar: 1}',
'[',
92,
],
$completion_data,
);
$completion_items = $codebase->getCompletionItemsForArrayKeys($completion_data[0]);
$this->assertCount(1, $completion_items);
}
public function testTypeContextForFunctionArgument(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
function my_func(string $arg_a, bool $arg_b) : string {
}
my_func()',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$type = $codebase->getTypeContextAtPosition('somefile.php', new Position(5, 24));
$this->assertSame('string', (string) $type);
}
public function testTypeContextForFunctionArgumentWithWhiteSpace(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
namespace Bar;
function my_func(string $arg_a, bool $arg_b) : string {
}
my_func( "yes", )',
);
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$type = $codebase->getTypeContextAtPosition('somefile.php', new Position(5, 32));
$this->assertSame('bool', (string) $type);
}
public function testCallStaticInInstance(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$this->addFile(
'somefile.php',
'<?php
class Foo
{
public function testFoo() {
$this->
}
public static function bar() : void {}
public function baz() : void {}
}',
);
$codebase = $this->codebase;
$codebase->file_provider->openFile('somefile.php');
$codebase->scanFiles();
$this->analyzeFile('somefile.php', new Context());
$completion_data = $codebase->getCompletionDataAtPosition('somefile.php', new Position(4, 31));
$this->assertSame(['Foo&static', '->', 129], $completion_data);
$completion_items = $codebase->getCompletionItemsForClassishThing($completion_data[0], $completion_data[1], true);
$this->assertCount(3, $completion_items);
}
public function testCompletionsForType(): void
{
$codebase = $this->codebase;
$config = $codebase->config;
$config->throw_exception = false;
$completion_items = $codebase->getCompletionItemsForType(Type::parseString('bool'));
$this->assertCount(2, $completion_items);
$completion_items = $codebase->getCompletionItemsForType(Type::parseString('true'));
$this->assertCount(1, $completion_items);
$completion_items = $codebase->getCompletionItemsForType(Type::parseString("'yes'|'no'"));
$this->assertCount(2, $completion_items);
$completion_items = $codebase->getCompletionItemsForType(Type::parseString("1|2|3"));
$this->assertCount(3, $completion_items);
// Floats not supported.
$completion_items = $codebase->getCompletionItemsForType(Type::parseString("1.0"));
$this->assertCount(0, $completion_items);
$completion_items = $codebase->getCompletionItemsForType(Type::parseString("DateTime::RFC3339"));
$this->assertCount(1, $completion_items);
}
}