• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nshkrdotcom / ElixirScope / 5cec2b5f3d6eeb9cd1b58af4e46a4f2515dd5de6

29 May 2025 07:57PM UTC coverage: 58.426% (-0.03%) from 58.453%
5cec2b5f3d6eeb9cd1b58af4e46a4f2515dd5de6

push

github

NSHkr
refactor module_data

144 of 188 new or added lines in 6 files covered. (76.6%)

2 existing lines in 2 files now uncovered.

6081 of 10408 relevant lines covered (58.43%)

3205.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.46
/lib/elixir_scope/ast_repository/module_data/ast_analyzer.ex
1
# ==============================================================================
2
# AST Analysis Component
3
# ==============================================================================
4

5
defmodule ElixirScope.ASTRepository.ModuleData.ASTAnalyzer do
6
  @moduledoc """
7
  Analyzes AST structures to extract module types, exports, and callbacks.
8
  """
9

10
  @doc """
11
  Detects the module type based on AST patterns.
12
  """
13
  @spec detect_module_type(term()) :: atom()
14
  def detect_module_type(ast) do
15
    cond do
25✔
16
      has_use_directive?(ast, GenServer) -> :genserver
4✔
17
      has_use_directive?(ast, Supervisor) -> :supervisor
21✔
18
      has_use_directive?(ast, Agent) -> :agent
21✔
19
      has_use_directive?(ast, Task) -> :task
21✔
20
      has_phoenix_controller_pattern?(ast) -> :phoenix_controller
21✔
21
      has_phoenix_live_view_pattern?(ast) -> :phoenix_live_view
19✔
22
      has_ecto_schema_pattern?(ast) -> :ecto_schema
18✔
23
      true -> :module
17✔
24
    end
25
  end
26

27
  @doc """
28
  Extracts public function definitions from AST.
29
  """
30
  @spec extract_exports(term()) :: [{atom(), non_neg_integer()}]
31
  def extract_exports(ast) do
32
    case ast do
25✔
33
      {:defmodule, _, [_module_name, [do: body]]} ->
34
        extract_function_definitions(body, :public)
24✔
35
      _ ->
1✔
36
        []
37
    end
38
  end
39

40
  @doc """
41
  Extracts OTP callback implementations from AST.
42
  """
43
  @spec extract_callbacks(term()) :: [map()]
44
  def extract_callbacks(ast) do
45
    case ast do
25✔
46
      {:defmodule, _, [_name, [do: body]]} ->
47
        extract_callback_functions(body)
24✔
48
      _ ->
1✔
49
        []
50
    end
51
  end
52

53
  # Private implementation
54
  defp has_use_directive?(ast, target_module) do
55
    case ast do
88✔
56
      {:defmodule, _, [_name, [do: body]]} ->
57
        find_use_directive(body, target_module)
84✔
58
      _ ->
4✔
59
        false
60
    end
61
  end
62

63
  defp find_use_directive({:__block__, _, statements}, target_module) do
64
    Enum.any?(statements, &check_use_statement(&1, target_module))
44✔
65
  end
66

67
  defp find_use_directive(statement, target_module) do
68
    check_use_statement(statement, target_module)
40✔
69
  end
70

71
  defp check_use_statement({:use, _, [{:__aliases__, _, modules}]}, target_module) do
72
    ast_module = List.last(modules)
12✔
73
    target_atom = case target_module do
12✔
74
      atom when is_atom(atom) ->
75
        atom |> Module.split() |> List.last() |> String.to_atom()
12✔
NEW
76
      _ -> target_module
×
77
    end
78
    ast_module == target_atom
12✔
79
  end
80

81
  defp check_use_statement({:use, _, [module]}, target_module) when is_atom(module) do
NEW
82
    module == target_module
×
83
  end
84

85
  defp check_use_statement(_, _), do: false
139✔
86

87
  defp has_phoenix_controller_pattern?(ast) do
