Repository: rrrene/credo (elixir)

Issue:
+2 ABCSize for each `@impl SomeModule` inside quote block
### Environment

* Credo version (`mix credo -v`):
```
$ mix credo -v
1.7.10-ref.(HEAD odłączone na origin/master).2e15cdd2
```
* Erlang/Elixir version (`elixir -v`):
```
$ elixir -v
Erlang/OTP 27 [erts-15.2] [source] [64-bit] [smp:32:32] [ds:32:32:10] [async-threads:1] [jit:ns]

Elixir 1.18.0-rc.0 (f00f759) (compiled with Erlang/OTP 27)
```
* Operating system:
> Gentoo Linux using `desktop/plasma` profile for version `23.0` and with kernel version `6.6.58`.

### What were you trying to do?

I have found that size of `@impl SomeModule` is different inside `quote do … end` block. In result in 2 of my macros by simply commenting out all `@impl` lines changes the `credo` result from fail to pass. It's giving +2 size for each such line which is really a lot having in mind the default limit is 30. Think that just 2 **same** lines makes more than 10% check size.

### Expected outcome

In "normal" case it have `0.0` result, so if we use it as a starting point it should be as same within `quote` block or at least it should be more realistic (not more than 1 per line I guess) + don't increment with 2 or more same lines. Of course nowhere in code I have so many `@impl`, but I have noticed that those are increasing size way too much.

### Actual outcome

```elixir
source1 = """
@impl SomeModule
"""

{:ok, ast1} = Credo.Code.ast(source1)
Credo.Check.Refactor.ABCSize.abc_size_for(ast1, [])
0

source2 = """
def sample do
  quote do
    @impl SomeModule
  end
end
"""

{:ok, ast2} = Credo.Code.ast(source2)
Credo.Check.Refactor.ABCSize.abc_size_for(ast2, [])
3.0

source3 = """
def sample do
  quote do
    @impl SomeModule
    @impl SomeModule
  end
end
"""

{:ok, ast3} = Credo.Code.ast(source3)
Credo.Check.Refactor.ABCSize.abc_size_for(ast3, [])
5.0

many_impls = "@impl SomeModule" |> List.duplicate(30) |> Enum.join("\n    ")

source4 = """
def sample do
  quote do
    #{many_impls}
  end
end
"""

{:ok, ast4} = Credo.Code.ast(source4)
Credo.Check.Refactor.ABCSize.abc_size_for(ast4, [])
61.0
```

**Edit**: People with same problem can temporary workaround it by setting `[excluded_functions: ["@"]]` as check options.

Pull request:
fixes for ABCSize check
- ignore unquote() calls (used in macros)
- always count module attribute call as 1

fixes #1172

Patch:
diff --git a/lib/credo/check/refactor/abc_size.ex b/lib/credo/check/refactor/abc_size.ex
index 670247b1..5eeb75f2 100644
--- a/lib/credo/check/refactor/abc_size.ex
+++ b/lib/credo/check/refactor/abc_size.ex
@@ -154,7 +154,22 @@ defmodule Credo.Check.Refactor.ABCSize do
     {nil, acc}
   end
 
+  # ignore unquote calls
+  defp traverse_abc({:unquote, _, [arg]}, acc, _excluded_functions) do
+    {arg, acc}
+  end
+
   # A - assignments
+
+  # always count module attribute call as 1
+  defp traverse_abc(
+         {:@, _meta, _args},
+         [a: a, b: b, c: c, var_names: var_names],
+         _excluded_functions
+       ) do
+    {nil, [a: a + 1, b: b, c: c, var_names: var_names]}
+  end
+
   defp traverse_abc(
          {:=, _meta, [lhs | rhs]},
          [a: a, b: b, c: c, var_names: var_names],


Test patch:
diff --git a/test/credo/check/refactor/abc_size_test.exs b/test/credo/check/refactor/abc_size_test.exs
index ff228ff7..4bc3d93a 100644
--- a/test/credo/check/refactor/abc_size_test.exs
+++ b/test/credo/check/refactor/abc_size_test.exs
@@ -218,6 +218,33 @@ defmodule Credo.Check.Refactor.ABCSizeTest do
     |> Float.round(2)
   end
 
+  test "it should return the same ABC size with and without unquote call" do
+    with_unquote_source = """
+    def foo do
+      some_var = unquote(5)
+    end
+    """
+
+    without_unquote_source = """
+    def foo do
+      some_var = 5
+    end
+    """
+
+    assert abc_size(with_unquote_source) == abc_size(without_unquote_source)
+  end
+
+  @tag :current
+  test "module attributs should always be counted as 1" do
+    source = """
+    def foo do
+      @attr_name
+    end
+    """
+
+    assert abc_size(source) == 1.0
+  end
+
   test "it should return the correct ABC size for nullary function calls" do
     source = """
     def foo() do