@@ -60,12 +60,17 @@ defmodule Igniter.Refactors.Rename do
6060
6161 igniter = Igniter . include_all_elixir_files ( igniter )
6262
63- old_path =
64- find_module_file ( igniter , old_module_str ) ||
65- Igniter.Project.Module . proper_location ( igniter , old_module )
63+ { old_path , igniter } =
64+ case Igniter.Project.Module . find_module ( igniter , old_module ) do
65+ { :ok , { igniter , source , _zipper } } ->
66+ { Rewrite.Source . get ( source , :path ) , igniter }
67+
68+ { :error , igniter } ->
69+ { Igniter.Project.Module . proper_location ( igniter , old_module ) , igniter }
70+ end
6671
6772 new_path = Igniter.Project.Module . proper_location ( igniter , new_module )
68- affected_files = find_affected_files ( igniter , old_module_str , old_parts )
73+ affected_files = find_affected_files ( igniter , old_module_str , old_aliases , old_short )
6974
7075 igniter
7176 |> rewrite_affected_files (
@@ -935,51 +940,29 @@ defmodule Igniter.Refactors.Rename do
935940
936941 ## rename_module helpers
937942
938- defp find_module_file ( igniter , module_str ) do
939- igniter . rewrite
940- |> Rewrite . sources ( )
941- |> Enum . find_value ( fn source ->
942- path = Rewrite.Source . get ( source , :path )
943- content = Rewrite.Source . get ( source , :content )
944-
945- if String . ends_with? ( path , [ ".ex" , ".exs" ] ) &&
946- String . contains? ( content , "defmodule #{ module_str } " ) ,
947- do: path
948- end )
949- end
943+ # Substring check catches most references; AST fallback catches
944+ # `alias Ns.{..., Short, ...}` where the full name never appears literally.
945+ defp find_affected_files ( igniter , old_module_str , old_aliases , old_short ) do
946+ namespace_segs = Enum . drop ( old_aliases , - 1 )
947+ multi_alias_prefix = Enum . join ( namespace_segs , "." ) <> ".{"
950948
951- defp find_affected_files ( igniter , module_str , old_parts ) do
952949 igniter . rewrite
953950 |> Rewrite . sources ( )
954951 |> Enum . filter ( fn source ->
955952 path = Rewrite.Source . get ( source , :path )
956953 content = Rewrite.Source . get ( source , :content )
957954
958955 String . ends_with? ( path , [ ".ex" , ".exs" ] ) &&
959- ( String . contains? ( content , module_str ) ||
960- multi_alias_in_content? ( content , old_parts ) )
956+ ( String . contains? ( content , old_module_str ) ||
957+ ( namespace_segs != [ ] && String . contains? ( content , multi_alias_prefix ) &&
958+ source
959+ |> Rewrite.Source . get ( :quoted )
960+ |> Zipper . zip ( )
961+ |> has_multi_alias_for? ( namespace_segs , old_short ) ) )
961962 end )
962963 |> Enum . map ( & Rewrite.Source . get ( & 1 , :path ) )
963964 end
964965
965- # Detects `alias Foo.{Bar, Baz}` style references where the full module name
966- # (e.g. "Foo.Bar") is not present as a substring. Checks for the namespace
967- # prefix followed by ".{" and the short segment name anywhere in the content.
968- # This is a heuristic and may include false positives, but the subsequent AST
969- # passes are no-ops when nothing actually matches.
970- defp multi_alias_in_content? ( content , old_parts ) do
971- case Enum . split ( old_parts , - 1 ) do
972- { [ ] , _ } ->
973- false
974-
975- { namespace_parts , [ short ] } ->
976- namespace_str = Enum . join ( namespace_parts , "." )
977-
978- String . contains? ( content , "#{ namespace_str } .{" ) &&
979- String . contains? ( content , short )
980- end
981- end
982-
983966 defp rewrite_affected_files (
984967 igniter ,
985968 files ,
@@ -991,31 +974,22 @@ defmodule Igniter.Refactors.Rename do
991974 new_str
992975 ) do
993976 Enum . reduce ( files , igniter , fn path , igniter ->
977+ # String replace must run before the AST pass, or "Example" inside
978+ # a freshly-rendered "NewExample" gets replaced again → "NewNewExample".
994979 igniter
980+ |> replace_module_strings_in_file ( path , old_str , new_str )
995981 |> Igniter . update_elixir_file ( path , fn zipper ->
996982 zipper
997- # Short-form rename must run first: expand_alias reads the original
998- # alias declarations, which apply_module_renames would overwrite afterward.
999983 |> rename_aliased_short_forms ( old_aliases , old_short , new_short )
1000984 |> apply_module_renames ( renames )
1001985 |> then ( & { :ok , & 1 } )
1002986 end )
1003- |> replace_module_strings_in_file ( path , old_str , new_str )
1004987 end )
1005988 end
1006989
1007- # Renames short-form call sites (e.g. Bar.foo()) that resolve to old_aliases
1008- # via an alias declaration. Uses Igniter's expand_alias, which delegates to
1009- # Macro.Env.expand_alias/3 — the Elixir compiler itself. This correctly
1010- # handles plain `alias Foo.Bar` and `alias Foo.Bar, as: B` (the last form is
1011- # deliberately excluded: B.foo() stays B.foo() since the as: clause is
1012- # preserved by apply_module_renames).
1013- #
1014- # For multi-alias (`alias Foo.{Bar, Baz}`): Spitfire does not macro-expand
1015- # this form, so expand_alias cannot resolve the short name. We fall back to
1016- # an explicit AST scan via has_multi_alias_for?. The fallback also renames the
1017- # short name inside the declaration itself ([:Bar] → [:Baz] inside the braces),
1018- # which is correct because apply_module_renames only matches full alias lists.
990+ # Renames short-form call sites that resolve via `alias Foo.Bar` (plain or
991+ # multi-alias). `alias Foo.Bar, as: B` is intentionally left alone — the
992+ # as: clause keeps resolving after apply_module_renames updates the target.
1019993 defp rename_aliased_short_forms ( zipper , old_aliases , old_short , new_short ) do
1020994 namespace_segs = Enum . drop ( old_aliases , - 1 )
1021995 has_multi = has_multi_alias_for? ( Zipper . top ( zipper ) , namespace_segs , old_short )
@@ -1029,8 +1003,6 @@ defmodule Igniter.Refactors.Rename do
10291003 true
10301004
10311005 _ ->
1032- # Fallback for multi-alias: if the file declares
1033- # alias Ns.{..., OldShort, ...} we rename by convention.
10341006 has_multi
10351007 end
10361008
@@ -1046,8 +1018,7 @@ defmodule Igniter.Refactors.Rename do
10461018 end
10471019 end
10481020
1049- # Returns true when the zipper contains a multi-alias declaration of the form
1050- # alias <namespace_segs>.{..., <old_short>, ...}
1021+ # Matches `alias <namespace_segs>.{..., <old_short>, ...}`.
10511022 defp has_multi_alias_for? ( zipper , namespace_segs , old_short ) do
10521023 Zipper . find ( zipper , fn
10531024 { :alias , _ , [ { { :. , _ , [ { :__aliases__ , _ , ^ namespace_segs } , :{} ] } , _ , short_nodes } ] } ->
@@ -1089,8 +1060,8 @@ defmodule Igniter.Refactors.Rename do
10891060 end
10901061
10911062 defp move_submodule_files ( igniter , old_path , new_path ) do
1092- old_dir = module_file_dir ( old_path )
1093- new_dir = module_file_dir ( new_path )
1063+ old_dir = Path . rootname ( old_path ) <> "/"
1064+ new_dir = Path . rootname ( new_path ) <> "/"
10941065
10951066 igniter . rewrite
10961067 |> Rewrite . sources ( )
@@ -1102,17 +1073,13 @@ defmodule Igniter.Refactors.Rename do
11021073 end )
11031074 end
11041075
1105- defp module_file_dir ( path ) do
1106- path
1107- |> String . replace_suffix ( ".exs" , "" )
1108- |> String . replace_suffix ( ".ex" , "" )
1109- |> Kernel . <> ( "/" )
1110- end
1111-
11121076 defp move_module_file ( igniter , same , same ) , do: igniter
11131077
11141078 defp move_module_file ( igniter , old_path , new_path ) do
1115- Igniter . move_file ( igniter , old_path , new_path )
1079+ case Rewrite . source ( igniter . rewrite , old_path ) do
1080+ { :ok , _ } -> Igniter . move_file ( igniter , old_path , new_path )
1081+ { :error , _ } -> igniter
1082+ end
11161083 end
11171084
11181085 defp module_aliases_node_matches? ( { :__aliases__ , _meta , aliases } , target_aliases ) do
0 commit comments