Qqwy / elixir-type_check

TypeCheck: Fast and flexible runtime type-checking for your Elixir projects.
MIT License
521 stars 25 forks source link

Unexpected behaviour when defining multiple specs for a function #145

Open lbueso opened 2 years ago

lbueso commented 2 years ago
defmodule A do
  use TypeCheck

  @spec! f(integer()) :: integer()
  @spec! f(boolean()) :: boolean()
  def f(x), do: x
end

With this function definition of f I would expect TypeCheck to allow calling f with integers and booleans, but the current behaviour is:

iex(1)> A.f(1)
** (TypeCheck.TypeError) At lib/examples.ex:6:
    The call to `f/1` failed,
    because parameter no. 1 does not adhere to the spec `boolean()`.
    Rather, its value is: `1`.
    Details:
      The call `f(1)`
      does not adhere to spec `f(boolean()) :: boolean()`. Reason:
        parameter no. 1:
          `1` is not a boolean.
    (examples 0.1.0) lib/type_check/spec.ex:203: A."f (overridable 2)"/1
    (examples 0.1.0) lib/type_check/spec.ex:6: A.f/1
iex(1)> A.f(true)
** (TypeCheck.TypeError) At lib/examples.ex:6:
    The call to `f/1` failed,
    because parameter no. 1 does not adhere to the spec `boolean()`.
    Rather, its value is: `true`.
    Details:
      The call `f(true)`
      does not adhere to spec `f(boolean()) :: boolean()`. Reason:
        parameter no. 1:
          `true` is not an integer.
    (examples 0.1.0) lib/type_check/spec.ex:203: A.f/1
Qqwy commented 2 years ago

Thank you for submitting this bug report!

Defining multiple different specs for the same function is indeed allowed in Elixir/Erlang's own typespecs, but not currently in TypeCheck. We do want to support this in the future, but it requires some work to get the implementation right. Maybe the current limitation was surprising. Do you think we should make it more visible in the documentation maybe?

For the time being, I recommend writing down a single spec in the format

@spec! f(integer()) :: integer() | boolean()
lbueso commented 2 years ago

Making it more visible in the documentation will definetely be very useful while this is not implemented.

Maybe TypeCheck could also throw a compilation warning when it finds more than one spec for a function. This will be very useful for the user, because the current behaviour doesn't allow the user to correctly invoke the function with multiple specs.