Skip to content

Commit

Permalink
[#1098] Detect unused record fields
Browse files Browse the repository at this point in the history
  • Loading branch information
robertoaloi committed Sep 30, 2021
1 parent 3902e3e commit 532a154
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-module(diagnostics_unused_record_fields).

-export([main/1]).

-record(used_field, {field_a, field_b = 42}).
-record(unused_field, {field_c, field_d}).

main(#used_field{field_a = A, field_b = B}) ->
{A, B};
main(R) ->
R#unused_field.field_c.
1 change: 1 addition & 0 deletions apps/els_lsp/src/els_diagnostics.erl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ available_diagnostics() ->
, <<"elvis">>
, <<"unused_includes">>
, <<"unused_macros">>
, <<"unused_record_fields">>
].

-spec default_diagnostics() -> [diagnostic_id()].
Expand Down
73 changes: 73 additions & 0 deletions apps/els_lsp/src/els_unused_record_fields_diagnostics.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
%%==============================================================================
%% Unused Includes diagnostics
%%==============================================================================
-module(els_unused_record_fields_diagnostics).

%%==============================================================================
%% Behaviours
%%==============================================================================
-behaviour(els_diagnostics).

%%==============================================================================
%% Exports
%%==============================================================================
-export([ is_default/0
, run/1
, source/0
]).

%%==============================================================================
%% Includes
%%==============================================================================
-include("els_lsp.hrl").

%%==============================================================================
%% Callback Functions
%%==============================================================================

-spec is_default() -> boolean().
is_default() ->
true.

-spec run(uri()) -> [els_diagnostics:diagnostic()].
run(Uri) ->
case filename:extension(Uri) of
<<".erl">> ->
case els_utils:lookup_document(Uri) of
{error, _Error} ->
[];
{ok, Document} ->
UnusedRecordFields = find_unused_record_fields(Document),
[make_diagnostic(POI) || POI <- UnusedRecordFields ]
end;
_ ->
[]
end.

-spec source() -> binary().
source() ->
<<"UnusedRecordFields">>.

%%==============================================================================
%% Internal Functions
%%==============================================================================
-spec find_unused_record_fields(els_dt_document:item()) -> [poi()].
find_unused_record_fields(Document) ->
Definitions = els_dt_document:pois(Document, [record_def_field]),
Usages = els_dt_document:pois(Document, [record_field]),
UsagesIds = [Id || #{id := Id} <- Usages],
[POI || #{id := Id} = POI <- Definitions, not lists:member(Id, UsagesIds)].

-spec make_diagnostic(poi()) -> els_diagnostics:diagnostic().
make_diagnostic(#{id := POIId, range := POIRange}) ->
Range = els_protocol:range(POIRange),
MacroName = case POIId of
{Id, Arity} ->
els_utils:to_binary(
lists:flatten(io_lib:format("~s/~b", [Id, Arity])));
Id -> atom_to_binary(Id, utf8)
end,
Message = <<"Unused record field: ", MacroName/binary>>,
Severity = ?DIAGNOSTIC_WARNING,
Source = source(),
els_diagnostics:make_diagnostic(Range, Message, Severity, Source).
20 changes: 20 additions & 0 deletions apps/els_lsp/test/els_diagnostics_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
, unused_includes_compiler_attribute/1
, exclude_unused_includes/1
, unused_macros/1
, unused_record_fields/1
]).

%%==============================================================================
Expand Down Expand Up @@ -773,6 +774,25 @@ unused_macros(Config) ->
?assertEqual(Expected, lists:sort(F, Diagnostics)),
ok.

-spec unused_record_fields(config()) -> ok.
unused_record_fields(Config) ->
Uri = ?config(diagnostics_unused_record_fields_uri, Config),
els_mock_diagnostics:subscribe(),
ok = els_client:did_save(Uri),
Diagnostics = els_mock_diagnostics:wait_until_complete(),
Expected = [ #{ message => <<"Unused record field: unused_field/field_d">>
, range =>
#{ 'end' => #{character => 39, line => 5}
, start => #{character => 32, line => 5}
}
, severity => 2
, source => <<"UnusedRecordFields">>
}
],
F = fun(#{message := M1}, #{message := M2}) -> M1 =< M2 end,
?assertEqual(Expected, lists:sort(F, Diagnostics)),
ok.

%%==============================================================================
%% Internal Functions
%%==============================================================================
Expand Down
2 changes: 2 additions & 0 deletions apps/els_lsp/test/els_initialization_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ initialize_diagnostics_custom(Config) ->
, <<"dialyzer">>
, <<"unused_includes">>
, <<"unused_macros">>
, <<"unused_record_fields">>
],
Result = els_diagnostics:enabled_diagnostics(),
?assertEqual(Expected, Result),
Expand All @@ -148,6 +149,7 @@ initialize_diagnostics_invalid(Config) ->
, <<"elvis">>
, <<"unused_includes">>
, <<"unused_macros">>
, <<"unused_record_fields">>
],
?assertEqual(Expected, Result),
ok.
Expand Down
1 change: 1 addition & 0 deletions apps/els_lsp/test/els_test_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ sources() ->
, diagnostics_unused_includes
, diagnostics_unused_includes_compiler_attribute
, diagnostics_unused_macros
, diagnostics_unused_record_fields
, elvis_diagnostics
, execute_command_suggest_spec
, format_input
Expand Down

0 comments on commit 532a154

Please sign in to comment.