From 057093a91e301ace670588f00d43d3cc5358ea0f Mon Sep 17 00:00:00 2001 From: Bastien CHAMAGNE Date: Fri, 21 Apr 2023 11:23:00 +0200 Subject: [PATCH 1/2] Better error printing --- lib/archethic/contracts/interpreter.ex | 20 ++++++++++ test/archethic/contracts/interpreter_test.exs | 37 +++++++++++++++++++ 2 files changed, 57 insertions(+) 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/test/archethic/contracts/interpreter_test.exs b/test/archethic/contracts/interpreter_test.exs index 3c138c6fb..6ccf18d5a 100644 --- a/test/archethic/contracts/interpreter_test.exs +++ b/test/archethic/contracts/interpreter_test.exs @@ -74,6 +74,43 @@ 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 arguments for function List.empty? - 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 arity for function List.empty? - 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 - 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 From b1ed10175acf050506aa34feb8ac6316d5bb76c9 Mon Sep 17 00:00:00 2001 From: Bastien CHAMAGNE Date: Mon, 24 Apr 2023 17:44:39 +0200 Subject: [PATCH 2/2] avoid repetition in the error printing --- lib/archethic/contracts/interpreter/action_interpreter.ex | 6 +++--- lib/archethic/contracts/interpreter/common_interpreter.ex | 6 +++--- test/archethic/contracts/interpreter_test.exs | 7 +++---- 3 files changed, 9 insertions(+), 10 deletions(-) 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 6ccf18d5a..6a08ca9a6 100644 --- a/test/archethic/contracts/interpreter_test.exs +++ b/test/archethic/contracts/interpreter_test.exs @@ -76,7 +76,7 @@ defmodule Archethic.Contracts.InterpreterTest do end test "should return an human readable error if lib fn is called with bad arg" do - assert {:error, "invalid arguments for function List.empty? - List.empty?(12) - L4"} = + assert {:error, "invalid function arguments - List.empty?(12) - L4"} = """ @version 1 condition transaction: [] @@ -88,8 +88,7 @@ defmodule Archethic.Contracts.InterpreterTest do end test "should return an human readable error if lib fn is called with bad arity" do - assert {:error, - "invalid arity for function List.empty? - List.empty?([1], \"foobar\") - L4"} = + assert {:error, "invalid function arity - List.empty?([1], \"foobar\") - L4"} = """ @version 1 condition transaction: [] @@ -101,7 +100,7 @@ defmodule Archethic.Contracts.InterpreterTest do end test "should return an human readable error if lib fn does not exists" do - assert {:error, "unknown function: List.non_existing - List.non_existing([1, 2, 3]) - L4"} = + assert {:error, "unknown function - List.non_existing([1, 2, 3]) - L4"} = """ @version 1 condition transaction: []