Skip to content

Commit

Permalink
Merge pull request #50 from inaka/jfacorro.22.used.ignored.variables
Browse files Browse the repository at this point in the history
[Closes #17] Implemented 'used ignored variable' rule.
  • Loading branch information
elbrujohalcon committed Jul 21, 2014
2 parents c3aa4db + d7a9c0c commit 83a1720
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 18 deletions.
10 changes: 8 additions & 2 deletions src/elvis_code.erl
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,20 @@ find(Pred, Node, Results) ->

-spec type(tree_node()) -> atom().
type(#{type := Type}) ->
Type.
Type;
type(undefined) ->
undefined.

-spec attr(term(), tree_node()) -> term() | undefined.
attr(Key, #{attrs := Attrs}) ->
case maps:is_key(Key, Attrs) of
true -> maps:get(Key, Attrs);
false -> undefined
end.
end;
attr(_Key, Node) when is_map(Node) ->
undefined;
attr(_Key, undefined) ->
undefined.

-spec content(tree_node()) -> [tree_node()].
content(#{content := Content}) ->
Expand Down
63 changes: 51 additions & 12 deletions src/elvis_style.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
nesting_level/3,
god_modules/3,
no_if_expression/3,
invalid_dynamic_call/3
invalid_dynamic_call/3,
used_ignored_variable/3
]).

-define(LINE_LENGTH_MSG, "Line ~p is too long: ~p.").
Expand All @@ -33,6 +34,9 @@
-define (INVALID_DYNAMIC_CALL_MSG,
"Remove the dynamic function call on line ~p. "
"Only modules that define callbacks should make dynamic calls.").
-define(USED_IGNORED_VAR_MSG,
"Ignored variable is being used on line ~p and "
"column ~p.").

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Rules
Expand Down Expand Up @@ -104,7 +108,8 @@ no_if_expression(Config, Target, []) ->
[];
IfExprs ->
ResultFun =
fun (Node) ->
fun
(Node) ->
{LineNum, _} = elvis_code:attr(location, Node),
Msg = ?NO_IF_EXPRESSION_MSG,
Info = [LineNum],
Expand All @@ -126,6 +131,26 @@ invalid_dynamic_call(Config, Target, []) ->
[]
end.

-spec used_ignored_variable(elvis_config:config(), elvis_utils:file(), []) ->
[elvis_result:item()].
used_ignored_variable(Config, Target, []) ->
{ok, Src} = elvis_utils:src(Config, Target),
Root = elvis_code:parse_tree(Src),
case elvis_code:find(fun is_used_ignored_var/1, Root) of
[] ->
[];
UsedIgnoredVars ->
ResultFun =
fun
(Node) ->
{Line, Col} = elvis_code:attr(location, Node),
Msg = ?USED_IGNORED_VAR_MSG,
Info = [Line, Col],
elvis_result:new(item, Msg, Info, Line)
end,
lists:map(ResultFun, UsedIgnoredVars)
end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Expand Down Expand Up @@ -267,14 +292,28 @@ check_invalid_dynamic_calls(Root) ->

-spec is_dynamic_call(elvis_code:tree_node()) ->
boolean().
is_dynamic_call(Node = #{type := call}) ->
FunctionSpec = elvis_code:attr(function, Node),
case elvis_code:type(FunctionSpec) of
remote ->
ModuleName = elvis_code:attr(module, FunctionSpec),
var == elvis_code:type(ModuleName);
_Other ->
is_dynamic_call(Node) ->
case elvis_code:type(Node) of
call ->
FunctionSpec = elvis_code:attr(function, Node),
case elvis_code:type(FunctionSpec) of
remote ->
ModuleName = elvis_code:attr(module, FunctionSpec),
var == elvis_code:type(ModuleName);
_Other ->
false
end;
_ ->
false
end;
is_dynamic_call(_Node) ->
false.
end.

-spec is_used_ignored_var(elvis_code:tree_node()) ->
boolean().
is_used_ignored_var(Node) ->
case elvis_code:type(Node) of
var ->
Name = elvis_code:attr(name, Node),
[FirstChar | _] = atom_to_list(Name),
FirstChar == $_;
_OtherType -> false
end.
6 changes: 3 additions & 3 deletions test/examples/fail_no_if_expression.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
uses_if(Arg) ->
if
Arg -> ok;
false -> not_ok
Arg == false -> not_ok
end,
case 1 of
1 -> ok;
Expand All @@ -19,7 +19,7 @@ uses_if(Arg) ->
uses_if_twice(Arg) ->
if
Arg -> ok;
false -> not_ok
Arg == false -> not_ok
end,
case 1 of
1 -> ok;
Expand All @@ -28,5 +28,5 @@ uses_if_twice(Arg) ->
end,
if
Arg -> ok;
false -> not_ok
Arg == false -> not_ok
end.
17 changes: 17 additions & 0 deletions test/examples/fail_used_ignored_variable.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-module(fail_used_ignored_variable).

-export([
use_ignored_var/2,
use_ignored_var_in_fun/2
]).

use_ignored_var(_One, Two) ->
Three = _One + Two,
case Three of
_Four ->
_Four
end.

use_ignored_var_in_fun(_One, Two) ->
Fun = fun (_Three) -> _One + _Three end,
Fun(Two).
16 changes: 15 additions & 1 deletion test/rules_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
verify_nesting_level/1,
verify_god_modules/1,
verify_no_if_expression/1,
verify_invalid_dynamic_call/1
verify_invalid_dynamic_call/1,
verify_used_ignored_variable/1
]).

-define(EXCLUDED_FUNS,
Expand Down Expand Up @@ -164,3 +165,16 @@ verify_invalid_dynamic_call(_Config) ->
PathPass = "pass_invalid_dynamic_call.erl",
{ok, FilePass} = elvis_test_utils:find_file(SrcDirs, PathPass),
[] = elvis_style:invalid_dynamic_call(ElvisConfig, FilePass, []).

-spec verify_used_ignored_variable(config()) -> any().
verify_used_ignored_variable(_Config) ->
ElvisConfig = elvis_config:default(),
#{src_dirs := SrcDirs} = ElvisConfig,
Path = "fail_used_ignored_variable.erl",
{ok, File} = elvis_test_utils:find_file(SrcDirs, Path),
[
#{line_num := 9},
#{line_num := 12},
#{line_num := 16},
#{line_num := 16}
] = elvis_style:used_ignored_variable(ElvisConfig, File, []).

0 comments on commit 83a1720

Please sign in to comment.