From 45f2d986e66ad91583020b4985d1570ddf6c65cd Mon Sep 17 00:00:00 2001 From: Brown Date: Mon, 10 Jun 2019 17:09:34 -0400 Subject: [PATCH] Allow multiple class renames at once --- src/Psalm/FileManipulation.php | 4 +- src/Psalm/Internal/Codebase/ClassLikes.php | 195 +++++++++++++-------- tests/FileManipulation/ClassMoveTest.php | 68 +++++++ 3 files changed, 192 insertions(+), 75 deletions(-) diff --git a/src/Psalm/FileManipulation.php b/src/Psalm/FileManipulation.php index 4a80f19f6..77074b3e4 100644 --- a/src/Psalm/FileManipulation.php +++ b/src/Psalm/FileManipulation.php @@ -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 diff --git a/src/Psalm/Internal/Codebase/ClassLikes.php b/src/Psalm/Internal/Codebase/ClassLikes.php index 72ca41f41..a156e6e22 100644 --- a/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/src/Psalm/Internal/Codebase/ClassLikes.php @@ -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, diff --git a/tests/FileManipulation/ClassMoveTest.php b/tests/FileManipulation/ClassMoveTest.php index f71376dd1..6e119d43d 100644 --- a/tests/FileManipulation/ClassMoveTest.php +++ b/tests/FileManipulation/ClassMoveTest.php @@ -419,6 +419,74 @@ class ClassMoveTest extends \Psalm\Tests\TestCase 'Bar\Bat' => 'Bar\Baz\Bahh', ] ], + 'moveClassesIntoNamespace' => [ + ' 'Bar\Baz\A', + 'Foo\B' => 'Bar\Baz\B', + ] + ], 'moveClassDeeperIntoNamespaceAdjustUseWithAlias' => [ '