From 73f2032ad2baef67fed2ee659f2ee2a616e36ae8 Mon Sep 17 00:00:00 2001 From: bormilan Date: Fri, 15 Nov 2024 18:47:43 +0100 Subject: [PATCH] new rule: ms_transform_included --- RULES.md | 1 + .../elvis_style/ms_transform_included.md | 16 +++++ src/elvis_rulesets.erl | 2 + src/elvis_style.erl | 59 ++++++++++++++++++- test/examples/fail_ms_transform.erl | 5 -- test/examples/fail_ms_transform_included.erl | 5 ++ test/examples/pass_ms_transform.erl | 5 -- test/examples/pass_ms_transform_included.erl | 7 +++ test/style_SUITE.erl | 19 +++--- 9 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 doc_rules/elvis_style/ms_transform_included.md delete mode 100644 test/examples/fail_ms_transform.erl create mode 100644 test/examples/fail_ms_transform_included.erl delete mode 100644 test/examples/pass_ms_transform.erl create mode 100644 test/examples/pass_ms_transform_included.erl diff --git a/RULES.md b/RULES.md index df78f2c..82d3013 100644 --- a/RULES.md +++ b/RULES.md @@ -64,6 +64,7 @@ identified with `(since ...)` for convenience purposes. - [Variable Naming Convention](doc_rules/elvis_style/variable_naming_convention.md) - [No Init Lists](doc_rules/elvis_style/no_init_lists.md) - [Prefer Unquoted Atoms](doc_rules/elvis_text_style/prefer_unquoted_atoms.md) +- [ms_transform included](doc_rules/elvis_style/ms_transform_included.md) ## `.gitignore` rules diff --git a/doc_rules/elvis_style/ms_transform_included.md b/doc_rules/elvis_style/ms_transform_included.md new file mode 100644 index 0000000..42ab717 --- /dev/null +++ b/doc_rules/elvis_style/ms_transform_included.md @@ -0,0 +1,16 @@ + +# No Init Lists + +The library `ms_transform` needs to be included if the function`ets:fun2ms` is used in the module. + +> Works on `.beam` files? Yes! + +## Options + +- None. + +## Example + +```erlang +{elvis_style, ms_transform_included, #{}} +``` diff --git a/src/elvis_rulesets.erl b/src/elvis_rulesets.erl index 4d1d471..0160bbf 100644 --- a/src/elvis_rulesets.erl +++ b/src/elvis_rulesets.erl @@ -96,6 +96,7 @@ rules(erl_files_strict) -> max_function_clause_length, max_function_length, max_module_length, + ms_transform_included, no_call, no_init_lists, no_common_caveats_call, @@ -113,6 +114,7 @@ rules(beam_files) -> invalid_dynamic_call, max_anonymous_function_arity, max_function_arity, + ms_transform_included, module_naming_convention, nesting_level, no_author, diff --git a/src/elvis_style.erl b/src/elvis_style.erl index e52ba9f..f2889a7 100644 --- a/src/elvis_style.erl +++ b/src/elvis_style.erl @@ -13,7 +13,7 @@ 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, - no_init_lists/3]). + no_init_lists/3, ms_transform_included/3]). -export_type([empty_rule_config/0]). -export_type([ignorable/0]). @@ -33,6 +33,8 @@ -define(NO_INIT_LISTS_MSG, "Do not use a list as the parameter for the 'init' callback at position ~p."). +-define(MS_TRANSFORM_INCLUDED_MSG, + "Missing inclide library: stdlib/include/ms_transform.hrl at position ~p."). -define(INVALID_MACRO_NAME_REGEX_MSG, "The macro named ~p on line ~p does not respect the format " "defined by the regular expression '~p'."). @@ -260,7 +262,8 @@ default(RuleWithEmptyDefault) RuleWithEmptyDefault == always_shortcircuit; RuleWithEmptyDefault == no_space_after_pound; RuleWithEmptyDefault == export_used_types; - RuleWithEmptyDefault == consistent_variable_casing -> + RuleWithEmptyDefault == consistent_variable_casing; + RuleWithEmptyDefault == ms_transform_included -> #{}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1192,6 +1195,58 @@ is_list_node(#{type := match, content := Content}) -> is_list_node(_) -> false. +-spec ms_transform_included(elvis_config:config(), + elvis_file:file(), + empty_rule_config()) -> + [elvis_result:item()]. +ms_transform_included(Config, Target, RuleConfig) -> + Root = get_root(Config, Target, RuleConfig), + IsIncludeMsTransform = + fun(Node) -> + ktn_code:type(Node) == include_lib + andalso ktn_code:attr(value, Node) == "stdlib/include/ms_transform.hrl" + end, + + ResFunctions = + case elvis_code:find(IsIncludeMsTransform, Root) of + [] -> + get_fun_2_ms_calls(Root); + [_] -> + [] + end, + + ResultFun = + fun(Location) -> + Info = [Location], + Msg = ?MS_TRANSFORM_INCLUDED_MSG, + elvis_result:new(item, Msg, Info, Location) + end, + + lists:map(ResultFun, ResFunctions). + +get_fun_2_ms_calls(Root) -> + IsFun2MsFunction = + fun(Node) -> + case ktn_code:type(Node) == call of + true -> + {ets, fun2ms} == get_fun_and_mod_from_call(Node); + false -> + false + end + end, + + Functions = elvis_code:find(IsFun2MsFunction, Root), + ProcessResult = fun(Node) -> ktn_code:attr(location, Node) end, + + lists:map(ProcessResult, Functions). + +get_fun_and_mod_from_call(Node) -> + Fun = ktn_code:node_attr(function, Node), + Fun2 = ktn_code:node_attr(function, Fun), + Module = ktn_code:node_attr(module, Fun), + + {ktn_code:attr(value, Module), ktn_code:attr(value, Fun2)}. + -spec no_throw(elvis_config:config(), elvis_file:file(), empty_rule_config()) -> [elvis_result:item()]. no_throw(Config, Target, RuleConfig) -> diff --git a/test/examples/fail_ms_transform.erl b/test/examples/fail_ms_transform.erl deleted file mode 100644 index 4e20699..0000000 --- a/test/examples/fail_ms_transform.erl +++ /dev/null @@ -1,5 +0,0 @@ --module(fail_ms_transform). - --export([test/0]). - -test() -> ets:fun2ms(fun() -> ok end). diff --git a/test/examples/fail_ms_transform_included.erl b/test/examples/fail_ms_transform_included.erl new file mode 100644 index 0000000..d08b5a2 --- /dev/null +++ b/test/examples/fail_ms_transform_included.erl @@ -0,0 +1,5 @@ +-module(fail_ms_transform_included). + +-export([test/0]). + +test() -> ets:fun2ms(fun(_) -> ok end). diff --git a/test/examples/pass_ms_transform.erl b/test/examples/pass_ms_transform.erl deleted file mode 100644 index c6dfe6e..0000000 --- a/test/examples/pass_ms_transform.erl +++ /dev/null @@ -1,5 +0,0 @@ --module(pass_ms_transform). - --export([test/0]). - -test() -> ets:fun2ms(fun() -> ok end). diff --git a/test/examples/pass_ms_transform_included.erl b/test/examples/pass_ms_transform_included.erl new file mode 100644 index 0000000..44597d0 --- /dev/null +++ b/test/examples/pass_ms_transform_included.erl @@ -0,0 +1,7 @@ +-module(pass_ms_transform_included). + +-include_lib("stdlib/include/ms_transform.hrl"). + +-export([test/0]). + +test() -> ets:fun2ms(fun(_) -> ok end). diff --git a/test/style_SUITE.erl b/test/style_SUITE.erl index a403a6d..5232a48 100644 --- a/test/style_SUITE.erl +++ b/test/style_SUITE.erl @@ -27,7 +27,7 @@ verify_export_used_types/1, verify_consistent_variable_casing/1, verify_no_match_in_condition/1, verify_param_pattern_matching/1, verify_private_data_types/1, verify_unquoted_atoms/1, verify_no_init_lists/1, - verify_ms_transform/1]). + verify_ms_transform_included/1]). %% -elvis attribute -export([verify_elvis_attr_atom_naming_convention/1, verify_elvis_attr_numeric_format/1, verify_elvis_attr_dont_repeat_yourself/1, verify_elvis_attr_function_naming_convention/1, @@ -1398,15 +1398,16 @@ verify_unquoted_atoms(Config) -> [_, _] = elvis_core_apply_rule(Config, elvis_text_style, prefer_unquoted_atoms, #{}, FailPath). --spec verify_ms_transform(config()) -> any(). -verify_ms_transform(Config) -> - PassPath = "pass_verify_ms_transform." ++ "erl", - [] = - elvis_core_apply_rule(Config, elvis_text_style, prefer_unquoted_atoms, #{}, PassPath), +-spec verify_ms_transform_included(config()) -> any(). +verify_ms_transform_included(Config) -> + Ext = proplists:get_value(test_file_ext, Config, "erl"), - FailPath = "fail_verify_ms_transform." ++ "erl", - [_, _] = - elvis_core_apply_rule(Config, elvis_text_style, prefer_unquoted_atoms, #{}, FailPath). + PassPath = "pass_ms_transform_included." ++ Ext, + [] = elvis_core_apply_rule(Config, elvis_style, ms_transform_included, #{}, PassPath), + + FailPath = "fail_ms_transform_included." ++ Ext, + [_] = elvis_core_apply_rule(Config, elvis_style, ms_transform_included, #{}, FailPath), + ok. -spec verify_atom_naming_convention(config()) -> any(). verify_atom_naming_convention(Config) ->