Packages
credo
1.7.19
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.7-rc.0
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.2-rc.4
1.7.2-rc.3
1.7.2-rc.2
1.7.2-rc.1
1.7.2-rc.0
1.7.1
1.7.0
1.7.0-rc.2
1.7.0-rc.1
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.4.1
1.4.0
1.4.0-rc.2
1.4.0-rc.1
1.3.2
1.3.1
1.3.0
1.3.0-rc3
1.3.0-rc2
1.3.0-rc1
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc4
1.2.0-rc3
1.2.0-rc2
1.2.0-rc1
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.1.0-rc3
1.1.0-rc2
1.1.0-rc1
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.1-rc1
1.0.0
1.0.0-rc1
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.9.0-rc8
0.9.0-rc7
0.9.0-rc6
0.9.0-rc5
0.9.0-rc4
0.9.0-rc3
0.9.0-rc2
0.9.0-rc1
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.8.0-rc7
0.8.0-rc6
0.8.0-rc5
0.8.0-rc4
0.8.0-rc3
0.8.0-rc2
0.8.0-rc1
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.6.0-rc2
0.6.0-rc1
0.5.3
0.5.2
0.5.1
0.5.0
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.10-dev
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.4.0-beta5
0.4.0-beta4
0.4.0-beta3
0.4.0-beta2
0.4.0-beta1
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.3.0-dev2
0.3.0-dev
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.0.1-dev
A static code analysis tool with a focus on code consistency and teaching.
Current section
Files
Jump to
Current section
Files
lib/credo/check/readability/alias_order.ex
defmodule Credo.Check.Readability.AliasOrder do
use Credo.Check,
id: "EX3002",
base_priority: :low,
param_defaults: [
sort_method: :alpha
],
explanations: [
check: """
Alphabetically ordered lists are more easily scannable by the reader.
# preferred
alias ModuleA
alias ModuleB
alias ModuleC
# NOT preferred
alias ModuleA
alias ModuleC
alias ModuleB
Alias should be alphabetically ordered among their group:
# preferred
alias ModuleC
alias ModuleD
alias ModuleA
alias ModuleB
# NOT preferred
alias ModuleC
alias ModuleD
alias ModuleB
alias ModuleA
Like all `Readability` issues, this one is not a technical concern.
But you can improve the odds of others reading and liking your code by making
it easier to follow.
""",
params: [
sort_method: """
The ordering method to use.
Options
- `:alpha` - Alphabetical case-insensitive sorting.
- `:ascii` - Case-sensitive sorting where upper case characters are ordered
before their lower case equivalent.
"""
]
]
@doc false
@impl true
def run(%SourceFile{} = source_file, params) do
ctx = Context.build(source_file, params, __MODULE__, %{alias_memo: [], alias_groups: []})
Credo.Code.prewalk(source_file, &walk/2, ctx)
|> extract_group_from_memo()
|> find_issues()
end
defp walk(
{:alias, _, [{:__aliases__, meta, mod_list} | _]},
%{params: %{sort_method: sort_method}} = ctx
) do
fullname = Credo.Code.Name.full(mod_list)
line = meta[:line]
candidate =
{{compare_name(fullname, sort_method), line, line},
module: fullname, trigger: fullname, column: meta[:column]}
{nil, extract_group_and_add_candidate(ctx, candidate, line)}
end
defp walk(
{:alias, _, [{{:., _, [{:__aliases__, _, base_mod_list}, :{}]}, meta, multi_mod_list}]},
%{params: %{sort_method: sort_method}} = ctx
) do
candidates = multi_candidates(base_mod_list, multi_mod_list, sort_method)
line = meta[:line]
{{compare, _, _}, _} = List.first(candidates)
candidate =
{{compare, line, meta[:closing][:line]}, [multi_aliases: candidates]}
{nil, extract_group_and_add_candidate(ctx, candidate, line)}
end
defp walk(ast, ctx) do
{ast, ctx}
end
defp multi_candidates(base_mod_list, multi_mod_list, sort_method) do
Enum.map(multi_mod_list, fn {:__aliases__, meta, mod_list} ->
fullname = Credo.Code.Name.full(base_mod_list ++ mod_list)
trigger = Credo.Code.Name.full(mod_list)
{{compare_name(fullname, sort_method), meta[:line], meta[:line]},
module: fullname, trigger: trigger, column: meta[:column]}
end)
end
defp extract_group_and_add_candidate(ctx, candidate, line) do
ctx =
if new_group?(ctx, line) do
extract_group_from_memo(ctx)
else
ctx
end
unshift(ctx, :alias_memo, candidate)
end
defp extract_group_from_memo(ctx) do
ctx
|> unshift(:alias_groups, ctx.alias_memo)
|> Map.put(:alias_memo, [])
end
defp new_group?(%{alias_memo: []}, _line), do: true
defp new_group?(%{alias_memo: [{{_, _, line_end}, _} | _]}, line) do
line != line_end + 1
end
defp compare_name(value, :alpha) do
value
|> String.downcase()
|> compare_name(nil)
end
defp compare_name(value, _sort_method) do
value
|> String.replace(~r/[\{\}]/, "")
|> String.replace(~r/,.+/, "")
end
defp find_issues(ctx) do
Enum.flat_map(ctx.alias_groups, fn group ->
group = Enum.reverse(group)
find_multi_alias_issues(ctx, group) ++ List.wrap(find_issue(ctx, group))
end)
end
defp find_multi_alias_issues(ctx, group) do
Enum.map(group, fn {_pos_tuple, meta} ->
if meta[:multi_aliases] do
find_issue(ctx, meta[:multi_aliases])
end
end)
|> Enum.reject(&is_nil/1)
end
defp find_issue(ctx, group) do
sorted = Enum.sort(group)
if group != sorted do
{first_mismatch, _} =
Enum.zip(group, sorted)
|> Enum.find(fn {a, b} -> a != b end)
{{_, line_no, _}, meta} = first_mismatch
issue_for(ctx, line_no, meta[:column], meta[:trigger], meta[:module])
end
end
defp issue_for(ctx, line_no, column, trigger, module) do
format_issue(
ctx,
message: "The alias `#{module}` is not alphabetically ordered among its group.",
trigger: trigger,
line_no: line_no,
column: column
)
end
end