Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ config :meandro,
other_unused_option: "value3",
input_key_option: :ok,
nice_option: :nice

config :meandro, MeandroTest.UnusedConfigurationOptions.Module,
used_keyed_option: "value1",
unused_keyed_option: "value2"
23 changes: 22 additions & 1 deletion lib/meandro/rules/unused_configuration_options.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ defmodule Meandro.Rule.UnusedConfigurationOptions do
end

defp analyze_asts(asts, app_name, mix_env) do
options_set = app_name |> Application.get_all_env() |> Keyword.keys() |> Enum.sort()
options_set =
app_name
|> Application.get_all_env()
|> Keyword.keys()
|> maybe_aggregate_options(app_name)
|> Enum.sort()

usage_map =
Enum.reduce(asts, %{get_all_env: false, used_options: MapSet.new()}, fn ast, acc ->
Expand All @@ -64,6 +69,22 @@ defmodule Meandro.Rule.UnusedConfigurationOptions do
end
end

# This covers cases where using the macro config/3
# config(root_key, key, opts) so, `options` here is a root_key
defp maybe_aggregate_options(options, app_name) do
Enum.reduce(options, [], fn option, acc ->
key = Application.get_env(app_name, option)

case Keyword.keyword?(key) do
true ->
[Keyword.keys(key) | acc] |> List.flatten()

false ->
[option | acc]
end
end)
end

defp analyze_ast(ast, acc0, app_name) do
{_, usage_map} =
Macro.prewalk(ast, acc0, fn node, acc ->
Expand Down
8 changes: 5 additions & 3 deletions test/rules/unused_configuration_options/examples/module.exs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
defmodule MeandroTest.UnusedConfigurationOptions.Module do
# a function that returns a config option
def some_fun() do
Application.get_env(:meandro, used_option)
Application.get_env(:meandro, :used_option)
other_fun(Application.fetch_env!(:meandro, :input_key_option))
end

def other_fun(key) do
[
key,
Application.fetch_env(:meandro, nice_option),
Application.fetch_env(:meandro, :nice_option),
# calls get_all_env from other app -> ignore it!
Application.get_all_env(:other_app),
# calls to an unused option but from other app -> ignore it!
Application.fetch_env(:other_app, unused_option)
Application.fetch_env(:other_app, :unused_option),
# uses a keyed option also
Application.get_env(:meandro, :used_keyed_option)
]
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,30 @@ defmodule MeandroTest.Rule.UnusedConfigurationOptions do
file = @test_directory_path <> "module.exs"
files_and_asts = TestHelpers.parse_files([file])

expected_text1 =
"Configuration option :other_unused_option (MIX_ENV=test) is not used anywhere in the code"

expected_text2 =
"Configuration option :unused_option (MIX_ENV=test) is not used anywhere in the code"

assert [
%Rule{
file: nil,
line: 0,
pattern: :other_unused_option,
rule: UnusedConfigurationOptions,
text: ^expected_text1
text:
"Configuration option :other_unused_option (MIX_ENV=test) is not used anywhere in the code"
},
%Rule{
file: nil,
line: 0,
pattern: :unused_keyed_option,
rule: UnusedConfigurationOptions,
text:
"Configuration option :unused_keyed_option (MIX_ENV=test) is not used anywhere in the code"
},
%Rule{
file: nil,
line: 0,
pattern: :unused_option,
rule: UnusedConfigurationOptions,
text: ^expected_text2
text:
"Configuration option :unused_option (MIX_ENV=test) is not used anywhere in the code"
}
] =
Rule.analyze(UnusedConfigurationOptions, files_and_asts,
Expand Down