mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Merge pull request #10385 from issidorov/finding-methods-and-properties-for-auto-completion-feature
Finding methods and properties for auto completion feature
This commit is contained in:
commit
44a0d4ce9f
@ -18,6 +18,7 @@ use PhpParser\Node\Arg;
|
||||
use Psalm\CodeLocation\Raw;
|
||||
use Psalm\Exception\UnanalyzedFileException;
|
||||
use Psalm\Exception\UnpopulatedClasslikeException;
|
||||
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
|
||||
use Psalm\Internal\Analyzer\ProjectAnalyzer;
|
||||
@ -69,7 +70,6 @@ use ReflectionType;
|
||||
use UnexpectedValueException;
|
||||
|
||||
use function array_combine;
|
||||
use function array_merge;
|
||||
use function array_pop;
|
||||
use function array_reverse;
|
||||
use function array_values;
|
||||
@ -1868,13 +1868,26 @@ final class Codebase
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int> $allow_visibilities
|
||||
* @param list<string> $ignore_fq_class_names
|
||||
* @return list<CompletionItem>
|
||||
*/
|
||||
public function getCompletionItemsForClassishThing(
|
||||
string $type_string,
|
||||
string $gap,
|
||||
bool $snippets_supported = false
|
||||
bool $snippets_supported = false,
|
||||
array $allow_visibilities = null,
|
||||
array $ignore_fq_class_names = []
|
||||
): array {
|
||||
if ($allow_visibilities === null) {
|
||||
$allow_visibilities = [
|
||||
ClassLikeAnalyzer::VISIBILITY_PUBLIC,
|
||||
ClassLikeAnalyzer::VISIBILITY_PROTECTED,
|
||||
ClassLikeAnalyzer::VISIBILITY_PRIVATE,
|
||||
];
|
||||
}
|
||||
$allow_visibilities[] = null;
|
||||
|
||||
$completion_items = [];
|
||||
|
||||
$type = Type::parseString($type_string);
|
||||
@ -1884,12 +1897,25 @@ final class Codebase
|
||||
try {
|
||||
$class_storage = $this->classlike_storage_provider->get($atomic_type->value);
|
||||
|
||||
$methods = array_merge(
|
||||
$class_storage->methods,
|
||||
$class_storage->pseudo_methods,
|
||||
$class_storage->pseudo_static_methods,
|
||||
);
|
||||
foreach ($methods as $method_storage) {
|
||||
$method_storages = [];
|
||||
foreach ($class_storage->declaring_method_ids as $declaring_method_id) {
|
||||
try {
|
||||
$method_storages[] = $this->methods->getStorage($declaring_method_id);
|
||||
} catch (UnexpectedValueException $e) {
|
||||
error_log($e->getMessage());
|
||||
}
|
||||
}
|
||||
if ($gap === '->') {
|
||||
$method_storages += $class_storage->pseudo_methods;
|
||||
}
|
||||
if ($gap === '::') {
|
||||
$method_storages += $class_storage->pseudo_static_methods;
|
||||
}
|
||||
|
||||
foreach ($method_storages as $method_storage) {
|
||||
if (!in_array($method_storage->visibility, $allow_visibilities)) {
|
||||
continue;
|
||||
}
|
||||
if ($method_storage->is_static || $gap === '->') {
|
||||
$completion_item = new CompletionItem(
|
||||
$method_storage->cased_name,
|
||||
@ -1918,43 +1944,51 @@ final class Codebase
|
||||
}
|
||||
}
|
||||
|
||||
$pseudo_property_types = [];
|
||||
foreach ($class_storage->pseudo_property_get_types as $property_name => $type) {
|
||||
$pseudo_property_types[$property_name] = new CompletionItem(
|
||||
str_replace('$', '', $property_name),
|
||||
CompletionItemKind::PROPERTY,
|
||||
$type->__toString(),
|
||||
null,
|
||||
'1', //sort text
|
||||
str_replace('$', '', $property_name),
|
||||
($gap === '::' ? '$' : '') .
|
||||
if ($gap === '->') {
|
||||
$pseudo_property_types = [];
|
||||
foreach ($class_storage->pseudo_property_get_types as $property_name => $type) {
|
||||
$pseudo_property_types[$property_name] = new CompletionItem(
|
||||
str_replace('$', '', $property_name),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($class_storage->pseudo_property_set_types as $property_name => $type) {
|
||||
$pseudo_property_types[$property_name] = new CompletionItem(
|
||||
str_replace('$', '', $property_name),
|
||||
CompletionItemKind::PROPERTY,
|
||||
$type->__toString(),
|
||||
null,
|
||||
'1',
|
||||
str_replace('$', '', $property_name),
|
||||
($gap === '::' ? '$' : '') .
|
||||
CompletionItemKind::PROPERTY,
|
||||
$type->__toString(),
|
||||
null,
|
||||
'1', //sort text
|
||||
str_replace('$', '', $property_name),
|
||||
);
|
||||
str_replace('$', '', $property_name),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($class_storage->pseudo_property_set_types as $property_name => $type) {
|
||||
$pseudo_property_types[$property_name] = new CompletionItem(
|
||||
str_replace('$', '', $property_name),
|
||||
CompletionItemKind::PROPERTY,
|
||||
$type->__toString(),
|
||||
null,
|
||||
'1',
|
||||
str_replace('$', '', $property_name),
|
||||
str_replace('$', '', $property_name),
|
||||
);
|
||||
}
|
||||
|
||||
$completion_items = [...$completion_items, ...array_values($pseudo_property_types)];
|
||||
}
|
||||
|
||||
$completion_items = [...$completion_items, ...array_values($pseudo_property_types)];
|
||||
|
||||
foreach ($class_storage->declaring_property_ids as $property_name => $declaring_class) {
|
||||
$property_storage = $this->properties->getStorage(
|
||||
$declaring_class . '::$' . $property_name,
|
||||
);
|
||||
try {
|
||||
$property_storage = $this->properties->getStorage(
|
||||
$declaring_class . '::$' . $property_name,
|
||||
);
|
||||
} catch (UnexpectedValueException $e) {
|
||||
error_log($e->getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($property_storage->is_static || $gap === '->') {
|
||||
if (!in_array($property_storage->visibility, $allow_visibilities)) {
|
||||
continue;
|
||||
}
|
||||
if ($property_storage->is_static === ($gap === '::')) {
|
||||
$completion_items[] = new CompletionItem(
|
||||
'$' . $property_name,
|
||||
$property_name,
|
||||
CompletionItemKind::PROPERTY,
|
||||
$property_storage->getInfo(),
|
||||
$property_storage->description,
|
||||
@ -1976,6 +2010,22 @@ final class Codebase
|
||||
$const_name,
|
||||
);
|
||||
}
|
||||
|
||||
if ($gap === '->') {
|
||||
foreach ($class_storage->namedMixins as $mixin) {
|
||||
if (in_array($mixin->value, $ignore_fq_class_names)) {
|
||||
continue;
|
||||
}
|
||||
$mixin_completion_items = $this->getCompletionItemsForClassishThing(
|
||||
$mixin->value,
|
||||
$gap,
|
||||
$snippets_supported,
|
||||
[ClassLikeAnalyzer::VISIBILITY_PUBLIC],
|
||||
[$type_string, ...$ignore_fq_class_names],
|
||||
);
|
||||
$completion_items = [...$completion_items, ...$mixin_completion_items];
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log($e->getMessage());
|
||||
continue;
|
||||
|
@ -450,6 +450,8 @@ final class Populator
|
||||
$storage->pseudo_property_get_types += $trait_storage->pseudo_property_get_types;
|
||||
$storage->pseudo_property_set_types += $trait_storage->pseudo_property_set_types;
|
||||
|
||||
$storage->pseudo_static_methods += $trait_storage->pseudo_static_methods;
|
||||
|
||||
$storage->pseudo_methods += $trait_storage->pseudo_methods;
|
||||
$storage->declaring_pseudo_method_ids += $trait_storage->declaring_pseudo_method_ids;
|
||||
}
|
||||
@ -560,6 +562,8 @@ final class Populator
|
||||
|
||||
$parent_storage->dependent_classlikes[strtolower($storage->name)] = true;
|
||||
|
||||
$storage->pseudo_static_methods += $parent_storage->pseudo_static_methods;
|
||||
|
||||
$storage->pseudo_methods += $parent_storage->pseudo_methods;
|
||||
$storage->declaring_pseudo_method_ids += $parent_storage->declaring_pseudo_method_ids;
|
||||
}
|
||||
|
@ -0,0 +1,581 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psalm\Tests\Internal\Codebase;
|
||||
|
||||
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 function array_map;
|
||||
|
||||
/**
|
||||
* Fat tests for method `getCompletionItemsForClassishThing` of class `Psalm\Codebase`.
|
||||
*/
|
||||
final class MethodGetCompletionItemsForClassishThingTest extends TestCase
|
||||
{
|
||||
private 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;
|
||||
|
||||
$this->codebase->config->throw_exception = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
protected function getCompletionLabels(string $content, string $class_name, string $gap): array
|
||||
{
|
||||
$this->addFile('somefile.php', $content);
|
||||
|
||||
$this->analyzeFile('somefile.php', new Context());
|
||||
|
||||
$items = $this->codebase->getCompletionItemsForClassishThing($class_name, $gap, true);
|
||||
|
||||
return array_map(fn($item) => $item->label, $items);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<array-key, array{0: string}>
|
||||
*/
|
||||
public function providerGaps(): iterable
|
||||
{
|
||||
return [
|
||||
'object-gap' => ['->'],
|
||||
'static-gap' => ['::'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerGaps
|
||||
*/
|
||||
public function testSimpleOnceClass(string $gap): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
/**
|
||||
* @property int $magicObjProp1
|
||||
* @property-read string $magicObjProp2
|
||||
* @method int magicObjMethod()
|
||||
* @method static string magicStaticMethod()
|
||||
*/
|
||||
class A {
|
||||
public $publicObjProp;
|
||||
protected $protectedObjProp;
|
||||
private $privateObjProp;
|
||||
|
||||
public static $publicStaticProp;
|
||||
protected static $protectedStaticProp;
|
||||
private static $privateStaticProp;
|
||||
|
||||
public function publicObjMethod() {}
|
||||
protected function protectedObjMethod() {}
|
||||
private function privateObjMethod() {}
|
||||
|
||||
public static function publicStaticMethod() {}
|
||||
protected static function protectedStaticMethod() {}
|
||||
private static function privateStaticMethod() {}
|
||||
}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', $gap);
|
||||
|
||||
$expected_labels = [
|
||||
'->' => [
|
||||
'magicObjProp1',
|
||||
'magicObjProp2',
|
||||
|
||||
'magicObjMethod',
|
||||
|
||||
'publicObjProp',
|
||||
'protectedObjProp',
|
||||
'privateObjProp',
|
||||
|
||||
'publicObjMethod',
|
||||
'protectedObjMethod',
|
||||
'privateObjMethod',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
'::' => [
|
||||
'magicStaticMethod',
|
||||
|
||||
'publicStaticProp',
|
||||
'protectedStaticProp',
|
||||
'privateStaticProp',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels[$gap], $actual_labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerGaps
|
||||
*/
|
||||
public function testAbstractClass(string $gap): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
/**
|
||||
* @property int $magicObjProp1
|
||||
* @property-read string $magicObjProp2
|
||||
* @method int magicObjMethod()
|
||||
* @method static string magicStaticMethod()
|
||||
*/
|
||||
abstract class A {
|
||||
public $publicObjProp;
|
||||
protected $protectedObjProp;
|
||||
private $privateObjProp;
|
||||
|
||||
public static $publicStaticProp;
|
||||
protected static $protectedStaticProp;
|
||||
private static $privateStaticProp;
|
||||
|
||||
abstract public function abstractPublicMethod();
|
||||
abstract protected function abstractProtectedMethod();
|
||||
|
||||
public function publicObjMethod() {}
|
||||
protected function protectedObjMethod() {}
|
||||
private function privateObjMethod() {}
|
||||
|
||||
public static function publicStaticMethod() {}
|
||||
protected static function protectedStaticMethod() {}
|
||||
private static function privateStaticMethod() {}
|
||||
}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', $gap);
|
||||
|
||||
$expected_labels = [
|
||||
'->' => [
|
||||
'magicObjProp1',
|
||||
'magicObjProp2',
|
||||
|
||||
'magicObjMethod',
|
||||
|
||||
'publicObjProp',
|
||||
'protectedObjProp',
|
||||
'privateObjProp',
|
||||
|
||||
'abstractPublicMethod',
|
||||
'abstractProtectedMethod',
|
||||
|
||||
'publicObjMethod',
|
||||
'protectedObjMethod',
|
||||
'privateObjMethod',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
'::' => [
|
||||
'magicStaticMethod',
|
||||
|
||||
'publicStaticProp',
|
||||
'protectedStaticProp',
|
||||
'privateStaticProp',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels[$gap], $actual_labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerGaps
|
||||
*/
|
||||
public function testUseTrait(string $gap): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
/**
|
||||
* @property int $magicObjProp1
|
||||
* @property-read string $magicObjProp2
|
||||
* @method int magicObjMethod()
|
||||
* @method static string magicStaticMethod()
|
||||
*/
|
||||
trait C {
|
||||
public $publicObjProp;
|
||||
protected $protectedObjProp;
|
||||
private $privateObjProp;
|
||||
|
||||
public static $publicStaticProp;
|
||||
protected static $protectedStaticProp;
|
||||
private static $privateStaticProp;
|
||||
|
||||
abstract public function abstractPublicMethod();
|
||||
abstract protected function abstractProtectedMethod();
|
||||
|
||||
public function publicObjMethod() {}
|
||||
protected function protectedObjMethod() {}
|
||||
private function privateObjMethod() {}
|
||||
|
||||
public static function publicStaticMethod() {}
|
||||
protected static function protectedStaticMethod() {}
|
||||
private static function privateStaticMethod() {}
|
||||
}
|
||||
|
||||
class A {
|
||||
use C;
|
||||
}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', $gap);
|
||||
|
||||
$expected_labels = [
|
||||
'->' => [
|
||||
'magicObjProp1',
|
||||
'magicObjProp2',
|
||||
|
||||
'magicObjMethod',
|
||||
|
||||
'publicObjProp',
|
||||
'protectedObjProp',
|
||||
'privateObjProp',
|
||||
|
||||
'abstractPublicMethod',
|
||||
'abstractProtectedMethod',
|
||||
|
||||
'publicObjMethod',
|
||||
'protectedObjMethod',
|
||||
'privateObjMethod',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
'::' => [
|
||||
'magicStaticMethod',
|
||||
'publicStaticProp',
|
||||
'protectedStaticProp',
|
||||
'privateStaticProp',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels[$gap], $actual_labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerGaps
|
||||
*/
|
||||
public function testUseTraitWithAbstractClass(string $gap): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
/**
|
||||
* @property int $magicObjProp1
|
||||
* @property-read string $magicObjProp2
|
||||
* @method int magicObjMethod()
|
||||
* @method static string magicStaticMethod()
|
||||
*/
|
||||
trait C {
|
||||
public $publicObjProp;
|
||||
protected $protectedObjProp;
|
||||
private $privateObjProp;
|
||||
|
||||
public static $publicStaticProp;
|
||||
protected static $protectedStaticProp;
|
||||
private static $privateStaticProp;
|
||||
|
||||
abstract public function abstractPublicMethod();
|
||||
abstract protected function abstractProtectedMethod();
|
||||
|
||||
public function publicObjMethod() {}
|
||||
protected function protectedObjMethod() {}
|
||||
private function privateObjMethod() {}
|
||||
|
||||
public static function publicStaticMethod() {}
|
||||
protected static function protectedStaticMethod() {}
|
||||
private static function privateStaticMethod() {}
|
||||
}
|
||||
|
||||
abstract class A {
|
||||
use C;
|
||||
}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', $gap);
|
||||
|
||||
$expected_labels = [
|
||||
'->' => [
|
||||
'magicObjProp1',
|
||||
'magicObjProp2',
|
||||
|
||||
'magicObjMethod',
|
||||
|
||||
'publicObjProp',
|
||||
'protectedObjProp',
|
||||
'privateObjProp',
|
||||
|
||||
'abstractPublicMethod',
|
||||
'abstractProtectedMethod',
|
||||
|
||||
'publicObjMethod',
|
||||
'protectedObjMethod',
|
||||
'privateObjMethod',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
'::' => [
|
||||
'magicStaticMethod',
|
||||
'publicStaticProp',
|
||||
'protectedStaticProp',
|
||||
'privateStaticProp',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
'privateStaticMethod',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels[$gap], $actual_labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerGaps
|
||||
*/
|
||||
public function testClassWithExtends(string $gap): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
/**
|
||||
* @property int $magicObjProp1
|
||||
* @property-read string $magicObjProp2
|
||||
* @method int magicObjMethod()
|
||||
* @method static string magicStaticMethod()
|
||||
*/
|
||||
class C {
|
||||
public $publicObjProp;
|
||||
protected $protectedObjProp;
|
||||
private $privateObjProp;
|
||||
|
||||
public static $publicStaticProp;
|
||||
protected static $protectedStaticProp;
|
||||
private static $privateStaticProp;
|
||||
|
||||
public function publicObjMethod() {}
|
||||
protected function protectedObjMethod() {}
|
||||
private function privateObjMethod() {}
|
||||
|
||||
public static function publicStaticMethod() {}
|
||||
protected static function protectedStaticMethod() {}
|
||||
private static function privateStaticMethod() {}
|
||||
}
|
||||
|
||||
class A extends C {
|
||||
|
||||
}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', $gap);
|
||||
|
||||
$expected_labels = [
|
||||
'->' => [
|
||||
'magicObjProp1',
|
||||
'magicObjProp2',
|
||||
|
||||
'magicObjMethod',
|
||||
|
||||
'publicObjProp',
|
||||
'protectedObjProp',
|
||||
|
||||
'publicObjMethod',
|
||||
'protectedObjMethod',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
],
|
||||
'::' => [
|
||||
'magicStaticMethod',
|
||||
'publicStaticProp',
|
||||
'protectedStaticProp',
|
||||
|
||||
'publicStaticMethod',
|
||||
'protectedStaticMethod',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels[$gap], $actual_labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerGaps
|
||||
*/
|
||||
public function testAstractClassWithInterface(string $gap): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
interface C {
|
||||
public function publicObjMethod();
|
||||
protected function protectedObjMethod();
|
||||
}
|
||||
|
||||
abstract class A implements C {
|
||||
abstract public function publicObjMethod();
|
||||
abstract protected function protectedObjMethod();
|
||||
}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', $gap);
|
||||
|
||||
$expected_labels = [
|
||||
'->' => [
|
||||
'publicObjMethod',
|
||||
'protectedObjMethod',
|
||||
],
|
||||
'::' => [],
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels[$gap], $actual_labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerGaps
|
||||
*/
|
||||
public function testClassWithAnnotationMixin(string $gap): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
/**
|
||||
* @property int $magicObjProp1
|
||||
* @property-read string $magicObjProp2
|
||||
* @method int magicObjMethod()
|
||||
* @method static string magicStaticMethod()
|
||||
*/
|
||||
class C {
|
||||
public $publicObjProp;
|
||||
protected $protectedObjProp;
|
||||
private $privateObjProp;
|
||||
|
||||
public static $publicStaticProp;
|
||||
protected static $protectedStaticProp;
|
||||
private static $privateStaticProp;
|
||||
|
||||
public function publicObjMethod() {}
|
||||
protected function protectedObjMethod() {}
|
||||
private function privateObjMethod() {}
|
||||
|
||||
public static function publicStaticMethod() {}
|
||||
protected static function protectedStaticMethod() {}
|
||||
private static function privateStaticMethod() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @mixin C
|
||||
*/
|
||||
class A {
|
||||
|
||||
}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', $gap);
|
||||
|
||||
$expected_labels = [
|
||||
'->' => [
|
||||
'magicObjProp1',
|
||||
'magicObjProp2',
|
||||
'magicObjMethod',
|
||||
|
||||
'publicObjProp',
|
||||
|
||||
'publicObjMethod',
|
||||
|
||||
'publicStaticMethod',
|
||||
],
|
||||
'::' => [],
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels[$gap], $actual_labels);
|
||||
}
|
||||
|
||||
public function testResolveCollisionWithMixin(): void
|
||||
{
|
||||
$content = <<<'EOF'
|
||||
<?php
|
||||
namespace B;
|
||||
|
||||
/** @mixin A */
|
||||
class C {
|
||||
public $myObjProp;
|
||||
}
|
||||
|
||||
/** @mixin C */
|
||||
class A {}
|
||||
EOF;
|
||||
|
||||
$actual_labels = $this->getCompletionLabels($content, 'B\A', '->');
|
||||
|
||||
$expected_labels = [
|
||||
'myObjProp',
|
||||
];
|
||||
|
||||
$this->assertEqualsCanonicalizing($expected_labels, $actual_labels);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user