Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New elvis_style rule: no_init_lists #367

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

bormilan
Copy link

@bormilan bormilan commented Oct 7, 2024

Description

A brief description of your changes.

Closes #329;.


EDIT: I made a checklist based on @elbrujohalcon 's comment to to make the progress traceable.

  • has init([|]) -> …: Should fail.

  • has init(A = [1,2,3]) -> …: Should fail

  • has init/1 implemented with multiple clauses, all of them expecting a list: Should fail.

  • has init/1 implemented with multiple clauses, one of them not being a list: Should pass, even if other clauses expect a list.

  • has init/1 implemented using a map, but init/2 implemented using a list in one of the arguments: Should pass.

  • has init/0 implemented using a tuple, but also init/0: Should pass.

  • A module that doesn't implement a behaviour and has init([1,2,3]) -> …: Should pass.

@bormilan
Copy link
Author

bormilan commented Oct 7, 2024

Do we want to check that the module implements a gen_* behavior, or should we check every module like now?
Oh, and I want to check if this rule can work on .beam files.

Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs more tests, specifically:

  • A module that implements gen_server or gen_statem and…
    • has init([_|_]) -> …: Should fail.
    • has init(A = [1,2,3]) -> …: Should fail
    • has init/1 implemented with multiple clauses, all of them expecting a list: Should fail.
    • has init/1 implemented with multiple clauses, one of them not being a list: Should pass, even if other clauses expect a list.
    • has init/1 implemented using a map, but init/2 implemented using a list in one of the arguments: Should pass.
    • has init/0 implemented using a tuple, but also init/0: Should pass.
  • A module that doesn't implement a behaviour and has init([1,2,3]) -> …: Should pass.

RULES.md Outdated Show resolved Hide resolved
Comment on lines 3 to 4
This warns you if you use list as a parameter in an init function in a gen_* module.
[Reasoning](https://erlangforums.com/t/args-in-gen-init-1/3169/4?u=elbrujohalcon)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This warns you if you use list as a parameter in an init function in a gen_* module.
[Reasoning](https://erlangforums.com/t/args-in-gen-init-1/3169/4?u=elbrujohalcon)
Do not use a list as the parameter for the `init/1` callback when implementing `gen_*` behaviours. It's semantically clearer to use a tuple or a map. [More info](https://erlangforums.com/t/args-in-gen-init-1/3169/4?u=elbrujohalcon).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's safe to remove ?u=elbrujohalcon from the link.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right I removed and changed the last id from 4 to 5 to point the link to the right comment.

doc_rules/elvis_style/no_init_lists.md Outdated Show resolved Hide resolved
src/elvis_style.erl Outdated Show resolved Hide resolved
start_link(AParam) ->
gen_server:start_link(?MODULE, [AParam], []).

init([_AParam]) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should actually work… It should not fail since it's not implementing any behaviour.
If the file is not implementing a behaviour that requires an init/1 callback, we don't care how init/1 is implemented.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, to start we should just go with a few hard-coded Erlang behaviours; then we can extend as time goes by (they don't come by that often, in any case).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as Brujo's before; this file is irrelevant for the rule since it's not implementing a behaviour (not explicitly, at least).

Copy link
Collaborator

@paulo-ferraz-oliveira paulo-ferraz-oliveira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're on the right path here ❤️ Just a few more tweaks...

@paulo-ferraz-oliveira paulo-ferraz-oliveira changed the title No init lists New elvis_style rule: no_init_lists Oct 8, 2024
@paulo-ferraz-oliveira
Copy link
Collaborator

I tweaked the PR title since this'll go directly into the generated release Changelog.

@bormilan
Copy link
Author

bormilan commented Oct 10, 2024

Thanks for the comments, I will solve all of them if I have some time. ❤️

Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still missing some changes, but I just wanted to add feedback about the recent additions.

Comment on lines 1040 to 1041
no_init_lists(_Config, Target, _RuleConfig) ->
Root = get_root(#{}, Target, #{}),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
no_init_lists(_Config, Target, _RuleConfig) ->
Root = get_root(#{}, Target, #{}),
no_init_lists(Config, Target, RuleConfig) ->
Root = get_root(Config, Target, RuleConfig),

Did you need to use empty maps there for a reason, @bormilan ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right again, I just left it in...


FunListAttributeInfos = lists:map(PairFun, FunctionNodes),

FilterFun = fun({Name, _, C}) -> length(C) > 0 andalso Name =:= init end,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure but… if C is the list of arguments… the it has to be exactly 1.

Suggested change
FilterFun = fun({Name, _, C}) -> length(C) > 0 andalso Name =:= init end,
FilterFun = fun({Name, _, Args}) -> length(Args) =:= 1 andalso Name =:= init end,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right it's a dumb fault.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rule: Do not use lists on start_link/3,4 and init/1
3 participants