diff --git a/lib/archethic/contracts/interpreter.ex b/lib/archethic/contracts/interpreter.ex index 6e6263676..efe2d36fe 100644 --- a/lib/archethic/contracts/interpreter.ex +++ b/lib/archethic/contracts/interpreter.ex @@ -168,6 +168,26 @@ defmodule Archethic.Contracts.Interpreter do do_format_error_reason(reason, module, metadata) end + def format_error_reason( + {{:., metadata, [{:__aliases__, _, [atom: module_name]}, {:atom, function_name}]}, _, + args}, + reason + ) do + # this cover the following case: + # + # code: List.empty?(12) + # ast:{{:., [line: 4], [{:__aliases__, [line: 4], [atom: "List"]}, {:atom, "empty?"}]}, [line: 4], '\f'} + # + # macro.to_string would return this: + # "{:atom, \"List\"} . :atom => \"empty?\"(12)" + # + # this code return this: + # List.empty?(12) + args_str = Enum.map_join(args, ", ", &Macro.to_string/1) + + do_format_error_reason(reason, "#{module_name}.#{function_name}(#{args_str})", metadata) + end + def format_error_reason(ast_node = {_, metadata, _}, reason) do node_msg = try do diff --git a/lib/archethic/contracts/interpreter/action_interpreter.ex b/lib/archethic/contracts/interpreter/action_interpreter.ex index 3bbfc6245..e9be6bb8e 100644 --- a/lib/archethic/contracts/interpreter/action_interpreter.ex +++ b/lib/archethic/contracts/interpreter/action_interpreter.ex @@ -186,20 +186,20 @@ defmodule Archethic.Contracts.Interpreter.ActionInterpreter do # check function exists unless Library.function_exists?(absolute_module_atom, function_name) do - throw({:error, node, "unknown function: Contract.#{function_name}"}) + throw({:error, node, "unknown function"}) end # check function is available with given arity # (we add 1 to arity because we add the contract as 1st argument implicitely) unless Library.function_exists?(absolute_module_atom, function_name, length(args) + 1) do - throw({:error, node, "invalid arity for function Contract.#{function_name}"}) + throw({:error, node, "invalid function arity"}) end function_atom = String.to_existing_atom(function_name) # check the type of the args unless absolute_module_atom.check_types(function_atom, args) do - throw({:error, node, "invalid arguments for function Contract.#{function_name}"}) + throw({:error, node, "invalid function arguments"}) end new_node = diff --git a/lib/archethic/contracts/interpreter/common_interpreter.ex b/lib/archethic/contracts/interpreter/common_interpreter.ex index 014ad1eb7..bc0184c11 100644 --- a/lib/archethic/contracts/interpreter/common_interpreter.ex +++ b/lib/archethic/contracts/interpreter/common_interpreter.ex @@ -295,12 +295,12 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do # check function exists unless Library.function_exists?(absolute_module_atom, function_name) do - throw({:error, node, "unknown function: #{module_name}.#{function_name}"}) + throw({:error, node, "unknown function"}) end # check function is available with given arity unless Library.function_exists?(absolute_module_atom, function_name, length(args)) do - throw({:error, node, "invalid arity for function #{module_name}.#{function_name}"}) + throw({:error, node, "invalid function arity"}) end module_atom = String.to_existing_atom(module_name) @@ -308,7 +308,7 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do # check the type of the args unless absolute_module_atom.check_types(function_atom, args) do - throw({:error, node, "invalid arguments for function #{module_name}.#{function_name}"}) + throw({:error, node, "invalid function arguments"}) end meta_with_alias = Keyword.put(meta, :alias, absolute_module_atom) diff --git a/test/archethic/contracts/interpreter_test.exs b/test/archethic/contracts/interpreter_test.exs index 3c138c6fb..6a08ca9a6 100644 --- a/test/archethic/contracts/interpreter_test.exs +++ b/test/archethic/contracts/interpreter_test.exs @@ -74,6 +74,42 @@ defmodule Archethic.Contracts.InterpreterTest do """ |> Interpreter.parse() end + + test "should return an human readable error if lib fn is called with bad arg" do + assert {:error, "invalid function arguments - List.empty?(12) - L4"} = + """ + @version 1 + condition transaction: [] + actions triggered_by: transaction do + x = List.empty?(12) + end + """ + |> Interpreter.parse() + end + + test "should return an human readable error if lib fn is called with bad arity" do + assert {:error, "invalid function arity - List.empty?([1], \"foobar\") - L4"} = + """ + @version 1 + condition transaction: [] + actions triggered_by: transaction do + x = List.empty?([1], "foobar") + end + """ + |> Interpreter.parse() + end + + test "should return an human readable error if lib fn does not exists" do + assert {:error, "unknown function - List.non_existing([1, 2, 3]) - L4"} = + """ + @version 1 + condition transaction: [] + actions triggered_by: transaction do + x = List.non_existing([1,2,3]) + end + """ + |> Interpreter.parse() + end end describe "parse code v0" do