msz / hammox

🏝 automated contract testing via type checking for Elixir functions and mocks

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

FunctionClauseError raised on return type annotation

nicholasjhenry opened this issue · comments

For example, the following type definition works:

  @type greeting :: String.t()
  @callback perform(Decimal.t()) :: greeting

but the following raises a FunctionCaluseError exception:

  @callback perform(Decimal.t()) :: greeting :: String.t()

Stacktrace:

     ** (FunctionClauseError) no function clause matching in Hammox.TypeEngine.match_type/2

     The following arguments were given to Hammox.TypeEngine.match_type/2:

         # 1
         "hello"

         # 2
         {:ann_type, 7, [{:var, 7, :greeting}, {:remote_type, 7, [{:atom, 0, String}, {:atom, 0, :t}, []]}]}

     Attempted function clauses (showing 10 out of 91):

         def match_type(value, {:type, _, :union, union_types} = union) when is_list(union_types)
         def match_type(_value, {:type, _, :any, []})
         def match_type(value, {:type, _, :none, []} = type)
         def match_type(value, {:type, _, :atom, []}) when is_atom(value)
         def match_type(value, {:type, _, :atom, []} = type)
         def match_type(value, {:type, _, :map, :any}) when is_map(value)
         def match_type(value, {:type, _, :pid, []}) when is_pid(value)
         def match_type(value, {:type, _, :pid, []} = type)
         def match_type(value, {:type, _, :port, []}) when is_port(value)
         def match_type(value, {:type, _, :port, []} = type)

     code: HammoxPlayground.Mock.perform(Decimal.new("0.992"))
     stacktrace:
       (hammox) lib/hammox/type_engine.ex:6: Hammox.TypeEngine.match_type/2
       (hammox) lib/hammox.ex:312: Hammox.match_return_value/2
       (hammox) lib/hammox.ex:280: Hammox.match_call/3
       (hammox) lib/hammox.ex:264: anonymous fn/4 in Hammox.check_call/3
       (elixir) lib/enum.ex:3325: Enumerable.List.reduce/3
       (elixir) lib/enum.ex:1998: Enum.reduce_while/3
       (hammox) lib/hammox.ex:263: Hammox.check_call/3
       (hammox) lib/hammox.ex:256: Hammox.protected_code/3
       test/hammox_playground_test.exs:13: (test)

To clarify, this appears to be only an issue for inline definitions for return types.

Wow, didn't even know this was valid syntax!

I believe the snippets you posted are not equivalent - @type defines a named type accessible throughout the module and from outside, while the "inline syntax" seems to be annotation just like for arguments. Example: arg_name in foo(arg_name :: String.t()) :: String.t().

Updated the issue title accordingly.