Skip to content

Commit

Permalink
move rule to elvis_text_style
Browse files Browse the repository at this point in the history
  • Loading branch information
Milán Bór authored and Milán Bór committed Sep 16, 2024
1 parent 198544b commit 164cc66
Showing 3 changed files with 58 additions and 49 deletions.
46 changes: 1 addition & 45 deletions src/elvis_style.erl
Original file line number Diff line number Diff line change
@@ -12,8 +12,7 @@
atom_naming_convention/3, no_throw/3, no_dollar_space/3, no_author/3, no_import/3,
no_catch_expressions/3, no_single_clause_case/3, numeric_format/3, behaviour_spelling/3,
always_shortcircuit/3, consistent_generic_type/3, export_used_types/3,
no_match_in_condition/3, param_pattern_matching/3, private_data_types/3, option/3,
prefer_unquoted_atoms/3]).
no_match_in_condition/3, param_pattern_matching/3, private_data_types/3, option/3]).

-export_type([empty_rule_config/0]).
-export_type([ignorable/0]).
@@ -108,9 +107,6 @@
-define(ATOM_NAMING_CONVENTION_MSG,
"Atom ~p on line ~p does not respect the format "
"defined by the regular expression '~p'.").
-define(ATOM_PREFERRED_QUOTES_MSG,
"Atom ~p on line ~p is quoted "
"but quotes are not needed.").
-define(NO_THROW_MSG, "Usage of throw/1 on line ~p is not recommended").
-define(NO_DOLLAR_SPACE_MSG,
"'$ ' was found on line ~p. It's use is discouraged. "
@@ -1019,16 +1015,6 @@ atom_naming_convention(Config, Target, RuleConfig) ->
AtomNodes = elvis_code:find(fun is_atom_node/1, Root, #{traverse => all, mode => node}),
check_atom_names(Regex, RegexEnclosed, AtomNodes, []).

-spec prefer_unquoted_atoms(elvis_config:config(),
elvis_file:file(),
empty_rule_config()) ->
[elvis_result:item()].
prefer_unquoted_atoms(_Config, Target, _RuleConfig) ->
{Content, #{encoding := _Encoding}} = elvis_file:src(Target),
Tree = ktn_code:parse_tree(Content),
AtomNodes = elvis_code:find(fun is_atom_node/1, Tree, #{traverse => all, mode => node}),
check_atom_quotes(AtomNodes, []).

-spec no_throw(elvis_config:config(), elvis_file:file(), empty_rule_config()) ->
[elvis_result:item()].
no_throw(Config, Target, RuleConfig) ->
@@ -1470,36 +1456,6 @@ check_atom_names(Regex, RegexEnclosed, [AtomNode | RemainingAtomNodes], AccIn) -
end,
check_atom_names(Regex, RegexEnclosed, RemainingAtomNodes, AccOut).

%% @private
check_atom_quotes([] = _AtomNodes, Acc) ->
Acc;
check_atom_quotes([AtomNode | RemainingAtomNodes], AccIn) ->
AtomName = ktn_code:attr(text, AtomNode),
ValueAtomName = ktn_code:attr(value, AtomNode),

IsException = is_exception_prefer_quoted(ValueAtomName),

AccOut =
case unicode:characters_to_list(AtomName, unicode) of
[$' | _] when not IsException ->
Msg = ?ATOM_PREFERRED_QUOTES_MSG,
{Line, _} = ktn_code:attr(location, AtomNode),
Info = [AtomName, Line],
Result = elvis_result:new(item, Msg, Info, Line),
AccIn ++ [Result];
_ ->
AccIn
end,
check_atom_quotes(RemainingAtomNodes, AccOut).

%% @private
is_exception_prefer_quoted(Elem) ->
KeyWords =
['after', 'and', 'andalso', 'band', 'begin', 'bnot', 'bor', 'bsl', 'bsr', 'bxor', 'case',
'catch', 'cond', 'div', 'end', 'fun', 'if', 'let', 'not', 'of', 'or', 'orelse', 'receive',
'rem', 'try', 'when', 'xor'],
lists:member(Elem, KeyWords).

%% @private
string_strip_enclosed([$' | Rest]) ->
[$' | Reversed] = lists:reverse(Rest),
55 changes: 53 additions & 2 deletions src/elvis_text_style.erl
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
-module(elvis_text_style).

-export([default/1, line_length/3, no_tabs/3, no_trailing_whitespace/3]).
-export([default/1, line_length/3, no_tabs/3, no_trailing_whitespace/3,
prefer_unquoted_atoms/3]).

-export_type([line_length_config/0, no_trailing_whitespace_config/0]).

-define(LINE_LENGTH_MSG, "Line ~p is too long. It has ~p characters.").
-define(NO_TABS_MSG, "Line ~p has a tab at column ~p.").
-define(NO_TRAILING_WHITESPACE_MSG, "Line ~b has ~b trailing whitespace characters.").
-define(ATOM_PREFERRED_QUOTES_MSG,
"Atom ~p on line ~p is quoted "
"but quotes are not needed.").

% These are part of a non-declared "behaviour"
% The reason why we don't try to handle them with different arity is
% that arguments are ignored in different positions (1 and 3) so that'd
% probably be messier than to ignore the warning
-hank([{unnecessary_function_arguments,
[{no_trailing_whitespace, 3}, {no_tabs, 3}, {line_length, 3}]}]).
[{no_trailing_whitespace, 3},
{no_tabs, 3},
{line_length, 3},
{prefer_unquoted_atoms, 3}]}]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Default values
@@ -71,6 +78,38 @@ no_trailing_whitespace(_Config, Target, RuleConfig) ->
end,
RuleConfig).

-spec prefer_unquoted_atoms(elvis_config:config(),
elvis_file:file(),
elvis_style:empty_rule_config()) ->
[elvis_result:item()].
prefer_unquoted_atoms(_Config, Target, _RuleConfig) ->
{Content, #{encoding := _Encoding}} = elvis_file:src(Target),
Tree = ktn_code:parse_tree(Content),
AtomNodes = elvis_code:find(fun is_atom_node/1, Tree, #{traverse => all, mode => node}),
check_atom_quotes(AtomNodes, []).

%% @private
check_atom_quotes([] = _AtomNodes, Acc) ->
Acc;
check_atom_quotes([AtomNode | RemainingAtomNodes], AccIn) ->
AtomName = ktn_code:attr(text, AtomNode),
ValueAtomName = ktn_code:attr(value, AtomNode),

IsException = is_exception_prefer_quoted(ValueAtomName),

AccOut =
case unicode:characters_to_list(AtomName, unicode) of
[$' | _] when not IsException ->
Msg = ?ATOM_PREFERRED_QUOTES_MSG,
{Line, _} = ktn_code:attr(location, AtomNode),
Info = [AtomName, Line],
Result = elvis_result:new(item, Msg, Info, Line),
AccIn ++ [Result];
_ ->
AccIn
end,
check_atom_quotes(RemainingAtomNodes, AccOut).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -164,6 +203,18 @@ check_no_trailing_whitespace(Line, Num, IgnoreEmptyLines) ->
{ok, Result}
end.

%% @private
is_atom_node(MaybeAtom) ->
ktn_code:type(MaybeAtom) =:= atom.

%% @private
is_exception_prefer_quoted(Elem) ->
KeyWords =
['after', 'and', 'andalso', 'band', 'begin', 'bnot', 'bor', 'bsl', 'bsr', 'bxor', 'case',
'catch', 'cond', 'div', 'end', 'fun', 'if', 'let', 'not', 'of', 'or', 'orelse', 'receive',
'rem', 'try', 'when', 'xor'],
lists:member(Elem, KeyWords).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internal Function Definitions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 changes: 4 additions & 2 deletions test/style_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1300,10 +1300,12 @@ verify_no_successive_maps(_Config) ->
-spec verify_unquoted_atoms(config()) -> any().
verify_unquoted_atoms(Config) ->
PassPath = "pass_unquoted_atoms." ++ "erl",
[] = elvis_core_apply_rule(Config, elvis_style, prefer_unquoted_atoms, #{}, PassPath),
[] =
elvis_core_apply_rule(Config, elvis_text_style, prefer_unquoted_atoms, #{}, PassPath),

FailPath = "fail_quoted_atoms." ++ "erl",
[_, _] = elvis_core_apply_rule(Config, elvis_style, prefer_unquoted_atoms, #{}, FailPath).
[_, _] =
elvis_core_apply_rule(Config, elvis_text_style, prefer_unquoted_atoms, #{}, FailPath).

-spec verify_atom_naming_convention(config()) -> any().
verify_atom_naming_convention(Config) ->

0 comments on commit 164cc66

Please sign in to comment.