88
    case ast do
21✔
89
      {:defmodule, _, [_name, [do: body]]} ->
90
        has_controller_use_directive?(body) or has_controller_functions?(body)
20✔
91
      _ ->
1✔
92
        false
93
    end
94
  end
95

96
  defp has_controller_use_directive?({:__block__, _, statements}) do
97
    Enum.any?(statements, &is_controller_use_statement?/1)
10✔
98
  end
99

100
  defp has_controller_use_directive?(statement) do
101
    is_controller_use_statement?(statement)
10✔
102
  end
103

104
  defp is_controller_use_statement?({:use, _, [{:__aliases__, _, modules}]}) do
105
    case modules do
2✔
NEW
106
      [_, "Web"] -> true
×
NEW
107
      ["Phoenix", "Controller"] -> true
×
108
      _ -> false
2✔
109
    end
110
  end
111

NEW
112
  defp is_controller_use_statement?({:use, _, [module, :controller]}) when is_atom(module) do
×
113
    true
114
  end
115

116
  defp is_controller_use_statement?(_), do: false
34✔
117

118
  defp has_controller_functions?({:__block__, _, statements}) do
119
    Enum.any?(statements, &is_controller_function?/1)
10✔
120
  end
121

122
  defp has_controller_functions?(statement) do
123
    is_controller_function?(statement)
10✔
124
  end
125

126
  defp is_controller_function?({:def, _, [{name, _, _} | _]}) when name in [:index, :show, :new, :create, :edit, :update, :delete] do
2✔
127
    true
128
  end
129

130
  defp is_controller_function?(_), do: false
32✔
131

132
  defp has_phoenix_live_view_pattern?(ast) do
133
    case ast do
19✔
134
      {:defmodule, _, [_name, [do: body]]} ->
135
        has_live_view_use_directive?(body) or has_live_view_functions?(body)
18✔
136
      _ ->
1✔
137
        false
138
    end
139
  end
140

141
  defp has_live_view_use_directive?({:__block__, _, statements}) do
142
    Enum.any?(statements, &is_live_view_use_statement?/1)
8✔
143
  end
144

145
  defp has_live_view_use_directive?(statement) do
146
    is_live_view_use_statement?(statement)
10✔
147
  end
148

149
  defp is_live_view_use_statement?({:use, _, [{:__aliases__, _, modules}]}) do
150
    case modules do
2✔
NEW
151
      ["Phoenix", "LiveView"] -> true
×
NEW
152
      [_, "Web", "LiveView"] -> true
×
153
      _ -> false
2✔
154
    end
155
  end
156

157
  defp is_live_view_use_statement?(_), do: false
29✔
158

159
  defp has_live_view_functions?({:__block__, _, statements}) do
160
    Enum.any?(statements, &is_live_view_function?/1)
8✔
161
  end
162

163
  defp has_live_view_functions?(statement) do
164
    is_live_view_function?(statement)
10✔
165
  end
166

167
  defp is_live_view_function?({:def, _, [{name, _, _} | _]}) when name in [:mount, :handle_event, :handle_info, :handle_params, :render] do
1✔
168
    true
169
  end
170

171
  defp is_live_view_function?(_), do: false
28✔
172

173
  defp has_ecto_schema_pattern?(ast) do
174
    case ast do
18✔
175
      {:defmodule, _, [_name, [do: body]]} ->
176
        has_ecto_use_directive?(body) or has_schema_definition?(body)
17✔
177
      _ ->
1✔
178
        false
179
    end
180
  end
181

182
  defp has_ecto_use_directive?({:__block__, _, statements}) do
183
    Enum.any?(statements, &is_ecto_use_statement?/1)
7✔
184
  end
185

186
  defp has_ecto_use_directive?(statement) do
187
    is_ecto_use_statement?(statement)
10✔
188
  end
189

190
  defp is_ecto_use_statement?({:use, _, [{:__aliases__, _, modules}]}) do
