1
0
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:
Matthew Brown 2019-06-01 10:07:38 -04:00
parent 7ec9818192
commit 7e4de611bf
6 changed files with 170 additions and 96 deletions

View File

@ -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;
}
}

View File

@ -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 = [];

View File

@ -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
);
}

View File

@ -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

View File

@ -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) {

View File

@ -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)',
]
],
];
}
}