From 99986675a6a185eba03224212c0c9adc0c1fc428 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 4 Aug 2023 18:54:34 +0100 Subject: [PATCH 01/18] Remove dead code --- test/examples/fail_invalid_dynamic_call.erl | 2 -- test/examples/fail_line_length.erl | 2 -- test/examples/fail_macro_module_names.erl | 3 --- test/examples/fail_no_debug_call.erl | 3 --- test/examples/fail_no_nested_try_catch.erl | 3 --- test/examples/pass_invalid_dynamic_call.erl | 3 --- test/examples/pass_invalid_dynamic_call_elvis_attr.erl | 9 --------- test/examples/pass_line_length_elvis_attr.erl | 2 -- test/examples/pass_macro_module_names_elvis_attr.erl | 4 ---- test/examples/pass_no_debug_call_elvis_attr.erl | 3 --- test/examples/pass_no_nested_try_catch_elvis_attr.erl | 3 --- 11 files changed, 37 deletions(-) diff --git a/test/examples/fail_invalid_dynamic_call.erl b/test/examples/fail_invalid_dynamic_call.erl index 45cf27d6..409323b1 100644 --- a/test/examples/fail_invalid_dynamic_call.erl +++ b/test/examples/fail_invalid_dynamic_call.erl @@ -1,7 +1,5 @@ -module(fail_invalid_dynamic_call). --ignore_xref({normal, call, 0}). --ignore_xref({another_normal, call, 0}). -elvis([{elvis_style, no_catch_expressions, disable}]). -dialyzer(no_match). diff --git a/test/examples/fail_line_length.erl b/test/examples/fail_line_length.erl index 07fa4d72..d790fe53 100644 --- a/test/examples/fail_line_length.erl +++ b/test/examples/fail_line_length.erl @@ -1,7 +1,5 @@ -module(fail_line_length). --ignore_xref({model, random_ant_state, 1}). - -export([ function_1/0, function_2/0, diff --git a/test/examples/fail_macro_module_names.erl b/test/examples/fail_macro_module_names.erl index 6ec09e8b..3f569d7b 100644 --- a/test/examples/fail_macro_module_names.erl +++ b/test/examples/fail_macro_module_names.erl @@ -1,8 +1,5 @@ -module(fail_macro_module_names). --ignore_xref({module, function_name, 1}). --ignore_xref({lists, fail_macro_module_names, 0}). - -dialyzer({nowarn_function, [function_name/0, build_binary/0]}). -export([ diff --git a/test/examples/fail_no_debug_call.erl b/test/examples/fail_no_debug_call.erl index 886ee33d..fd888d37 100644 --- a/test/examples/fail_no_debug_call.erl +++ b/test/examples/fail_no_debug_call.erl @@ -1,9 +1,6 @@ -module(fail_no_debug_call). -export([fail/0]). --ignore_xref({cthr, pal, 1}). --ignore_xref({cthr, pal, 2}). - -spec fail() -> any(). fail() -> erlang:display("debug print"), diff --git a/test/examples/fail_no_nested_try_catch.erl b/test/examples/fail_no_nested_try_catch.erl index e5a5fc24..250226ff 100644 --- a/test/examples/fail_no_nested_try_catch.erl +++ b/test/examples/fail_no_nested_try_catch.erl @@ -1,8 +1,5 @@ -module(fail_no_nested_try_catch). --ignore_xref({perhaps, throw, 1}). --ignore_xref({a_function, that_deals, 2}). - -dialyzer({nowarn_function, bad2/0}). -export([ diff --git a/test/examples/pass_invalid_dynamic_call.erl b/test/examples/pass_invalid_dynamic_call.erl index 670a1b79..80a50e7a 100644 --- a/test/examples/pass_invalid_dynamic_call.erl +++ b/test/examples/pass_invalid_dynamic_call.erl @@ -2,9 +2,6 @@ -dialyzer(no_match). --ignore_xref({normal, call, 0}). --ignore_xref({another_normal, call, 0}). - -export([ dynamic_module_name_call/0, dynamic_function_name_call/0, diff --git a/test/examples/pass_invalid_dynamic_call_elvis_attr.erl b/test/examples/pass_invalid_dynamic_call_elvis_attr.erl index 25601e06..ce85bb32 100644 --- a/test/examples/pass_invalid_dynamic_call_elvis_attr.erl +++ b/test/examples/pass_invalid_dynamic_call_elvis_attr.erl @@ -6,15 +6,6 @@ -elvis([{elvis_style, no_catch_expressions, disable}]). -dialyzer(no_match). --ignore_xref({normal, call, 0}). --ignore_xref({a_module, call, 0}). --ignore_xref({a_module, a_function, 0}). --ignore_xref({another_normal, call, 0}). --ignore_xref({another_normal, call_to__another_function, 0}). --ignore_xref({yam, call_to_function, 0}). --ignore_xref({yamm, call_to_function, 0}). --ignore_xref({yammm, call_to_function, 0}). - -export([ dynamic_module_name_call/0, dynamic_function_name_call/0, diff --git a/test/examples/pass_line_length_elvis_attr.erl b/test/examples/pass_line_length_elvis_attr.erl index 2509b0f4..4caba26c 100644 --- a/test/examples/pass_line_length_elvis_attr.erl +++ b/test/examples/pass_line_length_elvis_attr.erl @@ -5,8 +5,6 @@ -elvis([{elvis_style, function_naming_convention, #{regex => "^function_[0-9]+$"}}]). -elvis([{elvis_style, macro_module_names, disable}]). --ignore_xref([{model, random_ant_state, 1}]). - -export([ function_1/0, function_2/0, diff --git a/test/examples/pass_macro_module_names_elvis_attr.erl b/test/examples/pass_macro_module_names_elvis_attr.erl index edc48a0d..b48f75c3 100644 --- a/test/examples/pass_macro_module_names_elvis_attr.erl +++ b/test/examples/pass_macro_module_names_elvis_attr.erl @@ -19,10 +19,6 @@ build_binary/0 ]). --ignore_xref({pass_macro_module_names_elvis_attr, function_name, 0}). --ignore_xref({module, function_name, 1}). --ignore_xref({lists, pass_macro_module_names_elvis_attr, 0}). - -define(FUN_NAME, function_name). -define(BINARY, "bla"). -define(BINARY_SIZE, 3). diff --git a/test/examples/pass_no_debug_call_elvis_attr.erl b/test/examples/pass_no_debug_call_elvis_attr.erl index f7c1a346..0278f6a7 100644 --- a/test/examples/pass_no_debug_call_elvis_attr.erl +++ b/test/examples/pass_no_debug_call_elvis_attr.erl @@ -3,9 +3,6 @@ -elvis([{elvis_style, no_debug_call, #{debug_functions => [{ct, log}]}}]). --ignore_xref({cthr, pal, 1}). --ignore_xref({cthr, pal, 2}). - -spec fail() -> any(). fail() -> erlang:display("debug print"), diff --git a/test/examples/pass_no_nested_try_catch_elvis_attr.erl b/test/examples/pass_no_nested_try_catch_elvis_attr.erl index 36025945..441b9beb 100644 --- a/test/examples/pass_no_nested_try_catch_elvis_attr.erl +++ b/test/examples/pass_no_nested_try_catch_elvis_attr.erl @@ -2,9 +2,6 @@ -dialyzer({nowarn_function, bad2/0}). --ignore_xref([{it_may, throw, 1}]). --ignore_xref([{a_function, that_deals, 2}]). - -export([ bad1/0, bad2/0, From fa7f4094b619716cab1f2238e38b07c0b40fa71c Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sat, 5 Aug 2023 18:10:10 +0100 Subject: [PATCH 02/18] Update tests due to recent changes around them --- test/style_SUITE.erl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/test/style_SUITE.erl b/test/style_SUITE.erl index d93ebd6f..9b259528 100644 --- a/test/style_SUITE.erl +++ b/test/style_SUITE.erl @@ -240,7 +240,7 @@ verify_line_length_rule(Config) -> elvis_core_apply_rule(Config, elvis_text_style, line_length, #{limit => 100}, Path), 8 = length(Result), #{info := Info, message := Msg} = lists:nth(7, Result), - <<"Line 34 is too long. It has ", _/binary>> = list_to_binary(io_lib:format(Msg, Info)), + <<"Line 32 is too long. It has ", _/binary>> = list_to_binary(io_lib:format(Msg, Info)), WholeLineResult = elvis_core_apply_rule(Config, @@ -362,15 +362,15 @@ verify_macro_module_names(Config) -> Ext = proplists:get_value(test_file_ext, Config, "erl"), Path = "fail_macro_module_names." ++ Ext, - [#{line_num := 23}, + [#{line_num := 20}, + #{line_num := 20}, + #{line_num := 21}, + #{line_num := 22}, + #{line_num := 23}, #{line_num := 23}, - #{line_num := 24}, - #{line_num := 25}, - #{line_num := 26}, - #{line_num := 26}, - #{line_num := 30}, - #{line_num := 31}, - #{line_num := 32}] = + #{line_num := 27}, + #{line_num := 28}, + #{line_num := 29}] = elvis_core_apply_rule(Config, elvis_style, macro_module_names, #{}, Path). -spec verify_no_macros(config()) -> any(). @@ -604,13 +604,13 @@ verify_invalid_dynamic_call(Config) -> #{line_num := _}] = elvis_core_apply_rule(Config, elvis_style, invalid_dynamic_call, #{}, PathFail); erl_files -> - [#{line_num := 21}, - #{line_num := 33}, - #{line_num := 34}, - #{line_num := 42}, - #{line_num := 50}, - #{line_num := 61}, - #{line_num := 68}] = + [#{line_num := 19}, + #{line_num := 31}, + #{line_num := 32}, + #{line_num := 40}, + #{line_num := 48}, + #{line_num := 59}, + #{line_num := 66}] = elvis_core_apply_rule(Config, elvis_style, invalid_dynamic_call, #{}, PathFail) end, @@ -1221,10 +1221,10 @@ verify_no_nested_try_catch(Config) -> Path = atom_to_list(Module) ++ "." ++ Ext, _ = case Group of beam_files -> - [#{line_num := 13}, #{line_num := 22}, #{line_num := 25}] = + [#{line_num := 9}, #{line_num := 18}, #{line_num := 21}] = elvis_core_apply_rule(Config, elvis_style, no_nested_try_catch, #{}, Path); erl_files -> - [#{line_num := 18}, #{line_num := 33}, #{line_num := 40}] = + [#{line_num := 15}, #{line_num := 30}, #{line_num := 37}] = elvis_core_apply_rule(Config, elvis_style, no_nested_try_catch, #{}, Path) end, From b1a33023e3dd55cfe4d58763a648867e2140d681 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 18:22:40 +0100 Subject: [PATCH 03/18] Move http:// to https:// --- LICENSE | 4 ++-- doc_rules/elvis_style/dont_repeat_yourself.md | 2 +- doc_rules/elvis_style/no_common_caveats_call.md | 2 +- doc_rules/elvis_style/no_successive_maps.md | 2 +- src/elvis_text_style.erl | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index 48cc4e9d..3fa9a693 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -194,7 +194,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/doc_rules/elvis_style/dont_repeat_yourself.md b/doc_rules/elvis_style/dont_repeat_yourself.md index 4b935d21..733cb014 100644 --- a/doc_rules/elvis_style/dont_repeat_yourself.md +++ b/doc_rules/elvis_style/dont_repeat_yourself.md @@ -1,6 +1,6 @@ # Don't Repeat Yourself -The *Don't Repeat Yourself* ([DRY](http://en.wikipedia.org/wiki/Don't_repeat_yourself)) rule checks +The *Don't Repeat Yourself* ([DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself)) rule checks if there is repeated code within a module. A piece of code is considered repeated or duplicated when its structure can be found in at least another piece of code in the same module. diff --git a/doc_rules/elvis_style/no_common_caveats_call.md b/doc_rules/elvis_style/no_common_caveats_call.md index 2e17db46..93e0f3cf 100644 --- a/doc_rules/elvis_style/no_common_caveats_call.md +++ b/doc_rules/elvis_style/no_common_caveats_call.md @@ -2,7 +2,7 @@ (since [0.4.0](https://github.com/inaka/elvis_core/releases/tag/0.4.0)) -The [Erlang Efficiency Guide](http://erlang.org/doc/efficiency_guide/commoncaveats.html) has a list +The [Erlang Efficiency Guide](https://erlang.org/doc/efficiency_guide/commoncaveats.html) has a list of "Common Caveats" suggesting more efficient alternatives to several common functions. This rule provides warnings if you call "inefficient" functions with entirely equivalent (efficient) alternatives. diff --git a/doc_rules/elvis_style/no_successive_maps.md b/doc_rules/elvis_style/no_successive_maps.md index 8a2115f5..199a16c2 100644 --- a/doc_rules/elvis_style/no_successive_maps.md +++ b/doc_rules/elvis_style/no_successive_maps.md @@ -3,7 +3,7 @@ (since [2.0.0](https://github.com/inaka/elvis_core/releases/tag/2.0.0)) The idea behind this rule comes from -[this email](http://erlang.org/pipermail/erlang-questions/2017-April/092112.html) by @kvakvs. +[this email](https://erlang.org/pipermail/erlang-questions/2017-April/092112.html) by @kvakvs. Basically, the warning is emitted if a developer _forgets a comma_ and writes something like the following: ```erlang diff --git a/src/elvis_text_style.erl b/src/elvis_text_style.erl index 5c229c56..9583266a 100644 --- a/src/elvis_text_style.erl +++ b/src/elvis_text_style.erl @@ -142,7 +142,7 @@ check_no_tabs(Line, Num) -> check_no_trailing_whitespace(Line, Num, IgnoreEmptyLines) -> Regex = case IgnoreEmptyLines of - %% Lookbehind assertion: http://erlang.org/doc/man/re.html#sect17 + %% Lookbehind assertion: https://erlang.org/doc/man/re.html#sect17 true -> "(?<=\\S)\\s+$"; false -> From 8cbc569a7f7523c138836d70a7dc16a223ccd439 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 18:23:40 +0100 Subject: [PATCH 04/18] Validate rebar3_ex_doc results --- README.md | 17 +++++--------- src/elvis_core.erl | 7 ++++++ src/elvis_file.erl | 6 +++++ src/elvis_project.erl | 8 +++++++ src/elvis_style.erl | 49 ++++++++++++++++++++++++++++++++++++++++ src/elvis_text_style.erl | 5 ++++ 6 files changed, 81 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index fba29a9b..0ae334e5 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,4 @@ -# elvis_core [![GitHub Actions CI][ci-img]][ci] [![Erlang Support][support-img]][support] - -[ci]: https://github.com/inaka/elvis_core -[ci-img]: https://github.com/inaka/elvis_core/workflows/build/badge.svg -[support]: http://www.erlang.org -[support-img]: https://img.shields.io/badge/Erlang/OTP-23+-blue +# elvis_core [![GitHub Actions CI](https://github.com/inaka/elvis_core/workflows/build/badge.svg)](https://github.com/inaka/elvis_core) [![Erlang Support](https://img.shields.io/badge/Erlang/OTP-24+-blue)](https://www.erlang.org) `elvis_core` is the core library for the [`elvis`](https://github.com/inaka/elvis) Erlang style reviewer. It is also used by [`rebar3_lint`](https://github.com/project-fifo/rebar3_lint) for easier @@ -59,7 +54,7 @@ current directory. If no configuration is found `{invalid_config, _}` is thrown. #### Providing configuration as a value Another option for using `elvis_core` from the shell is to explicitly provide the configuration as -an argument to `rock/1`: +an argument to `elvis_core:rock/1`: ```shell 1> ElvisConfig = [#{dirs => ["src"], filter => "*.erl", rules => []}]. @@ -241,16 +236,16 @@ environment variables, i.e. as they would be found by `application:get_env/2,3`. ### Pre-defined rules A reference to all pre-defined rules (and some other information) implemented in `elvis_core` can be -found in this repository's [RULES.md](RULES.md). +found in this repository's [RULES.md](https://github.com/inaka/elvis_core/blob/main/RULES.md). ### User-defined rules The implementation of a new rule is a function that takes 3 arguments in the following order: -1. `elvis_config:config()`: the value of option `config` as found in the +1. `t:elvis_config:config()`: the value of option `config` as found in the [configuration](#configuration), -1. `elvis_file:file()`: the file to be analyzed, -1. `map()`: a configuration map specific to your user-defined rule. +1. `t:elvis_file:file()`: the file to be analyzed, +1. `t:erlang:map()`: a configuration map specific to your user-defined rule. This means you can define rules of your own (user-defined rules) as long as the functions that implement them respect this interface. diff --git a/src/elvis_core.erl b/src/elvis_core.erl index 85876fe5..8609c0b1 100644 --- a/src/elvis_core.erl +++ b/src/elvis_core.erl @@ -142,6 +142,7 @@ main([]) -> %%% Private %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @private -spec combine_results(ok | {fail, [elvis_result:file()]}, ok | {fail, [elvis_result:file()]}) -> ok | {fail, [elvis_result:file()]}. @@ -157,6 +158,7 @@ apply_rules_and_print(Config, File) -> elvis_result:print_results(Results), Results. +%% @private -spec apply_rules(elvis_config:configs() | elvis_config:config(), File :: elvis_file:file()) -> elvis_result:file(). @@ -168,20 +170,24 @@ apply_rules(Config, File) -> lists:foldl(fun apply_rule/2, Acc, merge_rules({file, ParseTree}, lists:flatten(Rules))), elvis_result:new(file, File, RulesResults). +%% @private merge_rules({file, ParseTree}, ElvisConfigRules) -> ElvisAttrs = elvis_code:find(fun is_elvis_attr/1, ParseTree, #{traverse => content, mode => node}), ElvisAttrRules = elvis_attr_rules(ElvisAttrs), elvis_config:merge_rules(ElvisAttrRules, ElvisConfigRules). +%% @private is_elvis_attr(Node) -> ktn_code:type(Node) =:= elvis. +%% @private elvis_attr_rules([] = _ElvisAttrs) -> []; elvis_attr_rules(ElvisAttrs) -> [Rule || ElvisAttr <- ElvisAttrs, Rule <- ktn_code:attr(value, ElvisAttr)]. +%% @private -spec apply_rule({Mod, Fun} | {Mod, Fun, RuleCfg}, {Results, ElvisCfg, File}) -> Result when Mod :: module(), Fun :: atom(), @@ -223,6 +229,7 @@ apply_rule({Module, Function, ConfigArgs}, {Result, Config, File}) -> end, {[RuleResult | Result], Config, File}. +%% @private %% @doc Process a tules configuration argument and converts it to a map. ensure_config_map(_, _, Map) when is_map(Map) -> Map; diff --git a/src/elvis_file.erl b/src/elvis_file.erl index dc591905..b8999558 100644 --- a/src/elvis_file.erl +++ b/src/elvis_file.erl @@ -120,6 +120,7 @@ module(#{path := Path}) -> %% Private %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @private -spec resolve_parse_tree(string(), string() | binary(), module(), list()) -> undefined | ktn_code:tree_node(). resolve_parse_tree(".erl", Content, Mod, Ignore) -> @@ -131,6 +132,7 @@ resolve_parse_tree(".hrl", Content, Mod, Ignore) -> resolve_parse_tree(_, _, _, _) -> undefined. +%% @private filter_tree_for(Tree, Mod, Ignore) when is_map(Tree) -> TreeContent = maps:get(content, Tree, []), Tree#{content => @@ -145,6 +147,7 @@ filter_tree_for(Tree, Mod, Ignore) when is_map(Tree) -> filter_tree_for(Tree, _Mod, _Ignore) -> Tree. +%% @private -spec find_encoding(Content :: binary()) -> atom(). find_encoding(Content) -> case epp:read_encoding_from_binary(Content) of @@ -154,6 +157,7 @@ find_encoding(Content) -> Enc end. +%% @private -spec maybe_add_abstract_parse_tree(Config, File, Mod, Ignore) -> Res when Config :: elvis_config:configs() | elvis_config:config(), File :: file(), @@ -169,6 +173,7 @@ maybe_add_abstract_parse_tree(#{ruleset := beam_files}, maybe_add_abstract_parse_tree(_Config, File, _Mod, _Ignore) -> File. +%% @private -spec get_abstract_parse_tree(BeamPath, Mod, Ignore) -> Res when BeamPath :: file:filename(), Mod :: module(), @@ -178,6 +183,7 @@ get_abstract_parse_tree(BeamPath, Mod, Ignore) -> AbstractSrc = get_abstract_source(BeamPath), resolve_parse_tree(".erl", AbstractSrc, Mod, Ignore). +%% @private -spec get_abstract_source(BeamPath) -> Res when BeamPath :: file:filename() | binary(), Res :: string(). diff --git a/src/elvis_project.erl b/src/elvis_project.erl index 8745cf63..fc73b46f 100644 --- a/src/elvis_project.erl +++ b/src/elvis_project.erl @@ -88,6 +88,7 @@ old_configuration_format(_Config, Target, _RuleConfig) -> %%% Rebar +%% @private get_deps(File) -> {Src, _} = elvis_file:src(File), Terms = ktn_code:consult(Src), @@ -105,16 +106,19 @@ get_deps(File) -> end. %% Rebar3 +%% @private is_branch_dep({_AppName, {_SCM, _Location, {branch, _}}}) -> true; is_branch_dep({AppName, {raw, DepResourceSpecification}}) -> is_branch_dep({AppName, DepResourceSpecification}); %% Rebar2 +%% @private is_branch_dep({_AppName, _Vsn, {_SCM, _Location, {branch, _}}}) -> true; is_branch_dep(_) -> false. +%% @private is_hex_dep(_AppName) when is_atom(_AppName) -> true; is_hex_dep({_AppName, _Vsn, {pkg, _PackageName}}) @@ -125,6 +129,7 @@ is_hex_dep({_AppName, _Vsn}) when is_atom(_AppName), is_list(_Vsn) -> is_hex_dep(_) -> false. +%% @private is_not_git_dep({_AppName, {pkg, _OtherName}}, _Regex) -> false; is_not_git_dep({_AppName, {_SCM, Url, _Branch}}, Regex) -> @@ -138,6 +143,7 @@ is_not_git_dep({_AppName, _Vsn, {_SCM, Url}}, Regex) -> is_not_git_dep({_AppName, _Vsn, {_SCM, Url, _Branch}}, Regex) -> nomatch == re:run(Url, Regex, []). +%% @private dep_to_result({AppName, _}, Message, {IgnoreDeps, Regex}) -> case lists:member(AppName, IgnoreDeps) of true -> @@ -159,6 +165,7 @@ dep_to_result({AppName, _, GitInfo}, Message, IgnoreDeps) -> %% Old config +%% @private is_old_config(ElvisConfig) -> case proplists:get_value(config, ElvisConfig) of undefined -> @@ -172,6 +179,7 @@ is_old_config(ElvisConfig) -> lists:filter(SrcDirsIsKey, Config) /= [] end. +%% @private exists_old_rule(#{rules := Rules}) -> Filter = fun ({_, _, Args}) when is_list(Args) -> diff --git a/src/elvis_style.erl b/src/elvis_style.erl index e54ddf69..11fbcf1d 100644 --- a/src/elvis_style.erl +++ b/src/elvis_style.erl @@ -1339,10 +1339,12 @@ public_data_types(TypesToCheck, TreeRootNode, ExportedTypes) -> %% Private %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @private -spec name_arity_from_type_line(ktn_code:tree_node()) -> {atom(), integer()}. name_arity_from_type_line(#{attrs := #{name := Name}, node_attrs := #{args := Args}}) -> {Name, length(Args)}. +%% @private -spec map_type_declarations_to_line_numbers(ktn_code:tree_node()) -> #{{atom(), number()} => number()}. map_type_declarations_to_line_numbers(TreeRootNode) -> @@ -1358,11 +1360,13 @@ map_type_declarations_to_line_numbers(TreeRootNode) -> #{}, AllTypes). +%% @private specific_or_default(same, Regex) -> Regex; specific_or_default(RegexEnclosed, _Regex) -> RegexEnclosed. +%% @private check_numeric_format(_Regex, [], Acc) -> lists:reverse(Acc); check_numeric_format(Regex, [NumNode | RemainingNumNodes], AccIn) -> @@ -1386,12 +1390,15 @@ check_numeric_format(Regex, [NumNode | RemainingNumNodes], AccIn) -> end, check_numeric_format(Regex, RemainingNumNodes, AccOut). +%% @private is_integer_node(Node) -> ktn_code:type(Node) =:= integer. +%% @private is_float_node(Node) -> ktn_code:type(Node) =:= float. +%% @private is_exception_class(error) -> true; is_exception_class(exit) -> @@ -1401,6 +1408,7 @@ is_exception_class(throw) -> is_exception_class(_) -> false. +%% @private check_atom_names(_Regex, _RegexEnclosed, [] = _AtomNodes, Acc) -> Acc; check_atom_names(Regex, RegexEnclosed, [AtomNode | RemainingAtomNodes], AccIn) -> @@ -1432,6 +1440,7 @@ check_atom_names(Regex, RegexEnclosed, [AtomNode | RemainingAtomNodes], AccIn) - end, check_atom_names(Regex, RegexEnclosed, RemainingAtomNodes, AccOut). +%% @private string_strip_enclosed([$' | Rest]) -> [$' | Reversed] = lists:reverse(Rest), IsEnclosed = true, @@ -1441,6 +1450,7 @@ string_strip_enclosed(NonEnclosedAtomName) -> IsEnclosed = false, {IsEnclosed, NonEnclosedAtomName}. +%% @private re_compile_for_atom_type(false = _IsEnclosed, Regex, _RegexEnclosed) -> {ok, RE} = re:compile(Regex, [unicode]), RE; @@ -1448,10 +1458,12 @@ re_compile_for_atom_type(true = _IsEnclosed, _Regex, RegexEnclosed) -> {ok, RE} = re:compile(RegexEnclosed, [unicode]), RE. +%% @private is_atom_node(MaybeAtom) -> ktn_code:type(MaybeAtom) =:= atom. %% Variables name +%% @private check_variables_name(_Regex, []) -> []; check_variables_name(Regex, [Variable | RemainingVars]) -> @@ -1471,6 +1483,7 @@ check_variables_name(Regex, [Variable | RemainingVars]) -> %% Result building +%% @private result_node_line_fun(Msg) -> fun(Node) -> {Line, _} = ktn_code:attr(location, Node), @@ -1478,6 +1491,7 @@ result_node_line_fun(Msg) -> elvis_result:new(item, Msg, Info, Line) end. +%% @private result_node_line_col_fun(Msg) -> fun(Node) -> {Line, Col} = ktn_code:attr(location, Node), @@ -1489,6 +1503,7 @@ result_node_line_col_fun(Msg) -> %% Line Length +%% @private -spec line_is_comment(binary()) -> boolean(). line_is_comment(Line) -> case re:run(Line, "^[ \t]*%") of @@ -1498,6 +1513,7 @@ line_is_comment(Line) -> true end. +%% @private -spec line_is_whitespace(binary()) -> boolean(). line_is_whitespace(Line) -> case re:run(Line, "^[ \t]*$") of @@ -1509,6 +1525,7 @@ line_is_whitespace(Line) -> %% Macro Names +%% @private check_macro_names(_Regexp, [] = _MacroNodes, ResultsIn) -> ResultsIn; check_macro_names(Regexp, [MacroNode | RemainingMacroNodes], ResultsIn) -> @@ -1530,6 +1547,7 @@ check_macro_names(Regexp, [MacroNode | RemainingMacroNodes], ResultsIn) -> -dialyzer({no_match, is_macro_define_node/1}). +%% @private is_macro_define_node(MaybeMacro) -> case ktn_code:type(MaybeMacro) of {atom, [_, _], define} -> @@ -1538,6 +1556,7 @@ is_macro_define_node(MaybeMacro) -> false end. +%% @private macro_name_from_node(MacroNode) -> MacroNodeValue = ktn_code:attr(value, MacroNode), MacroAsAtom = macro_as_atom(false, [call, var, atom], MacroNodeValue), @@ -1545,6 +1564,7 @@ macro_name_from_node(MacroNode) -> MacroNameStripped = string:strip(MacroNameOriginal, both, $'), {MacroNameStripped, MacroNameOriginal}. +%% @private macro_as_atom({var, _Text, MacroAsAtom}, _Types, _MacroNodeValue) -> MacroAsAtom; macro_as_atom({atom, _Text, MacroAsAtom}, _Types, _MacroNodeValue) -> @@ -1558,6 +1578,7 @@ macro_as_atom(false, [Type | OtherTypes], MacroNodeValue) -> macro_as_atom(lists:keyfind(Type, _N = 1, MacroNodeValue), OtherTypes, MacroNodeValue). %% Operator (and Text) Spaces +%% @private -spec check_spaces(Lines :: [binary()], Nodes :: [ktn_code:tree_node()], Rule :: {right | left, string()}, @@ -1594,11 +1615,13 @@ check_spaces(Lines, UnfilteredNodes, {Position, Text}, Encoding, {How0, _} = How end, lists:flatmap(FlatFun, Nodes). +%% @private maybe_run_regex(undefined = _Regex, _Line) -> false; maybe_run_regex({ok, Regex}, Line) -> re:run(Line, Regex). +%% @private -spec character_at_location(Position :: atom(), Lines :: [binary()], Text :: string(), @@ -1656,6 +1679,7 @@ character_at_location(Position, end. %% Nesting Level +%% @private -spec check_nesting_level(ktn_code:tree_node(), [integer()]) -> [elvis_result:item()]. check_nesting_level(ParentNode, [MaxLevel]) -> case elvis_code:past_nesting_limit(ParentNode, MaxLevel) of @@ -1675,6 +1699,7 @@ check_nesting_level(ParentNode, [MaxLevel]) -> %% Invalid Dynamic Calls +%% @private -spec check_invalid_dynamic_calls(ktn_code:tree_node()) -> [elvis_result:item()]. check_invalid_dynamic_calls(Root) -> case elvis_code:find(fun is_dynamic_call/1, Root, #{traverse => all}) of @@ -1685,6 +1710,7 @@ check_invalid_dynamic_calls(Root) -> lists:map(ResultFun, InvalidCalls) end. +%% @private -spec is_dynamic_call(ktn_code:tree_node()) -> boolean(). is_dynamic_call(Node) -> case ktn_code:type(Node) of @@ -1702,6 +1728,7 @@ is_dynamic_call(Node) -> end. %% Plain Variable +%% @private -spec is_var(zipper:zipper(_)) -> boolean(). is_var(Zipper) -> case ktn_code:type( @@ -1729,6 +1756,7 @@ is_var(Zipper) -> %% Ignored Variable +%% @private -spec is_ignored_var(zipper:zipper(_)) -> boolean(). is_ignored_var(Zipper) -> Node = zipper:node(Zipper), @@ -1741,6 +1769,7 @@ is_ignored_var(Zipper) -> false end. +%% @private check_parent_match(Zipper) -> case zipper:up(Zipper) of undefined -> @@ -1759,6 +1788,7 @@ check_parent_match(Zipper) -> %% State record in OTP module +%% @private -spec is_otp_module(ktn_code:tree_node()) -> boolean(). is_otp_module(Root) -> OtpSet = sets:from_list([gen_server, gen_event, gen_fsm, gen_statem, supervisor_bridge]), @@ -1776,6 +1806,7 @@ is_otp_module(Root) -> sets:intersection(OtpSet, BehaviorsSet)) end. +%% @private -spec has_state_record(ktn_code:tree_node()) -> boolean(). has_state_record(Root) -> IsStateRecord = @@ -1783,6 +1814,7 @@ has_state_record(Root) -> end, [] /= elvis_code:find(IsStateRecord, Root). +%% @private -spec has_state_type(ktn_code:tree_node()) -> boolean(). has_state_type(Root) -> IsStateType = @@ -1792,6 +1824,7 @@ has_state_type(Root) -> %% Spec includes records +%% @private -spec spec_includes_record(ktn_code:tree_node()) -> boolean(). spec_includes_record(Node) -> IsTypeRecord = @@ -1802,6 +1835,7 @@ spec_includes_record(Node) -> %% Don't repeat yourself +%% @private -spec find_repeated_nodes(ktn_code:tree_node(), non_neg_integer()) -> [ktn_code:tree_node()]. find_repeated_nodes(Root, MinComplexity) -> @@ -1831,10 +1865,12 @@ find_repeated_nodes(Root, MinComplexity) -> lists:map(fun lists:sort/1, Locations). +%% @private -spec remove_attrs_zipper(zipper:zipper(_), map()) -> ktn_code:tree_node(). remove_attrs_zipper(Zipper, TypeAttrs) -> zipper:fmap(fun remove_attrs/2, [TypeAttrs], Zipper). +%% @private -spec remove_attrs(ktn_code:tree_node() | [ktn_code:tree_node()], map()) -> ktn_code:tree_node(). remove_attrs(Nodes, TypeAttrs) when is_list(Nodes) -> @@ -1858,6 +1894,7 @@ remove_attrs(#{attrs := Attrs, type := Type} = Node, TypeAttrs) -> remove_attrs(Node, _TypeAttrs) -> Node. +%% @private -spec filter_repeated(map()) -> map(). filter_repeated(NodesLocs) -> NotRepeated = @@ -1875,11 +1912,13 @@ filter_repeated(NodesLocs) -> maps:without(Nested, RepeatedMap). +%% @private is_children(Parent, Node) -> Zipper = elvis_code:code_zipper(Parent), [] =/= zipper:filter(fun(Child) -> Child == Node end, Zipper). %% No call +%% @private -spec no_call_common(elvis_config:config(), elvis_file:file(), [function_spec()], @@ -1892,6 +1931,7 @@ no_call_common(Config, Target, NoCallFuns, Msg, RuleConfig) -> Calls = elvis_code:find(fun is_call/1, Root), check_no_call(Calls, Msg, NoCallFuns). +%% @private -spec check_no_call([ktn_code:tree_node()], string(), [function_spec()]) -> [elvis_result:item()]. check_no_call(Calls, Msg, NoCallFuns) -> @@ -1904,11 +1944,13 @@ check_no_call(Calls, Msg, NoCallFuns) -> end, lists:map(ResultFun, DebugCalls). +%% @private is_in_call_list(Call, DebugFuns) -> MFA = call_mfa(Call), MatchFun = fun(Spec) -> fun_spec_match(Spec, MFA) end, lists:any(MatchFun, DebugFuns). +%% @private call_mfa(Call) -> FunctionSpec = ktn_code:node_attr(function, Call), M = ktn_code:attr(value, ktn_code:node_attr(module, FunctionSpec)), @@ -1916,6 +1958,7 @@ call_mfa(Call) -> A = length(ktn_code:content(Call)), {M, F, A}. +%% @private is_call(Node, {F, A}) -> ktn_code:type(Node) =:= call andalso list_to_atom(ktn_code:attr(text, Node)) =:= F @@ -1923,9 +1966,11 @@ is_call(Node, {F, A}) -> is_call(Node, {M, F, A}) -> call_mfa(Node) =:= {M, F, A}. +%% @private is_call(Node) -> ktn_code:type(Node) =:= call. +%% @private fun_spec_match({M, F}, {M, F, _}) -> true; fun_spec_match({M, F, A}, {M, F, A}) -> @@ -1933,6 +1978,7 @@ fun_spec_match({M, F, A}, {M, F, A}) -> fun_spec_match(_, _) -> false. +%% @private %% @doc No nested try...catch blocks check_nested_try_catchs(ResultFun, TryExp) -> Predicate = fun(Node) -> ktn_code:type(Node) == 'try' end, @@ -1943,6 +1989,7 @@ check_nested_try_catchs(ResultFun, TryExp) -> end, elvis_code:find(Predicate, TryExp)). +%% @private %% @doc No #{...}#{...} check_successive_maps(ResultFun, MapExp) -> case ktn_code:node_attr(var, MapExp) of @@ -1958,6 +2005,7 @@ check_successive_maps(ResultFun, MapExp) -> end. %% Consistent Generic Type +%% @private consistent_generic_type_predicate(TypePreference) -> fun(Node) -> NodeType = ktn_code:type(Node), @@ -1967,6 +2015,7 @@ consistent_generic_type_predicate(TypePreference) -> andalso NodeName /= TypePreference end. +%% @private consistent_generic_type_result(TypePreference) -> fun(Node) -> {Line, _} = ktn_code:attr(location, Node), diff --git a/src/elvis_text_style.erl b/src/elvis_text_style.erl index 9583266a..c560d166 100644 --- a/src/elvis_text_style.erl +++ b/src/elvis_text_style.erl @@ -77,6 +77,7 @@ no_trailing_whitespace(_Config, Target, RuleConfig) -> %% Line Length +%% @private -spec line_is_comment(binary()) -> boolean(). line_is_comment(Line) -> case re:run(Line, "^[ \t]*%") of @@ -86,6 +87,7 @@ line_is_comment(Line) -> true end. +%% @private -spec remove_comment(binary()) -> binary(). remove_comment(Line) -> case re:run(Line, "([^%]+)", [{capture, first, binary}]) of @@ -95,6 +97,7 @@ remove_comment(Line) -> Without end. +%% @private -spec check_line_length(binary(), integer(), [term()]) -> no_result | {ok, elvis_result:item()}. check_line_length(Line, Num, [Limit, whole_line, Encoding]) -> @@ -123,6 +126,7 @@ check_line_length(Line, Num, [Limit, Encoding]) -> %% No Tabs +%% @private -spec check_no_tabs(binary(), integer()) -> no_result | {ok, elvis_result:item()}. check_no_tabs(Line, Num) -> case binary:match(Line, <<"\t">>) of @@ -137,6 +141,7 @@ check_no_tabs(Line, Num) -> %% No Trailing Whitespace +%% @private -spec check_no_trailing_whitespace(binary(), integer(), boolean()) -> no_result | {ok, elvis_result:item()}. check_no_trailing_whitespace(Line, Num, IgnoreEmptyLines) -> From 5cd99e7431d8d8fc32ab89375afe61223f4313d5 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 19:16:31 +0100 Subject: [PATCH 05/18] Fix as per `rebar3 as test test` results --- src/elvis_core.erl | 2 +- src/elvis_project.erl | 10 +++++----- test/examples/consistent_generic_type_no_checks.erl | 2 +- test/examples/fail_no_catch_expressions.erl | 2 ++ test/examples/fail_no_space.erl | 2 +- test/examples/fail_no_space_after_pound.erl | 4 ++-- test/examples/fail_no_throw.erl | 2 ++ test/examples/fail_operator_spaces.erl | 4 ++-- test/examples/pass_no_space_after_pound.erl | 4 ++-- test/style_SUITE.erl | 4 ++-- 10 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/elvis_core.erl b/src/elvis_core.erl index 8609c0b1..ad849130 100644 --- a/src/elvis_core.erl +++ b/src/elvis_core.erl @@ -192,7 +192,7 @@ elvis_attr_rules(ElvisAttrs) -> when Mod :: module(), Fun :: atom(), RuleCfg :: rule_config(), - Results :: [elvis_result:rule()], + Results :: [elvis_result:rule() | elvis_result:elvis_error()], ElvisCfg :: elvis_config:config(), File :: elvis_file:file(), Result :: {Results, ElvisCfg, File}. diff --git a/src/elvis_project.erl b/src/elvis_project.erl index fc73b46f..47b7b79b 100644 --- a/src/elvis_project.erl +++ b/src/elvis_project.erl @@ -2,7 +2,7 @@ -export([default/1, no_branch_deps/3, protocol_for_deps/3, old_configuration_format/3]). --export_type([protocol_for_deps_config/0, empty_rule_config/0]). +-export_type([protocol_for_deps_config/0]). -define(DEP_BRANCH, "Dependency '~s' uses a branch. " @@ -53,7 +53,9 @@ protocol_for_deps(_Config, Target, RuleConfig) -> lists:flatmap(fun(Line) -> dep_to_result(Line, ?DEP_NO_GIT, {IgnoreDeps, Regex}) end, BadDeps). --spec no_branch_deps(elvis_config:config(), elvis_file:file(), empty_rule_config()) -> +-spec no_branch_deps(elvis_config:config(), + elvis_file:file(), + elvis_style:empty_rule_config()) -> [elvis_result:item()]. no_branch_deps(_Config, Target, RuleConfig) -> IgnoreDeps = option(ignore, RuleConfig, no_branch_deps), @@ -61,11 +63,9 @@ no_branch_deps(_Config, Target, RuleConfig) -> BadDeps = lists:filter(fun is_branch_dep/1, Deps), lists:flatmap(fun(Line) -> dep_to_result(Line, ?DEP_BRANCH, IgnoreDeps) end, BadDeps). --type empty_rule_config() :: #{}. - -spec old_configuration_format(elvis_config:config(), elvis_file:file(), - empty_rule_config()) -> + elvis_style:empty_rule_config()) -> [elvis_result:item()]. old_configuration_format(_Config, Target, _RuleConfig) -> {Content, _} = elvis_file:src(Target), diff --git a/test/examples/consistent_generic_type_no_checks.erl b/test/examples/consistent_generic_type_no_checks.erl index b76396b8..8082bffc 100644 --- a/test/examples/consistent_generic_type_no_checks.erl +++ b/test/examples/consistent_generic_type_no_checks.erl @@ -22,6 +22,6 @@ term() -> term. % A function that calls the function called any --spec my_function(Thing :: any | term) -> any | term. +-spec my_function(Thing :: #my_record{}) -> any | term. my_function(#my_record{any = any}) -> any(); my_function(#my_record{term = term}) -> term(). diff --git a/test/examples/fail_no_catch_expressions.erl b/test/examples/fail_no_catch_expressions.erl index 776d2646..e75d4a19 100644 --- a/test/examples/fail_no_catch_expressions.erl +++ b/test/examples/fail_no_catch_expressions.erl @@ -2,6 +2,8 @@ -export([catchf/0, try_catch/0, mixem/0]). +-dialyzer({nowarn_function, [catchf/0, try_catch/0, mixem/0]}). + catchf() -> F = fun (a) -> "catch" end, catch F(b). diff --git a/test/examples/fail_no_space.erl b/test/examples/fail_no_space.erl index c7cb917d..36b47413 100644 --- a/test/examples/fail_no_space.erl +++ b/test/examples/fail_no_space.erl @@ -108,7 +108,7 @@ this() -> should_not_crash. -spec this(shouldnt_either) --> -1. +-> 32. this(shouldnt_either) -> A = 1 - 2, A, $ . diff --git a/test/examples/fail_no_space_after_pound.erl b/test/examples/fail_no_space_after_pound.erl index 66b22a0c..0a0a753c 100644 --- a/test/examples/fail_no_space_after_pound.erl +++ b/test/examples/fail_no_space_after_pound.erl @@ -2,7 +2,7 @@ -format ignore. --record(bad, {record :: # bad{}}). +-record(bad, {record :: undefined | # bad{}}). -type bad_record() :: # bad{}. -type bad_map() :: # {bad => map}. @@ -15,7 +15,7 @@ bad_records() -> # bad{record = R2} = R, R2# bad{record = element(# bad.record, R)}. --spec bad_maps() -> # {bad => map} | bad_map(). +-spec bad_maps() -> # {bad := bad_map()}. bad_maps() -> M = # {bad => # {bad => map}}, # {bad := M2} = M, diff --git a/test/examples/fail_no_throw.erl b/test/examples/fail_no_throw.erl index e568e913..fff3f574 100644 --- a/test/examples/fail_no_throw.erl +++ b/test/examples/fail_no_throw.erl @@ -2,6 +2,8 @@ -export([f/0]). +-dialyzer({nowarn_function, [f/0, thro/2]}). + f() -> thro(exit, 2), throw = throw, diff --git a/test/examples/fail_operator_spaces.erl b/test/examples/fail_operator_spaces.erl index 8a459b53..70f8becf 100644 --- a/test/examples/fail_operator_spaces.erl +++ b/test/examples/fail_operator_spaces.erl @@ -119,7 +119,7 @@ pass_more_operators() -> X > 3, X >= 4, X == 5, - X =:= 6, + X =:= 6.0, X /= 7, X =/= 8, D -- "", @@ -141,7 +141,7 @@ fail_more_operators()-> X>3, X>=4, X==5, - X=:=6, + X=:=6.0, X/=7, X=/=8, D--"", diff --git a/test/examples/pass_no_space_after_pound.erl b/test/examples/pass_no_space_after_pound.erl index e5eb6392..54ddb158 100644 --- a/test/examples/pass_no_space_after_pound.erl +++ b/test/examples/pass_no_space_after_pound.erl @@ -2,7 +2,7 @@ -format ignore. --record(good, {record :: #good{}}). +-record(good, {record :: undefined | #good{}}). -type good_record() :: #good{}. -type good_map() :: #{good => map}. @@ -15,7 +15,7 @@ good_records() -> #good{record = R2} = R, R2#good{record = element(#good.record, R)}. --spec good_maps() -> #{good => map} | good_map(). +-spec good_maps() -> #{good => good_map()}. good_maps() -> M = #{good => #{good => map}}, #{good := M2} = M, diff --git a/test/style_SUITE.erl b/test/style_SUITE.erl index 9b259528..fbfb73cf 100644 --- a/test/style_SUITE.erl +++ b/test/style_SUITE.erl @@ -1435,9 +1435,9 @@ verify_no_catch_expressions(Config) -> R = elvis_core_apply_rule(Config, elvis_style, no_catch_expressions, #{}, FailPath), _ = case Group of beam_files -> - [#{info := [18]}, #{info := [18]}, #{info := [7]}] = R; + [#{info := [21]}, #{info := [21]}, #{info := [10]}] = R; erl_files -> - [#{info := [24]}, #{info := [22]}, #{info := [7]}] = R + [#{info := [26]}, #{info := [24]}, #{info := [9]}] = R end. -spec verify_no_single_clause_case(config()) -> any(). From 2d929d32c804e77aa0b4391ed8408df1921a5980 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 19:21:23 +0100 Subject: [PATCH 06/18] Ease maintenance of .gitignore Only keep generated stuff + _* (e.g. for _build and _checkouts) --- .gitignore | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 5408aa84..042748fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,10 @@ -.eunit -.rebar -deps -ebin -*.o -*.beam -*.plt erl_crash.dump -log*/ -_build +_* +doc/ +rebar3.crashdump +.rebar3 +logs +test/**/*.beam + # Ignore elvis escript elvis -rebar -elvis.d -doc -rebar3.crashdump -.rebar3/ -rebar3 From db54559a8967825661e3a66088e938c802ee5b13 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 19:53:11 +0100 Subject: [PATCH 07/18] Update reference to "the present" --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16377bc9..d26ad3fd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,4 +33,4 @@ file. ## Questions? If you have any questions or general comments regarding how to contribute, please use our public -[hipchat room](http://inaka.net/hipchat). +Erlanger Slack channel: [#elvis](https://erlanger.slack.com/archives/C01073W0E15). From cdc062dd34accb602bf8fe7ed5a46bffad194a96 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 19:54:02 +0100 Subject: [PATCH 08/18] Up our CI versions --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/lint.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c166bfa3..31962065 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,15 +13,16 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - otp_vsn: [23, 24, 25] - os: [ubuntu-20.04, windows-latest] + otp_vsn: ['24', '25', '26'] + rebar3_vsn: ['3.22'] + os: [ubuntu-22.04, windows-2022] steps: - uses: actions/checkout@v3 - uses: erlef/setup-beam@v1 id: setup-beam with: otp-version: ${{matrix.otp_vsn}} - rebar3-version: '3.20' + rebar3-version: ${{matrix.rebar3_vsn}} - name: Restore _build uses: actions/cache@v3 with: @@ -43,7 +44,6 @@ jobs: - name: Compile run: rebar3 compile - name: Format check - if: ${{ matrix.os == 'ubuntu-20.04' }} run: rebar3 format --verify - name: Run test run: rebar3 test diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0c7ecd0c..5742cd21 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,14 +5,14 @@ on: [push, pull_request] jobs: md_and_yml: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 # uses .markdownlint.yml for configuration - name: markdownlint - uses: DavidAnson/markdownlint-cli2-action@v5 + uses: DavidAnson/markdownlint-cli2-action@v11 with: globs: | LICENSE From 77f0a7aff1c19acd9ec5a238a93232651c578aae Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 19:54:11 +0100 Subject: [PATCH 09/18] Move Github to GitHub --- src/elvis_core.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elvis_core.app.src b/src/elvis_core.app.src index fedd9209..c2d09fa6 100644 --- a/src/elvis_core.app.src +++ b/src/elvis_core.app.src @@ -7,5 +7,5 @@ {modules, [elvis_core, elvis_config, elvis_result, elvis_utils, elvis_style]}, {registered, []}, {licenses, ["Apache 2.0"]}, - {links, [{"Github", "https://github.com/inaka/elvis_core"}]}, + {links, [{"GitHub", "https://github.com/inaka/elvis_core"}]}, {build_tools, ["rebar3"]}]}. From 1c14277d2ab142cfa8d0c608e5a42b39bf0a851a Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 19:54:26 +0100 Subject: [PATCH 10/18] Make it easier to pre-test pull requests in forks --- .github/workflows/ci.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31962065..53d34af6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,6 @@ --- name: build -on: - push: - branches: - - main - pull_request: - branches: - - main +on: [push, pull_request] jobs: ci: name: OTP ${{matrix.otp_vsn}} on ${{matrix.os}} From 14c58ef4d7426d0035b4396fd51b0fc66430d160 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 20:00:14 +0100 Subject: [PATCH 11/18] "Modernize" it - tweak erl_opts - bump minimum_otp_vsn - move test options to `test` profile - update our dep.s'/plugins' versions - tweak alias `test` - tweak `test` profile `dialyzer` and use it to improve the project - tweak dialyzer options - add ex_doc - tweak format options - tweak hank options --- rebar.config | 72 +++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/rebar.config b/rebar.config index 7edcc8c8..2ad96669 100644 --- a/rebar.config +++ b/rebar.config @@ -1,61 +1,59 @@ -%% -*- mode: erlang;erlang-indent-level: 2;indent-tabs-mode: nil -*- -%% ex: ts=4 sw=4 ft=erlang et +%% == Compiler and Profiles == -%% == Erlang Compiler == +{erl_opts, + [warn_unused_import, warn_export_vars, warnings_as_errors, verbose, report, debug_info]}. -%% Erlang compiler options -{erl_opts, [debug_info, warn_export_vars, warn_missing_spec, warn_unused_import]}. +{minimum_otp_vsn, "23"}. {profiles, [{test, [{extra_src_dirs, ["test/examples"]}, {deps, [{meck, "0.9.2"}]}, {erl_opts, [nowarn_missing_spec, nowarn_export_all]}, - {dialyzer, [{warnings, [no_return, unmatched_returns, error_handling]}]}]}]}. + {dialyzer, [{warnings, [no_return, error_handling]}, {plt_extra_apps, [common_test]}]}, + {ct_opts, [{sys_config, ["./config/test.config"]}, {logdir, "./logs"}, {verbose, true}]}, + {cover_enabled, true}, + {cover_opts, [verbose]}]}]}. -{ct_opts, [{sys_config, ["./config/test.config"]}, {logdir, "./logs"}, {verbose, true}]}. +{alias, [{test, [compile, format, hank, lint, xref, dialyzer, ct, cover, ex_doc]}]}. -%% == Cover == - -{cover_enabled, true}. - -{cover_opts, [verbose]}. +{shell, [{config, "config/test.config"}]}. -%% == Dependencies == +%% == Dependencies and plugins == -{deps, [{zipper, "1.0.1"}, {katana_code, "~> 2.0.2"}]}. +{deps, [{zipper, "1.0.1"}, {katana_code, "~> 2.1.0"}]}. -%% == Dialyzer == +{project_plugins, + [{rebar3_hank, "~> 1.4.0"}, + {rebar3_hex, "~> 7.0.7"}, + {rebar3_format, "~> 1.3.0"}, + {rebar3_lint, "~> 3.0.1"}, + {rebar3_ex_doc, "~> 0.2.18"}]}. -{dialyzer, - [{warnings, [no_return, unmatched_returns, error_handling, unknown]}, - {plt_apps, top_level_deps}, - {plt_extra_apps, [kernel, stdlib]}, - {plt_location, local}]}. +%% == Documentation == -{shell, [{config, "config/test.config"}]}. +{ex_doc, + [{source_url, <<"https://github.com/inaka/elvis_core">>}, + {extras, [<<"README.md">>, <<"LICENSE">>]}, + {main, <<"readme">>}]}. -%% == xref == +{hex, [{doc, #{provider => ex_doc}}]}. -{xref_checks, [undefined_function_calls, locals_not_used, deprecated_function_calls]}. +%% == Format == -%% == Aliases == +{format, + [{files, + ["config/**/*.config", "src/**/*.app.src", "src/**/*.erl", "test/*.erl", "*.config"]}]}. -{alias, [{test, [format, xref, dialyzer, hank, ct, cover, edoc]}]}. +%% == Hank == -{project_plugins, - [{rebar3_hex, "~> 7.0.1"}, {rebar3_format, "~> 1.2.0"}, {rebar3_hank, "~> 1.3.0"}]}. +{hank, [{ignore, ["test/*/**"]}]}. -%% == hank == +%% == Dialyzer + XRef == -{hank, - [{ignore, - [{"test/*/**", unnecessary_function_arguments}, - {"test/*/**", unused_macros}, - {"test/*/**", unused_callbacks}]}]}. +{dialyzer, [{warnings, [no_return, unmatched_returns, error_handling, unknown]}]}. -%% == format == +{xref_checks, + [undefined_function_calls, deprecated_function_calls, deprecated_functions]}. -{format, - [{files, - ["config/**/*.config", "src/**/*.app.src", "src/**/*.erl", "test/*.erl", "*.config"]}]}. +{xref_extra_paths, ["test/**"]}. From 001a2cb661502ee961a2dfb3daf397bf3dedf13a Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 20:05:41 +0100 Subject: [PATCH 12/18] Fix as per CI results: md_and_yml README.md:1:101 MD013/line-length Line length [Expected: 100; Actual: 229] --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ae334e5..1e5b2ade 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# elvis_core [![GitHub Actions CI](https://github.com/inaka/elvis_core/workflows/build/badge.svg)](https://github.com/inaka/elvis_core) [![Erlang Support](https://img.shields.io/badge/Erlang/OTP-24+-blue)](https://www.erlang.org) +# elvis_core + +[![GitHub Actions CI](https://github.com/inaka/elvis_core/workflows/build/badge.svg)](https://github.com/inaka/elvis_core) +[![Erlang Support](https://img.shields.io/badge/Erlang/OTP-24+-blue)](https://www.erlang.org) `elvis_core` is the core library for the [`elvis`](https://github.com/inaka/elvis) Erlang style reviewer. It is also used by [`rebar3_lint`](https://github.com/project-fifo/rebar3_lint) for easier From 21e7f13a3c281ba54de5ac5a6e97ff877f3958e3 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 20:08:33 +0100 Subject: [PATCH 13/18] Fix as per CI results: format --verify failing on Windows --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53d34af6..a89e259f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,7 @@ jobs: - name: Compile run: rebar3 compile - name: Format check + if: ${{ matrix.os == 'ubuntu-22.04' }} run: rebar3 format --verify - name: Run test run: rebar3 test From 00d92ce785d2e531ef7767384d11836a3101a2e8 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 20:51:47 +0100 Subject: [PATCH 14/18] Be less optimistic regarding rebar3_ex_doc Version 0.2.19 is failing with an issue I can't understand at the moment --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 2ad96669..9d57533b 100644 --- a/rebar.config +++ b/rebar.config @@ -28,7 +28,7 @@ {rebar3_hex, "~> 7.0.7"}, {rebar3_format, "~> 1.3.0"}, {rebar3_lint, "~> 3.0.1"}, - {rebar3_ex_doc, "~> 0.2.18"}]}. + {rebar3_ex_doc, "0.2.18"}]}. %% == Documentation == From 8db5c89d718983c384d6db6d42b3aff78f344f28 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 6 Aug 2023 21:52:05 +0100 Subject: [PATCH 15/18] Fix two minor documentation related issues --- RULES.md | 2 +- doc_rules/elvis_style/operator_spaces.md | 35 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 doc_rules/elvis_style/operator_spaces.md diff --git a/RULES.md b/RULES.md index 808aea39..8970c6e6 100644 --- a/RULES.md +++ b/RULES.md @@ -45,7 +45,7 @@ identified with `(since ...)` for convenience purposes. - [No Match in Condition](doc_rules/elvis_style/no_match_in_condition.md) - [No Nested try...catch Blocks](doc_rules/elvis_style/no_nested_try_catch.md) - [No Single-Clause Case Statements](doc_rules/elvis_style/no_single_clause_case.md) -- [No Space after #](doc_rules/elvis_style/no_space_after_pount.md) +- [No Space after #](doc_rules/elvis_style/no_space_after_pound.md) - [No Space](doc_rules/elvis_style/no_space.md) - [No Spec With Records](doc_rules/elvis_style/no_spec_with_records.md) - [No Specs](doc_rules/elvis_style/no_specs.md) diff --git a/doc_rules/elvis_style/operator_spaces.md b/doc_rules/elvis_style/operator_spaces.md new file mode 100644 index 00000000..cdf68035 --- /dev/null +++ b/doc_rules/elvis_style/operator_spaces.md @@ -0,0 +1,35 @@ +# Operator Spaces + +There should be a space in the position (e.g., `right` or `left`) of the operators specified. The +operator can be any string. + +> Works on `.beam` file? Not really! (it consumes results Ok, but these might be unexpected) + +## Options + +- `rules :: [{right | left, string()}].` + - default: + - before [1.5.0](https://github.com/inaka/elvis_core/releases/tag/1.5.0): `[{right, ","}, + {right, "++"}, {left, "++"}]` + - since [1.5.0](https://github.com/inaka/elvis_core/releases/tag/1.5.0): + +```erlang + [{right, "++"}, {left, "++"}, {right, "="}, {left, "="}, {right, "+"}, {left, "+"}, + {right, "-"}, {left, "-"}, {right, "*"}, {left, "*"}, {right, "/"}, {left, "/"}, + {right, "=<"}, {left, "=<"}, {right, "<"}, {left, "<"}, {right, ">"}, {left, ">"}, + {right, ">="}, {left, ">="}, {right, "=="}, {left, "=="}, {right, "=:="}, {left, "=:="}, + {right, "/="}, {left, "/="}, {right, "=/="}, {left, "=/="}, {right, "--"}, {left, "--"}, + {right, "=>"}, {left, "=>"}, {right, ":="}, {left, ":="}, {right, "<-"}, {left, "<-"}, + {right, "<="}, {left, "<="}, {right, "||"}, {left, "||"}, {right, "|"}, {left, "|"}, + {right, "::"}, {left, "::"}, {right, "->"}, {left, "->"}, {right, ","}] +``` + +## Example + +```erlang +{elvis_style, operator_spaces, #{ rules => [{right, ","} + , {right, "++"} + , {left, "++"} + ] + }} +``` From 177636c2d08df200cb5d3f6307fb7941ad738ad3 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Mon, 7 Aug 2023 11:15:08 +0100 Subject: [PATCH 16/18] Act on review result: elvis_core already lints itself in CI --- rebar.config | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 9d57533b..b034a1ad 100644 --- a/rebar.config +++ b/rebar.config @@ -15,7 +15,7 @@ {cover_enabled, true}, {cover_opts, [verbose]}]}]}. -{alias, [{test, [compile, format, hank, lint, xref, dialyzer, ct, cover, ex_doc]}]}. +{alias, [{test, [compile, format, hank, xref, dialyzer, ct, cover, ex_doc]}]}. {shell, [{config, "config/test.config"}]}. @@ -27,7 +27,6 @@ [{rebar3_hank, "~> 1.4.0"}, {rebar3_hex, "~> 7.0.7"}, {rebar3_format, "~> 1.3.0"}, - {rebar3_lint, "~> 3.0.1"}, {rebar3_ex_doc, "0.2.18"}]}. %% == Documentation == From 0982d97611ac4f0e94d34b356ab8677faaa5c47d Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Mon, 7 Aug 2023 11:26:16 +0100 Subject: [PATCH 17/18] Have `rebar3 test` run self-tests --- .github/workflows/ci.yml | 4 ---- rebar.config | 4 +++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a89e259f..78b10b41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,3 @@ jobs: run: rebar3 format --verify - name: Run test run: rebar3 test - - name: Run elvis on elvis - run: | - rebar3 escriptize - _build/default/bin/elvis_core diff --git a/rebar.config b/rebar.config index b034a1ad..1415bf4c 100644 --- a/rebar.config +++ b/rebar.config @@ -15,7 +15,9 @@ {cover_enabled, true}, {cover_opts, [verbose]}]}]}. -{alias, [{test, [compile, format, hank, xref, dialyzer, ct, cover, ex_doc]}]}. +{alias, [{test, [compile, format, hank, xref, dialyzer, ct, cover, ex_doc, escriptize]}]}. + +{post_hooks, [{escriptize, "_build/default/bin/elvis_core"}]}. {shell, [{config, "config/test.config"}]}. From b0167c264c8aa5d6a4517f1f0ed8eeed40d2c92c Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Mon, 7 Aug 2023 11:46:07 +0100 Subject: [PATCH 18/18] Fix as per CI results: rebar3 escriptize post-hook failing on Windows --- .github/workflows/ci.yml | 4 ++++ rebar.config | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78b10b41..a89e259f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,3 +42,7 @@ jobs: run: rebar3 format --verify - name: Run test run: rebar3 test + - name: Run elvis on elvis + run: | + rebar3 escriptize + _build/default/bin/elvis_core diff --git a/rebar.config b/rebar.config index 1415bf4c..b034a1ad 100644 --- a/rebar.config +++ b/rebar.config @@ -15,9 +15,7 @@ {cover_enabled, true}, {cover_opts, [verbose]}]}]}. -{alias, [{test, [compile, format, hank, xref, dialyzer, ct, cover, ex_doc, escriptize]}]}. - -{post_hooks, [{escriptize, "_build/default/bin/elvis_core"}]}. +{alias, [{test, [compile, format, hank, xref, dialyzer, ct, cover, ex_doc]}]}. {shell, [{config, "config/test.config"}]}.