mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Allow multiple class renames at once
This commit is contained in:
parent
c21e9917fa
commit
45f2d986e6
@ -30,7 +30,9 @@ class FileManipulation
|
||||
|
||||
public function getKey() : string
|
||||
{
|
||||
return sha1($this->start . ':' . $this->insertion_text);
|
||||
return $this->start === $this->end
|
||||
? ($this->start . ':' . sha1($this->insertion_text))
|
||||
: ($this->start . ':' . $this->end);
|
||||
}
|
||||
|
||||
public function transform(string $existing_contents) : string
|
||||
|
@ -838,7 +838,7 @@ class ClassLikes
|
||||
$source_classlike_storage->parent_class
|
||||
);
|
||||
|
||||
$this->airliftDocblockType(
|
||||
$this->airliftClassDefinedDocblockType(
|
||||
$replace_type,
|
||||
$destination_fq_class_name,
|
||||
$source_property_storage->stmt_location->file_path,
|
||||
@ -966,7 +966,6 @@ class ClassLikes
|
||||
string $fq_class_name,
|
||||
?string $calling_method_id
|
||||
) : bool {
|
||||
|
||||
$calling_fq_class_name = $source->getFQCLN();
|
||||
|
||||
// if we're inside a moved class static method
|
||||
@ -993,6 +992,79 @@ class ClassLikes
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we're outside a moved class, but we're changing all references to a class
|
||||
if (isset($codebase->class_transforms[strtolower($fq_class_name)])) {
|
||||
$new_fq_class_name = $codebase->class_transforms[strtolower($fq_class_name)];
|
||||
$file_manipulations = [];
|
||||
|
||||
if ($class_name_node instanceof PhpParser\Node\Identifier) {
|
||||
$destination_parts = explode('\\', $new_fq_class_name);
|
||||
|
||||
$destination_class_name = array_pop($destination_parts);
|
||||
$file_manipulations = [];
|
||||
|
||||
$file_manipulations[] = new \Psalm\FileManipulation(
|
||||
(int) $class_name_node->getAttribute('startFilePos'),
|
||||
(int) $class_name_node->getAttribute('endFilePos') + 1,
|
||||
$destination_class_name
|
||||
);
|
||||
|
||||
FileManipulationBuffer::add($source->getFilePath(), $file_manipulations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$uses_flipped = $source->getAliasedClassesFlipped();
|
||||
$uses_flipped_replaceable = $source->getAliasedClassesFlippedReplaceable();
|
||||
|
||||
$old_fq_class_name = strtolower($fq_class_name);
|
||||
|
||||
$migrated_source_fqcln = $calling_fq_class_name;
|
||||
|
||||
if ($calling_fq_class_name
|
||||
&& isset($codebase->class_transforms[strtolower($calling_fq_class_name)])
|
||||
) {
|
||||
$migrated_source_fqcln = $codebase->class_transforms[strtolower($calling_fq_class_name)];
|
||||
}
|
||||
|
||||
$source_namespace = $source->getNamespace();
|
||||
|
||||
if ($migrated_source_fqcln && $calling_fq_class_name !== $migrated_source_fqcln) {
|
||||
$new_source_parts = explode('\\', $migrated_source_fqcln);
|
||||
array_pop($new_source_parts);
|
||||
$source_namespace = implode('\\', $new_source_parts);
|
||||
}
|
||||
|
||||
if (isset($uses_flipped_replaceable[$old_fq_class_name])) {
|
||||
$alias = $uses_flipped_replaceable[$old_fq_class_name];
|
||||
unset($uses_flipped_replaceable[$old_fq_class_name]);
|
||||
$old_class_name_parts = explode('\\', $old_fq_class_name);
|
||||
$old_class_name = end($old_class_name_parts);
|
||||
if (strtolower($old_class_name) === strtolower($alias)) {
|
||||
$new_class_name_parts = explode('\\', $new_fq_class_name);
|
||||
$new_class_name = end($new_class_name_parts);
|
||||
$uses_flipped[strtolower($new_fq_class_name)] = $new_class_name;
|
||||
} else {
|
||||
$uses_flipped[strtolower($new_fq_class_name)] = $alias;
|
||||
}
|
||||
}
|
||||
|
||||
$file_manipulations[] = new \Psalm\FileManipulation(
|
||||
(int) $class_name_node->getAttribute('startFilePos'),
|
||||
(int) $class_name_node->getAttribute('endFilePos') + 1,
|
||||
Type::getStringFromFQCLN(
|
||||
$new_fq_class_name,
|
||||
$source_namespace,
|
||||
$uses_flipped,
|
||||
$migrated_source_fqcln
|
||||
)
|
||||
);
|
||||
|
||||
FileManipulationBuffer::add($source->getFilePath(), $file_manipulations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we're inside a moved class (could be a method, could be a property/class const default)
|
||||
if ($codebase->classes_to_move
|
||||
&& $calling_fq_class_name
|
||||
@ -1028,46 +1100,6 @@ class ClassLikes
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we're outside a moved class, but we're changing all references to a class
|
||||
if (isset($codebase->class_transforms[strtolower($fq_class_name)])) {
|
||||
$new_fq_class_name = $codebase->class_transforms[strtolower($fq_class_name)];
|
||||
$file_manipulations = [];
|
||||
|
||||
$uses_flipped = $source->getAliasedClassesFlipped();
|
||||
$uses_flipped_replaceable = $source->getAliasedClassesFlippedReplaceable();
|
||||
|
||||
$old_fq_class_name = strtolower($fq_class_name);
|
||||
|
||||
if (isset($uses_flipped_replaceable[$old_fq_class_name])) {
|
||||
$alias = $uses_flipped_replaceable[$old_fq_class_name];
|
||||
unset($uses_flipped_replaceable[$old_fq_class_name]);
|
||||
$old_class_name_parts = explode('\\', $old_fq_class_name);
|
||||
$old_class_name = end($old_class_name_parts);
|
||||
if (strtolower($old_class_name) === strtolower($alias)) {
|
||||
$new_class_name_parts = explode('\\', $new_fq_class_name);
|
||||
$new_class_name = end($new_class_name_parts);
|
||||
$uses_flipped[strtolower($new_fq_class_name)] = $new_class_name;
|
||||
} else {
|
||||
$uses_flipped[strtolower($new_fq_class_name)] = $alias;
|
||||
}
|
||||
}
|
||||
|
||||
$file_manipulations[] = new \Psalm\FileManipulation(
|
||||
(int) $class_name_node->getAttribute('startFilePos'),
|
||||
(int) $class_name_node->getAttribute('endFilePos') + 1,
|
||||
Type::getStringFromFQCLN(
|
||||
$new_fq_class_name,
|
||||
$source->getNamespace(),
|
||||
$uses_flipped,
|
||||
$calling_fq_class_name
|
||||
)
|
||||
);
|
||||
|
||||
FileManipulationBuffer::add($source->getFilePath(), $file_manipulations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1078,7 +1110,6 @@ class ClassLikes
|
||||
CodeLocation $type_location,
|
||||
?string $calling_method_id
|
||||
) : void {
|
||||
|
||||
$calling_fq_class_name = $source->getFQCLN();
|
||||
|
||||
$moved_type = false;
|
||||
@ -1093,34 +1124,7 @@ class ClassLikes
|
||||
|
||||
$destination_class = explode('::', $codebase->methods_to_move[strtolower($calling_method_id)])[0];
|
||||
|
||||
$this->airliftDocblockType(
|
||||
$type,
|
||||
$destination_class,
|
||||
$source->getFilePath(),
|
||||
$bounds[0],
|
||||
$bounds[1]
|
||||
);
|
||||
|
||||
$moved_type = true;
|
||||
}
|
||||
|
||||
// if we're inside a moved class (could be a method, could be a property/class const default)
|
||||
if (!$moved_type
|
||||
&& $codebase->classes_to_move
|
||||
&& $calling_fq_class_name
|
||||
&& isset($codebase->classes_to_move[strtolower($calling_fq_class_name)])
|
||||
) {
|
||||
$bounds = $type_location->getSelectionBounds();
|
||||
|
||||
$destination_class = $codebase->classes_to_move[strtolower($calling_fq_class_name)];
|
||||
|
||||
if ($type->containsClassLike(strtolower($calling_fq_class_name))) {
|
||||
$type = clone $type;
|
||||
|
||||
$type->replaceClassLike(strtolower($calling_fq_class_name), $destination_class);
|
||||
}
|
||||
|
||||
$this->airliftDocblockType(
|
||||
$this->airliftClassDefinedDocblockType(
|
||||
$type,
|
||||
$destination_class,
|
||||
$source->getFilePath(),
|
||||
@ -1146,6 +1150,22 @@ class ClassLikes
|
||||
$uses_flipped = $source->getAliasedClassesFlipped();
|
||||
$uses_flipped_replaceable = $source->getAliasedClassesFlippedReplaceable();
|
||||
|
||||
$migrated_source_fqcln = $calling_fq_class_name;
|
||||
|
||||
if ($calling_fq_class_name
|
||||
&& isset($codebase->class_transforms[strtolower($calling_fq_class_name)])
|
||||
) {
|
||||
$migrated_source_fqcln = $codebase->class_transforms[strtolower($calling_fq_class_name)];
|
||||
}
|
||||
|
||||
$source_namespace = $source->getNamespace();
|
||||
|
||||
if ($migrated_source_fqcln && $calling_fq_class_name !== $migrated_source_fqcln) {
|
||||
$new_source_parts = explode('\\', $migrated_source_fqcln);
|
||||
array_pop($new_source_parts);
|
||||
$source_namespace = implode('\\', $new_source_parts);
|
||||
}
|
||||
|
||||
if (isset($uses_flipped_replaceable[$old_fq_class_name])) {
|
||||
$alias = $uses_flipped_replaceable[$old_fq_class_name];
|
||||
unset($uses_flipped_replaceable[$old_fq_class_name]);
|
||||
@ -1164,9 +1184,9 @@ class ClassLikes
|
||||
$bounds[0],
|
||||
$bounds[1],
|
||||
$type->toNamespacedString(
|
||||
$source->getNamespace(),
|
||||
$source_namespace,
|
||||
$uses_flipped,
|
||||
null,
|
||||
$migrated_source_fqcln,
|
||||
false
|
||||
)
|
||||
);
|
||||
@ -1175,9 +1195,36 @@ class ClassLikes
|
||||
$source->getFilePath(),
|
||||
$file_manipulations
|
||||
);
|
||||
|
||||
$moved_type = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we're inside a moved class (could be a method, could be a property/class const default)
|
||||
if (!$moved_type
|
||||
&& $codebase->classes_to_move
|
||||
&& $calling_fq_class_name
|
||||
&& isset($codebase->classes_to_move[strtolower($calling_fq_class_name)])
|
||||
) {
|
||||
$bounds = $type_location->getSelectionBounds();
|
||||
|
||||
$destination_class = $codebase->classes_to_move[strtolower($calling_fq_class_name)];
|
||||
|
||||
if ($type->containsClassLike(strtolower($calling_fq_class_name))) {
|
||||
$type = clone $type;
|
||||
|
||||
$type->replaceClassLike(strtolower($calling_fq_class_name), $destination_class);
|
||||
}
|
||||
|
||||
$this->airliftClassDefinedDocblockType(
|
||||
$type,
|
||||
$destination_class,
|
||||
$source->getFilePath(),
|
||||
$bounds[0],
|
||||
$bounds[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function airliftClassLikeReference(
|
||||
@ -1215,7 +1262,7 @@ class ClassLikes
|
||||
);
|
||||
}
|
||||
|
||||
public function airliftDocblockType(
|
||||
public function airliftClassDefinedDocblockType(
|
||||
Type\Union $type,
|
||||
string $destination_fq_class_name,
|
||||
string $source_file_path,
|
||||
|
@ -419,6 +419,74 @@ class ClassMoveTest extends \Psalm\Tests\TestCase
|
||||
'Bar\Bat' => 'Bar\Baz\Bahh',
|
||||
]
|
||||
],
|
||||
'moveClassesIntoNamespace' => [
|
||||
'<?php
|
||||
namespace Foo {
|
||||
class A {
|
||||
/** @var ?B */
|
||||
public $x = null;
|
||||
/** @var ?A */
|
||||
public $y = null;
|
||||
/** @var A|B|C|null */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Foo {
|
||||
class B {
|
||||
/** @var ?A */
|
||||
public $x = null;
|
||||
/** @var ?B */
|
||||
public $y = null;
|
||||
/** @var A|B|C|null */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Foo {
|
||||
class C {
|
||||
/** @var ?A */
|
||||
public $x = null;
|
||||
/** @var ?B */
|
||||
public $y = null;
|
||||
}
|
||||
}',
|
||||
'<?php
|
||||
namespace Bar\Baz {
|
||||
class A {
|
||||
/** @var null|B */
|
||||
public $x = null;
|
||||
/** @var null|self */
|
||||
public $y = null;
|
||||
/** @var null|self|B|\Foo\C */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Bar\Baz {
|
||||
class B {
|
||||
/** @var null|A */
|
||||
public $x = null;
|
||||
/** @var null|self */
|
||||
public $y = null;
|
||||
/** @var null|A|self|\Foo\C */
|
||||
public $z = null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Foo {
|
||||
class C {
|
||||
/** @var null|\Bar\Baz\A */
|
||||
public $x = null;
|
||||
/** @var null|\Bar\Baz\B */
|
||||
public $y = null;
|
||||
}
|
||||
}',
|
||||
[
|
||||
'Foo\A' => 'Bar\Baz\A',
|
||||
'Foo\B' => 'Bar\Baz\B',
|
||||
]
|
||||
],
|
||||
'moveClassDeeperIntoNamespaceAdjustUseWithAlias' => [
|
||||
'<?php
|
||||
namespace Foo {
|
||||
|
Loading…
Reference in New Issue
Block a user