From f2ed57683430d4c7843af22c6aff6a43bb8feac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Pr=C3=A9vost?= Date: Sun, 3 Jun 2018 12:08:25 -0400 Subject: [PATCH] Add new AliasOrder check --- .credo.exs | 1 + lib/credo/check/readability/alias_order.ex | 130 ++++++++++++++++++ .../check/readability/alias_order_test.exs | 2 - 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 lib/credo/check/readability/alias_order.ex diff --git a/.credo.exs b/.credo.exs index 118c31c36..38ba5d737 100644 --- a/.credo.exs +++ b/.credo.exs @@ -82,6 +82,7 @@ # ## Readability Checks # + {Credo.Check.Readability.AliasOrder}, {Credo.Check.Readability.FunctionNames}, {Credo.Check.Readability.LargeNumbers}, {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 80}, diff --git a/lib/credo/check/readability/alias_order.ex b/lib/credo/check/readability/alias_order.ex new file mode 100644 index 000000000..501dcbfd4 --- /dev/null +++ b/lib/credo/check/readability/alias_order.ex @@ -0,0 +1,130 @@ +defmodule Credo.Check.Readability.AliasOrder do + @moduledoc """ + Alphabetically ordered lists are more easily scannable by the read. + + # preferred + + alias Module1 + alias Module2 + alias Module3 + + # NOT preferred + + alias Module1 + alias Module3 + alias Module2 + + Alias should be alphabetically ordered among their group: + + # preferred + + alias Module3 + alias Module4 + + alias Module1 + alias Module2 + + # NOT preferred + + alias Module3 + alias Module4 + + alias Module2 + alias Module1 + + 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. + """ + + @explanation [check: @moduledoc] + + alias Credo.Code + alias Credo.Code.Name + + use Credo.Check, base_priority: :high + + @doc false + def run(source_file, params \\ []) do + issue_meta = IssueMeta.for(source_file, params) + + Code.prewalk(source_file, &traverse(&1, &2, issue_meta)) + end + + defp traverse({:defmodule, _, _} = ast, issues, issue_meta) do + new_issues = + ast + |> extract_alias_groups() + |> Enum.reduce([], &traverse_groups(&1, &2, issue_meta)) + + {ast, issues ++ new_issues} + end + + defp traverse(ast, issues, _issue_meta), do: {ast, issues} + + defp traverse_groups(group, acc, issue_meta) do + group + |> Enum.chunk_every(2, 1) + |> Enum.reduce_while(nil, &process_group/2) + |> case do + nil -> + acc + + {line_no, trigger} -> + acc ++ [issue_for(issue_meta, line_no, trigger)] + end + end + + defp process_group([{_, first_line_content}, {_, second_line_content} = second_line], _) + when first_line_content > second_line_content, + do: {:halt, second_line} + + defp process_group(_, _), do: {:cont, nil} + + defp extract_alias_groups({:defmodule, _, _arguments} = ast) do + ast + |> Code.postwalk(&find_alias_groups/2) + |> Enum.reverse() + |> Enum.reduce([[]], fn definition, acc -> + case definition do + nil -> + [[]] ++ acc + + definition -> + [group | groups] = acc + [group ++ [definition]] ++ groups + end + end) + |> Enum.reverse() + end + + defp find_alias_groups( + {:alias, _, [{:__aliases__, [line: line, column: _], mod_list}]} = ast, + aliases + ) do + module_names = + mod_list + |> Name.full() + |> List.wrap() + |> Enum.join() + + case aliases do + [{line_no, _} | _] when line_no != 0 and line_no != line - 1 -> + {ast, [{line, module_names}, nil] ++ aliases} + + _ -> + {ast, [{line, module_names}] ++ aliases} + end + end + + defp find_alias_groups(ast, aliases), do: {ast, aliases} + + defp issue_for(issue_meta, line_no, trigger) do + format_issue( + issue_meta, + message: "The alias `#{trigger}` is not alphabetically ordered among its group.", + trigger: trigger, + line_no: line_no + ) + end +end diff --git a/test/credo/check/readability/alias_order_test.exs b/test/credo/check/readability/alias_order_test.exs index 96d50ddff..62cf96b08 100644 --- a/test/credo/check/readability/alias_order_test.exs +++ b/test/credo/check/readability/alias_order_test.exs @@ -3,8 +3,6 @@ defmodule Credo.Check.Readability.AliasOrderTest do @described_check Credo.Check.Readability.AliasOrder - @moduletag :to_be_implemented - # # cases NOT raising issues #