diff --git a/config/test.exs b/config/test.exs index afe2b54..71773df 100644 --- a/config/test.exs +++ b/config/test.exs @@ -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" diff --git a/lib/meandro/rules/unused_configuration_options.ex b/lib/meandro/rules/unused_configuration_options.ex index f225db3..fd0d827 100644 --- a/lib/meandro/rules/unused_configuration_options.ex +++ b/lib/meandro/rules/unused_configuration_options.ex @@ -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 -> @@ -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 -> diff --git a/test/rules/unused_configuration_options/examples/module.exs b/test/rules/unused_configuration_options/examples/module.exs index 7513a96..b18b5a2 100644 --- a/test/rules/unused_configuration_options/examples/module.exs +++ b/test/rules/unused_configuration_options/examples/module.exs @@ -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 diff --git a/test/rules/unused_configuration_options/unused_configuration_options_test.exs b/test/rules/unused_configuration_options/unused_configuration_options_test.exs index c0f76d0..64d9153 100644 --- a/test/rules/unused_configuration_options/unused_configuration_options_test.exs +++ b/test/rules/unused_configuration_options/unused_configuration_options_test.exs @@ -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,