191
    case modules do
1✔
NEW
192
      ["Ecto", "Schema"] -> true
×
NEW
193
      [_, "Schema"] -> true
×
194
      _ -> false
1✔
195
    end
196
  end
197

198
  defp is_ecto_use_statement?(_), do: false
26✔
199

200
  defp has_schema_definition?({:__block__, _, statements}) do
201
    Enum.any?(statements, &is_schema_statement?/1)
7✔
202
  end
203

204
  defp has_schema_definition?(statement) do
205
    is_schema_statement?(statement)
10✔
206
  end
207

208
  defp is_schema_statement?({:schema, _, _}), do: true
1✔
NEW
209
  defp is_schema_statement?({:embedded_schema, _, _}), do: true
×
210
  defp is_schema_statement?(_), do: false
26✔
211

212
  defp extract_callback_functions({:__block__, _, statements}) do
213
    statements
214
    |> Enum.filter(&is_callback_function?/1)
215
    |> Enum.map(&extract_callback_info/1)
216
    |> Enum.reject(&is_nil/1)
14✔
217
  end
218

219
  defp extract_callback_functions(statement) do
220
    if is_callback_function?(statement) do
10✔
NEW
221
      case extract_callback_info(statement) do
×
NEW
222
        nil -> []
×
NEW
223
        callback -> [callback]
×
224
      end
225
    else
226
      []
227
    end
228
  end
229

230
  defp is_callback_function?({:def, _, [{name, _, args} | _]}) do
231
    arity = if is_list(args), do: length(args), else: 0
41✔
232

233
    case {name, arity} do
41✔
234
      {:init, 1} -> true
4✔
235
      {:handle_call, 3} -> true
3✔
236
      {:handle_cast, 2} -> true
3✔
237
      {:handle_info, 2} -> true
2✔
238
      {:terminate, 2} -> true
1✔
239
      {:code_change, 3} -> true
1✔
NEW
240
      {:handle_continue, 2} -> true
×
241
      {:mount, 3} -> true
1✔
242
      {:handle_event, 3} -> true
1✔
NEW
243
      {:handle_params, 3} -> true
×
244
      {:render, 1} -> true
1✔
NEW
245
      {:action, 2} -> true
×
NEW
246
      {:run, 1} -> true
×
247
      _ -> false
24✔
248
    end
249
  end
250

251
  defp is_callback_function?(_), do: false
20✔
252

253
  defp extract_callback_info({:def, _, [{name, _, args} | _]}) do
254
    arity = if is_list(args), do: length(args), else: 0
17✔
255

256
    %{
17✔
257
      name: name,
258
      arity: arity,
259
      type: determine_callback_type(name, arity)
260
    }
261
  end
262

NEW
263
  defp extract_callback_info(_), do: nil
×
264

265
  defp determine_callback_type(name, arity) do
266
    case {name, arity} do
17✔
267
      {:init, 1} -> :genserver
4✔
268
      {:handle_call, 3} -> :genserver
3✔
269
      {:handle_cast, 2} -> :genserver
3✔
270
      {:handle_info, 2} -> :genserver
2✔
271
      {:terminate, 2} -> :genserver
1✔
272
      {:code_change, 3} -> :genserver
1✔
NEW
273
      {:handle_continue, 2} -> :genserver
×
274
      {:mount, 3} -> :live_view
1✔
275
      {:handle_event, 3} -> :live_view
1✔
NEW
276
      {:handle_params, 3} -> :live_view
×
277
      {:render, 1} -> :live_view
1✔
NEW
278
      {:action, 2} -> :controller
×
NEW
279
      {:run, 1} -> :task
×
NEW
280
      _ -> :unknown
×
281
    end
282
  end
283

284
  defp extract_function_definitions(_body, _visibility) do
24✔
285
    # TODO: Implement function definition extraction
286
    []
287
  end
288
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc