gradient icon indicating copy to clipboard operation
gradient copied to clipboard

no function clause matching in Gradient.ElixirExpr.pp_guards/1

Open alanvardy opened this issue 2 years ago • 3 comments

Hello,

I've been really enjoying using gradient, thank you for the work! I've encountered this error today when running mix gradient

(FunctionClauseError) no function clause matching in Gradient.ElixirExpr.pp_guards/1

    The following arguments were given to Gradient.ElixirExpr.pp_guards/1:

        # 1
        [[{:call, [generated: true, location: 486], {:remote, [generated: true, location: 486], {:atom, [generated: true, location: 486], :erlang}, {:atom, [generated: true, location: 486], :is_atom}}, [{:var, [generated: true, location: 486], :_@2}]}, {:op, [generated: true, location: 486], :"=/=", {:var, [generated: true, location: 486], :_@2}, {:atom, [generated: true, location: 486], nil}}, {:op, [generated: true, location: 486], :"=/=", {:var, [generated: true, location: 486], :_@2}, {:atom, [generated: true, location: 486], true}}, {:op, [generated: true, location: 486], :"=/=", {:var, [generated: true, location: 486], :_@2}, {:atom, [generated: true, location: 486], false}}]]

    Attempted function clauses (showing 2 out of 2):

        def pp_guards([])
        def pp_guards([[guard]])

    Gradient.ElixirExpr.pp_guards/1
    lib/gradient/elixir_expr.ex:310: Gradient.ElixirExpr.pp_case_clause/1
    (elixir 1.14.2) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
    (elixir 1.14.2) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
    lib/gradient/elixir_expr.ex:248: Gradient.ElixirExpr.pp_clauses/2
    lib/gradient/elixir_expr.ex:207: Gradient.ElixirExpr.pp_expr/1
    lib/gradient/elixir_expr.ex:154: Gradient.ElixirExpr.pp_expr/1
    (elixir 1.14.2) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
[[Command exited with 1]]

The error occurs three times within the same module and nowhere else. I believe that the issue stems from my custom guard which is used in three different functions.

Here is the guard definition

  @max_integer 32_767

  defguard pos_int(integer) when integer < @max_integer and integer > 0

And here is an example of it in use

  @doc false
  @spec kills(Match.t(), puuid) :: res(non_neg_integer)
  def kills(match, puuid) do
    case Fetch.player(match, puuid) do
      {:ok, %Player{stats: %PlayerStats{kills: kills}}} when pos_int(kills) ->
        {:ok, kills}

      {:ok, %Player{}} ->
        {:ok, 0}

      {:error, error} ->
        {:error, error}
    end
  end

alanvardy avatar May 15 '23 17:05 alanvardy

Thanks for the report!

From a quick look it seems we're missing a clause for used-defined guards. We'll take a look at this.

erszcz avatar May 16 '23 07:05 erszcz

https://github.com/esl/gradient/issues/145#issuecomment-1322441536 seems to be related.

erszcz avatar May 16 '23 07:05 erszcz

I manage to get a small reproducible case that might be related to this


  @spec indirection(number(), number(), non_neg_integer()) ::
          list({atom(), list(), binary()})
  def indirection(storage, key, order) do
      [:hi, [<<2>>], <<4>>]
  end



  @spec crash() :: number()
  def crash() do
    order = 2
    key = 5
    with {:atomic, [{_, [^order, ^key | 0], value}]} <- indirection(order, key, order) do
      2 + 5
    end
  end

calling mix gradiant gives me

lib/project/storage.ex: The atom on line 289 is expected to have type {atom(), list(), binary()} but it has type :hi
287           list({atom(), list(), binary()})
288   def indirection(storage, key, order) do
289       [:hi, [<<2>>], <<4>>]
290   end
291 

lib/project/storage.ex: ** (FunctionClauseError) no function clause matching in Gradient.ElixirExpr.pp_cons/1    
    
    The following arguments were given to Gradient.ElixirExpr.pp_cons/1:
    
        # 1
        {:integer, 299, 0}
    
    Attempted function clauses (showing 3 out of 3):
    
        defp pp_cons({:cons, _, h, {nil, _}})
        defp pp_cons({:cons, _, h, {:var, _, _} = v})
        defp pp_cons({:cons, _, h, t})
    
    (gradient 0.1.0) Gradient.ElixirExpr.pp_cons/1
    (gradient 0.1.0) lib/gradient/elixir_expr.ex:491: Gradient.ElixirExpr.pp_cons/1
    (gradient 0.1.0) lib/gradient/elixir_expr.ex:68: Gradient.ElixirExpr.pp_expr/1
    (elixir 1.15.5) lib/enum.ex:1693: Enum."-map/2-lists^map/1-1-"/2
    (elixir 1.15.5) lib/enum.ex:1693: Enum."-map/2-lists^map/1-1-"/2
    (gradient 0.1.0) lib/gradient/elixir_expr.ex:158: Gradient.ElixirExpr.pp_expr/1
    (gradient 0.1.0) lib/gradient/elixir_expr.ex:68: Gradient.ElixirExpr.pp_expr/1
    (elixir 1.15.5) lib/enum.ex:1693: Enum."-map/2-lists^map/1-1-"/2

I've noticed that I can fix the crash by changing the code to the following

  @spec crash() :: number()
  def crash() do
    order = 2
    key = 5
    zero = 0
    with {:atomic, [{_, [^order, ^key | ^zero], value}]} <- indirection(order, key, order) do
      2 + 5
    end
  end

the | 0 for some reason is causing issues

mariari avatar Mar 27 '24 02:03 mariari