Skip to content

Conversation

@Premwoik
Copy link
Collaborator

@Premwoik Premwoik commented Feb 28, 2022

This PR focuses on #36 and introduces a new module that provides checks specific to Elixir. There's an option ex_check that allows to deactivate the ElixirChecker and use only Gradualizer checks.

Steps

  • Introduce the spec check.
  • Add formatters for spec errors.
  • Add tests for ElixirChecker module.
  • Add tests for new formatters.

Spec location

Currently, it can check whether the spec attribute is used correctly. We decided that spec must be placed above function which it concerns, and only one spec per function clause is allowed. The acceptable spec location:

@spec convert(integer()) :: float()
def convert(int) when is_integer(int), do: int / 1
@spec convert(atom()) :: binary()
def convert(atom) when is_atom(atom), do: to_string(atom)

Incorrect spec location:

  • Spec name doesn't match the function below.
    @spec last_two(atom()) :: atom()
    def last_three(:ok) do
      :ok
    end
  • More than one spec above function clause.
    @spec convert(integer()) :: float()
    @spec convert(atom()) :: binary()
    def convert(int) when is_integer(int), do: int / 1
    
    def convert(atom) when is_atom(atom), do: to_string(atom)

Questions

I wonder if I should check if the number of specs for a function is less than or equal to the number of function clauses?

No need for this.

Is it ok to have more the one spec before a function clause, or better to allow only one spec per one clause?

Let's try with one spec per function clause.

How to improve the proposed error messages?

@Premwoik Premwoik marked this pull request as ready for review March 2, 2022 14:07
@Premwoik Premwoik linked an issue Mar 2, 2022 that may be closed by this pull request
@erszcz
Copy link
Contributor

erszcz commented Mar 2, 2022

I wonder if I should check if the number of specs for a function is less than or equal to the number of function clauses?

I don't think so. Consider:

@spec len(list()) :: integer()
def len([]), do: 0
def len([_ | t]), do: 1 + len(t)

We pattern match on the same type multiple times, i.e. we have more function clauses than spec clauses and it's fine.
That's going to be a recurring pattern also with other types when using pattern matching for control flow in function heads.

Is it ok to have more the one spec before a function clause, or better to allow only one spec per one clause?

This is a good question. Let's go with one spec per function clause and see how that works on real world code - either by testing or based on community feedback.

@Premwoik Premwoik marked this pull request as draft March 3, 2022 14:53
@Premwoik Premwoik marked this pull request as ready for review March 4, 2022 10:49
@Premwoik Premwoik requested a review from erszcz March 4, 2022 10:50
@erszcz
Copy link
Contributor

erszcz commented Mar 4, 2022

This is awesome 🚀

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.

Double Specs not taken into account

3 participants