mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Migrate class references in static calls
This commit is contained in:
parent
7ec9818192
commit
7e4de611bf
@ -8,16 +8,31 @@ class Aliases
|
||||
*/
|
||||
public $uses;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $uses_flipped;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $functions;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $functions_flipped;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $constants;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $constants_flipped;
|
||||
|
||||
/** @var string|null */
|
||||
public $namespace;
|
||||
|
||||
@ -26,16 +41,25 @@ class Aliases
|
||||
* @param array<string, string> $uses
|
||||
* @param array<string, string> $functions
|
||||
* @param array<string, string> $constants
|
||||
* @param array<string, string> $uses_flipped
|
||||
* @param array<string, string> $functions_flipped
|
||||
* @param array<string, string> $constants_flipped
|
||||
*/
|
||||
public function __construct(
|
||||
$namespace = null,
|
||||
array $uses = [],
|
||||
array $functions = [],
|
||||
array $constants = []
|
||||
array $constants = [],
|
||||
array $uses_flipped = [],
|
||||
array $functions_flipped = [],
|
||||
array $constants_flipped = []
|
||||
) {
|
||||
$this->namespace = $namespace;
|
||||
$this->uses = $uses;
|
||||
$this->functions = $functions;
|
||||
$this->constants = $constants;
|
||||
$this->uses_flipped = $uses_flipped;
|
||||
$this->functions_flipped = $functions_flipped;
|
||||
$this->constants_flipped = $constants_flipped;
|
||||
}
|
||||
}
|
||||
|
@ -821,6 +821,8 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
}
|
||||
}
|
||||
|
||||
$moved_call = false;
|
||||
|
||||
foreach ($codebase->call_transforms as $original_pattern => $transformation) {
|
||||
if ($declaring_method_id
|
||||
&& strtolower($declaring_method_id) . '\((.*\))' === $original_pattern
|
||||
@ -851,10 +853,28 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
);
|
||||
|
||||
FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
|
||||
|
||||
$moved_call = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$moved_call
|
||||
&& $codebase->method_migrations
|
||||
&& $context->calling_method_id
|
||||
&& isset($codebase->method_migrations[strtolower($context->calling_method_id)])
|
||||
) {
|
||||
$destination_method_id = $codebase->method_migrations[strtolower($context->calling_method_id)];
|
||||
|
||||
$codebase->classlikes->airliftClassLikeReference(
|
||||
$fq_class_name,
|
||||
explode('::', $destination_method_id)[0],
|
||||
$statements_analyzer->getFilePath(),
|
||||
(int) $stmt->class->getAttribute('startFilePos'),
|
||||
(int) $stmt->class->getAttribute('endFilePos') + 1
|
||||
);
|
||||
}
|
||||
|
||||
if ($config->after_method_checks) {
|
||||
$file_manipulations = [];
|
||||
|
||||
|
@ -143,25 +143,15 @@ class ConstFetchAnalyzer
|
||||
if ($codebase->method_migrations
|
||||
&& $context->calling_method_id
|
||||
&& isset($codebase->method_migrations[strtolower($context->calling_method_id)])
|
||||
&& strtolower(explode('::', $context->calling_method_id)[0]) === strtolower($fq_class_name)
|
||||
&& !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
|
||||
) {
|
||||
$file_manipulations = [];
|
||||
$destination_method_id = $codebase->method_migrations[strtolower($context->calling_method_id)];
|
||||
|
||||
$file_manipulations[] = new \Psalm\FileManipulation(
|
||||
(int) $stmt->class->getAttribute('startFilePos'),
|
||||
(int) $stmt->class->getAttribute('endFilePos') + 1,
|
||||
Type::getStringFromFQCLN(
|
||||
$fq_class_name,
|
||||
null,
|
||||
[],
|
||||
null
|
||||
)
|
||||
);
|
||||
|
||||
\Psalm\Internal\FileManipulation\FileManipulationBuffer::add(
|
||||
$codebase->classlikes->airliftClassLikeReference(
|
||||
$fq_class_name,
|
||||
explode('::', $destination_method_id)[0],
|
||||
$statements_analyzer->getFilePath(),
|
||||
$file_manipulations
|
||||
(int) $stmt->class->getAttribute('startFilePos'),
|
||||
(int) $stmt->class->getAttribute('endFilePos') + 1
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -785,6 +785,41 @@ class ClassLikes
|
||||
FileManipulationBuffer::addCodeMigrations($code_migrations);
|
||||
}
|
||||
|
||||
public function airliftClassLikeReference(
|
||||
string $fq_class_name,
|
||||
string $destination_fq_class_name,
|
||||
string $source_file_path,
|
||||
int $source_start,
|
||||
int $source_end
|
||||
) : void {
|
||||
$project_analyzer = \Psalm\Internal\Analyzer\ProjectAnalyzer::getInstance();
|
||||
$codebase = $project_analyzer->getCodebase();
|
||||
|
||||
$destination_class_storage = $codebase->classlike_storage_provider->get($destination_fq_class_name);
|
||||
|
||||
if (!$destination_class_storage->aliases) {
|
||||
throw new \UnexpectedValueException('Aliases should not be null');
|
||||
}
|
||||
|
||||
$file_manipulations = [];
|
||||
|
||||
$file_manipulations[] = new \Psalm\FileManipulation(
|
||||
$source_start,
|
||||
$source_end,
|
||||
Type::getStringFromFQCLN(
|
||||
$fq_class_name,
|
||||
$destination_class_storage->aliases->namespace,
|
||||
$destination_class_storage->aliases->uses_flipped,
|
||||
$destination_class_storage->name
|
||||
)
|
||||
);
|
||||
|
||||
FileManipulationBuffer::add(
|
||||
$source_file_path,
|
||||
$file_manipulations
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class_name
|
||||
* @param mixed $visibility
|
||||
|
@ -165,7 +165,10 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
$node->name ? implode('\\', $node->name->parts) : '',
|
||||
$this->aliases->uses,
|
||||
$this->aliases->functions,
|
||||
$this->aliases->constants
|
||||
$this->aliases->constants,
|
||||
$this->aliases->uses_flipped,
|
||||
$this->aliases->functions_flipped,
|
||||
$this->aliases->constants_flipped
|
||||
);
|
||||
} elseif ($node instanceof PhpParser\Node\Stmt\Use_) {
|
||||
foreach ($node->uses as $use) {
|
||||
@ -176,14 +179,17 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $node->type) {
|
||||
case PhpParser\Node\Stmt\Use_::TYPE_FUNCTION:
|
||||
$this->aliases->functions[strtolower($use_alias)] = $use_path;
|
||||
$this->aliases->functions_flipped[strtolower($use_path)] = $use_alias;
|
||||
break;
|
||||
|
||||
case PhpParser\Node\Stmt\Use_::TYPE_CONSTANT:
|
||||
$this->aliases->constants[$use_alias] = $use_path;
|
||||
$this->aliases->constants_flipped[$use_path] = $use_alias;
|
||||
break;
|
||||
|
||||
case PhpParser\Node\Stmt\Use_::TYPE_NORMAL:
|
||||
$this->aliases->uses[strtolower($use_alias)] = $use_path;
|
||||
$this->aliases->uses_flipped[strtolower($use_path)] = $use_alias;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -197,14 +203,17 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $node->type) {
|
||||
case PhpParser\Node\Stmt\Use_::TYPE_FUNCTION:
|
||||
$this->aliases->functions[strtolower($use_alias)] = $use_path;
|
||||
$this->aliases->functions_flipped[strtolower($use_path)] = $use_alias;
|
||||
break;
|
||||
|
||||
case PhpParser\Node\Stmt\Use_::TYPE_CONSTANT:
|
||||
$this->aliases->constants[$use_alias] = $use_path;
|
||||
$this->aliases->constants_flipped[$use_path] = $use_alias;
|
||||
break;
|
||||
|
||||
case PhpParser\Node\Stmt\Use_::TYPE_NORMAL:
|
||||
$this->aliases->uses[strtolower($use_alias)] = $use_path;
|
||||
$this->aliases->uses_flipped[strtolower($use_path)] = $use_alias;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -809,6 +818,7 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
$storage->location = $name_location;
|
||||
$storage->user_defined = !$this->codebase->register_stub_files;
|
||||
$storage->stubbed = $this->codebase->register_stub_files;
|
||||
$storage->aliases = $this->aliases;
|
||||
|
||||
$doc_comment = $node->getDocComment();
|
||||
|
||||
@ -2739,8 +2749,6 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
|
||||
} else {
|
||||
$storage->public_class_constant_nodes[$const->name->name] = $const->value;
|
||||
}
|
||||
|
||||
$storage->aliases = $this->aliases;
|
||||
}
|
||||
|
||||
if ($deprecated) {
|
||||
|
@ -162,39 +162,6 @@ class MoveMethodTest extends \Psalm\Tests\TestCase
|
||||
[
|
||||
]
|
||||
],
|
||||
'moveEmptyStaticMethodShorterOnly' => [
|
||||
'<?php
|
||||
namespace Ns;
|
||||
|
||||
class A {
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function Fedbca() : void {}
|
||||
}
|
||||
|
||||
class B {
|
||||
}',
|
||||
'<?php
|
||||
namespace Ns;
|
||||
|
||||
class A {
|
||||
|
||||
}
|
||||
|
||||
class B {
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function Foo() : void {}
|
||||
}',
|
||||
[
|
||||
'ns\a::fedbca' => 'Ns\B::Foo',
|
||||
],
|
||||
[
|
||||
]
|
||||
],
|
||||
'moveStaticMethodOnly' => [
|
||||
'<?php
|
||||
namespace Ns;
|
||||
@ -207,10 +174,17 @@ class MoveMethodTest extends \Psalm\Tests\TestCase
|
||||
*/
|
||||
public static function Foo() : void {
|
||||
echo self::C;
|
||||
echo A::C;
|
||||
self::Bar();
|
||||
A::Bar();
|
||||
echo \Ns\B::D;
|
||||
}
|
||||
|
||||
public static function Bar() : void {}
|
||||
}
|
||||
|
||||
class B {
|
||||
const D = 5;
|
||||
}',
|
||||
'<?php
|
||||
namespace Ns;
|
||||
@ -219,15 +193,22 @@ class MoveMethodTest extends \Psalm\Tests\TestCase
|
||||
const C = 5;
|
||||
|
||||
|
||||
|
||||
public static function Bar() : void {}
|
||||
}
|
||||
|
||||
class B {
|
||||
const D = 5;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function Fedbca() : void {
|
||||
echo \Ns\A::C;
|
||||
echo A::C;
|
||||
echo A::C;
|
||||
A::Bar();
|
||||
A::Bar();
|
||||
echo self::D;
|
||||
}
|
||||
}',
|
||||
[
|
||||
@ -235,46 +216,6 @@ class MoveMethodTest extends \Psalm\Tests\TestCase
|
||||
],
|
||||
[]
|
||||
],
|
||||
'moveStaticMethodShorterOnly' => [
|
||||
'<?php
|
||||
namespace Ns;
|
||||
|
||||
class A {
|
||||
const C = 5;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function Fedbca() : void {
|
||||
echo self::C;
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
}',
|
||||
'<?php
|
||||
namespace Ns;
|
||||
|
||||
class A {
|
||||
const C = 5;
|
||||
|
||||
|
||||
}
|
||||
|
||||
class B {
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function Foo() : void {
|
||||
echo \Ns\A::C;
|
||||
}
|
||||
}',
|
||||
[
|
||||
'ns\a::fedbca' => 'Ns\B::Foo',
|
||||
],
|
||||
[]
|
||||
],
|
||||
'moveStaticMethodAndReferencesFromAbove' => [
|
||||
'<?php
|
||||
namespace Ns;
|
||||
@ -313,7 +254,7 @@ class MoveMethodTest extends \Psalm\Tests\TestCase
|
||||
* @return void
|
||||
*/
|
||||
public static function Fe() : void {
|
||||
echo \Ns\A::C;
|
||||
echo A::C;
|
||||
}
|
||||
}',
|
||||
[
|
||||
@ -355,7 +296,7 @@ class MoveMethodTest extends \Psalm\Tests\TestCase
|
||||
* @return void
|
||||
*/
|
||||
public static function Fe() : void {
|
||||
echo \Ns\A::C;
|
||||
echo A::C;
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,6 +363,62 @@ class MoveMethodTest extends \Psalm\Tests\TestCase
|
||||
'ns1\a::foo\((.*\))' => 'Ns2\Ns3\B::Fe($1)',
|
||||
]
|
||||
],
|
||||
'moveStaticMethodAndReferencesAcrossNamespacesWithExistingUse' => [
|
||||
'<?php
|
||||
namespace Ns1 {
|
||||
class A {
|
||||
const C = 5;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function Foo() : void {
|
||||
echo self::C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Ns2\Ns3 {
|
||||
use Ns1\A;
|
||||
|
||||
class B {
|
||||
public static function bar() : void {
|
||||
\Ns1\A::Foo();
|
||||
}
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
namespace Ns1 {
|
||||
class A {
|
||||
const C = 5;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace Ns2\Ns3 {
|
||||
use Ns1\A;
|
||||
|
||||
class B {
|
||||
public static function bar() : void {
|
||||
B::Fedcba();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function Fedcba() : void {
|
||||
echo A::C;
|
||||
}
|
||||
}
|
||||
}',
|
||||
[
|
||||
'ns1\a::foo' => 'Ns2\Ns3\B::Fedcba',
|
||||
],
|
||||
[
|
||||
'ns1\a::foo\((.*\))' => 'Ns2\Ns3\B::Fedcba($1)',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user