Merge pull request #142 from aeternity/more_structured_errors
More structured errors - also in aeso_compiler
This commit is contained in:
commit
23534640c1
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"5e16b85"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"a533fd5"}}}
|
||||||
, {getopt, "1.0.1"}
|
, {getopt, "1.0.1"}
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{"1.1.0",
|
{"1.1.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"5e16b85ae2385bdc010f319e2f2717c09cac3621"}},
|
{ref,"a533fd5fcb25d369caeef4dac95d79941c51620f"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_code_errors).
|
-module(aeso_code_errors).
|
||||||
|
|
||||||
-export([format/1]).
|
-export([format/1, pos/1]).
|
||||||
|
|
||||||
format({last_declaration_must_be_contract, Decl = {namespace, _, {con, _, C}, _}}) ->
|
format({last_declaration_must_be_contract, Decl = {namespace, _, {con, _, C}, _}}) ->
|
||||||
Msg = io_lib:format("Expected a contract as the last declaration instead of the namespace '~s'\n",
|
Msg = io_lib:format("Expected a contract as the last declaration instead of the namespace '~s'\n",
|
||||||
|
@ -65,18 +65,19 @@ version() ->
|
|||||||
{ok, list_to_binary(VsnString)}
|
{ok, list_to_binary(VsnString)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec file(string()) -> {ok, map()} | {error, binary()}.
|
-spec file(string()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||||
file(Filename) ->
|
file(Filename) ->
|
||||||
file(Filename, []).
|
file(Filename, []).
|
||||||
|
|
||||||
-spec file(string(), options()) -> {ok, map()} | {error, binary()}.
|
-spec file(string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||||
file(File, Options0) ->
|
file(File, Options0) ->
|
||||||
Options = add_include_path(File, Options0),
|
Options = add_include_path(File, Options0),
|
||||||
case read_contract(File) of
|
case read_contract(File) of
|
||||||
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]);
|
{ok, Bin} -> from_string(Bin, [{src_file, File} | Options]);
|
||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
ErrorString = [File,": ",file:format_error(Error)],
|
Msg = lists:flatten([File,": ",file:format_error(Error)]),
|
||||||
{error, join_errors("File errors", [ErrorString], fun(E) -> E end)}
|
Pos = aeso_errors:pos(0, 0),
|
||||||
|
{error, [aeso_errors:new(file_error, Pos, Msg)]}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
add_include_path(File, Options) ->
|
add_include_path(File, Options) ->
|
||||||
@ -88,7 +89,7 @@ add_include_path(File, Options) ->
|
|||||||
[{include, {file_system, [Cwd, Dir]}} | Options]
|
[{include, {file_system, [Cwd, Dir]}} | Options]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, binary()}.
|
-spec from_string(binary() | string(), options()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||||
from_string(Contract, Options) ->
|
from_string(Contract, Options) ->
|
||||||
from_string(proplists:get_value(backend, Options, aevm), Contract, Options).
|
from_string(proplists:get_value(backend, Options, aevm), Contract, Options).
|
||||||
|
|
||||||
@ -152,10 +153,6 @@ string_to_code(ContractString, Options) ->
|
|||||||
type_env => TypeEnv}
|
type_env => TypeEnv}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
join_errors(Prefix, Errors, Pfun) ->
|
|
||||||
Ess = [ Pfun(E) || E <- Errors ],
|
|
||||||
list_to_binary(string:join([Prefix|Ess], "\n")).
|
|
||||||
|
|
||||||
-define(CALL_NAME, "__call").
|
-define(CALL_NAME, "__call").
|
||||||
-define(DECODE_NAME, "__decode").
|
-define(DECODE_NAME, "__decode").
|
||||||
|
|
||||||
@ -167,7 +164,7 @@ join_errors(Prefix, Errors, Pfun) ->
|
|||||||
%% a special return type (typerep, T)
|
%% a special return type (typerep, T)
|
||||||
-spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type}, [term()]}
|
-spec check_call(string(), string(), [string()], options()) -> {ok, string(), {[Type], Type}, [term()]}
|
||||||
| {ok, string(), [term()]}
|
| {ok, string(), [term()]}
|
||||||
| {error, term()}
|
| {error, [aeso_errors:error()]}
|
||||||
when Type :: term().
|
when Type :: term().
|
||||||
check_call(Source, "init" = FunName, Args, Options) ->
|
check_call(Source, "init" = FunName, Args, Options) ->
|
||||||
case check_call1(Source, FunName, Args, Options) of
|
case check_call1(Source, FunName, Args, Options) of
|
||||||
@ -222,10 +219,7 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
|||||||
{ok, FunName, CallArgs}
|
{ok, FunName, CallArgs}
|
||||||
end
|
end
|
||||||
catch
|
catch
|
||||||
throw:{error, Errors} -> {error, Errors};
|
throw:{error, Errors} -> {error, Errors}
|
||||||
error:{badmatch, {error, missing_call_function}} ->
|
|
||||||
{error, join_errors("Type errors", ["missing __call function"],
|
|
||||||
fun (E) -> E end)}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
arguments_of_body(CallName, _FunName, Fcode) ->
|
arguments_of_body(CallName, _FunName, Fcode) ->
|
||||||
@ -273,24 +267,31 @@ last_contract_indent(Decls) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
-spec to_sophia_value(string(), string(), ok | error | revert, aeb_aevm_data:data()) ->
|
-spec to_sophia_value(string(), string(), ok | error | revert, aeb_aevm_data:data()) ->
|
||||||
{ok, aeso_syntax:expr()} | {error, term()}.
|
{ok, aeso_syntax:expr()} | {error, [aeso_errors:error()]}.
|
||||||
to_sophia_value(ContractString, Fun, ResType, Data) ->
|
to_sophia_value(ContractString, Fun, ResType, Data) ->
|
||||||
to_sophia_value(ContractString, Fun, ResType, Data, [{backend, aevm}]).
|
to_sophia_value(ContractString, Fun, ResType, Data, [{backend, aevm}]).
|
||||||
|
|
||||||
-spec to_sophia_value(string(), string(), ok | error | revert, binary(), options()) ->
|
-spec to_sophia_value(string(), string(), ok | error | revert, binary(), options()) ->
|
||||||
{ok, aeso_syntax:expr()} | {error, term()}.
|
{ok, aeso_syntax:expr()} | {error, [aeso_errors:error()]}.
|
||||||
to_sophia_value(_, _, error, Err, _Options) ->
|
to_sophia_value(_, _, error, Err, _Options) ->
|
||||||
{ok, {app, [], {id, [], "error"}, [{string, [], Err}]}};
|
{ok, {app, [], {id, [], "error"}, [{string, [], Err}]}};
|
||||||
to_sophia_value(_, _, revert, Data, Options) ->
|
to_sophia_value(_, _, revert, Data, Options) ->
|
||||||
case proplists:get_value(backend, Options, aevm) of
|
case proplists:get_value(backend, Options, aevm) of
|
||||||
aevm ->
|
aevm ->
|
||||||
case aeb_heap:from_binary(string, Data) of
|
case aeb_heap:from_binary(string, Data) of
|
||||||
{ok, Err} -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}};
|
{ok, Err} ->
|
||||||
{error, _} = Err -> Err
|
{ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}};
|
||||||
|
{error, _} ->
|
||||||
|
Msg = "Could not interpret the revert message\n",
|
||||||
|
{error, [aeso_errors:new(data_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
end;
|
end;
|
||||||
fate ->
|
fate ->
|
||||||
Err = aeb_fate_encoding:deserialize(Data),
|
try aeb_fate_encoding:deserialize(Data) of
|
||||||
{ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}}
|
Err -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}}
|
||||||
|
catch _:_ ->
|
||||||
|
Msg = "Could not deserialize the revert message\n",
|
||||||
|
{error, [aeso_errors:new(data_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
|
end
|
||||||
end;
|
end;
|
||||||
to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
@ -310,43 +311,41 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
|||||||
{ok, aeso_vm_decode:from_aevm(VmType, Type, VmValue)}
|
{ok, aeso_vm_decode:from_aevm(VmType, Type, VmValue)}
|
||||||
catch throw:cannot_translate_to_sophia ->
|
catch throw:cannot_translate_to_sophia ->
|
||||||
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
||||||
{error, join_errors("Translation error", [lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
|
Msg = io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
|
||||||
[Data, VmType, Type0Str]))],
|
[Data, VmType, Type0Str]),
|
||||||
fun (E) -> E end)}
|
{error, [aeso_errors:new(type_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
end;
|
end;
|
||||||
{error, _Err} ->
|
{error, _Err} ->
|
||||||
{error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))],
|
Msg = io_lib:format("Failed to decode binary as type ~p\n", [VmType]),
|
||||||
fun(E) -> E end)}
|
{error, [aeso_errors:new(code_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
end;
|
end;
|
||||||
fate ->
|
fate ->
|
||||||
try
|
try
|
||||||
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
||||||
catch throw:cannot_translate_to_sophia ->
|
catch throw:cannot_translate_to_sophia ->
|
||||||
{error, join_errors("Translation error",
|
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
||||||
[lists:flatten(io_lib:format("Cannot translate fate value ~p\n of Sophia type ~s\n",
|
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n",
|
||||||
[aeb_fate_encoding:deserialize(Data), Type]))],
|
[aeb_fate_encoding:deserialize(Data), Type1]),
|
||||||
fun (E) -> E end)};
|
{error, [aeso_errors:new(type_error, aeso_errors:pos(0, 0), Msg)]};
|
||||||
_:R ->
|
_:_ ->
|
||||||
{error, iolist_to_binary(io_lib:format("Decode error ~p: ~p\n", [R, erlang:get_stacktrace()]))}
|
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
||||||
|
Msg = io_lib:format("Failed to decode binary as type ~s\n", [Type1]),
|
||||||
|
{error, [aeso_errors:new(code_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
catch
|
catch
|
||||||
throw:{error, Errors} -> {error, Errors};
|
throw:{error, Errors} -> {error, Errors}
|
||||||
error:{badmatch, {error, missing_function}} ->
|
|
||||||
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
|
|
||||||
fun (E) -> E end)}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec create_calldata(string(), string(), [string()]) ->
|
-spec create_calldata(string(), string(), [string()]) ->
|
||||||
{ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()}
|
{ok, binary(), aeb_aevm_data:type(), aeb_aevm_data:type()}
|
||||||
| {error, term()}.
|
| {error, [aeso_errors:error()]}.
|
||||||
create_calldata(Code, Fun, Args) ->
|
create_calldata(Code, Fun, Args) ->
|
||||||
create_calldata(Code, Fun, Args, [{backend, aevm}]).
|
create_calldata(Code, Fun, Args, [{backend, aevm}]).
|
||||||
|
|
||||||
-spec create_calldata(string(), string(), [string()], [{atom(), any()}]) ->
|
-spec create_calldata(string(), string(), [string()], [{atom(), any()}]) ->
|
||||||
{ok, binary()}
|
{ok, binary()} | {error, [aeso_errors:error()]}.
|
||||||
| {error, term()}.
|
|
||||||
create_calldata(Code, Fun, Args, Options0) ->
|
create_calldata(Code, Fun, Args, Options0) ->
|
||||||
Options = [no_code | Options0],
|
Options = [no_code | Options0],
|
||||||
case proplists:get_value(backend, Options, aevm) of
|
case proplists:get_value(backend, Options, aevm) of
|
||||||
@ -366,7 +365,7 @@ create_calldata(Code, Fun, Args, Options0) ->
|
|||||||
|
|
||||||
-spec decode_calldata(string(), string(), binary()) ->
|
-spec decode_calldata(string(), string(), binary()) ->
|
||||||
{ok, [aeso_syntax:type()], [aeso_syntax:expr()]}
|
{ok, [aeso_syntax:type()], [aeso_syntax:expr()]}
|
||||||
| {error, term()}.
|
| {error, [aeso_errors:error()]}.
|
||||||
decode_calldata(ContractString, FunName, Calldata) ->
|
decode_calldata(ContractString, FunName, Calldata) ->
|
||||||
decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]).
|
decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]).
|
||||||
|
|
||||||
@ -393,15 +392,14 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
|||||||
%% Values are Sophia expressions in AST format
|
%% Values are Sophia expressions in AST format
|
||||||
{ok, ArgTypes, Values}
|
{ok, ArgTypes, Values}
|
||||||
catch throw:cannot_translate_to_sophia ->
|
catch throw:cannot_translate_to_sophia ->
|
||||||
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
||||||
{error, join_errors("Translation error",
|
Msg = io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
|
||||||
[lists:flatten(io_lib:format("Cannot translate VM value ~p\n of type ~p\n to Sophia type ~s\n",
|
[VmValue, VmType, Type0Str]),
|
||||||
[VmValue, VmType, Type0Str]))],
|
{error, [aeso_errors:new(type_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
fun (E) -> E end)}
|
|
||||||
end;
|
end;
|
||||||
{error, _Err} ->
|
{error, _Err} ->
|
||||||
{error, join_errors("Decode errors", [lists:flatten(io_lib:format("Failed to decode binary at type ~p", [VmType]))],
|
Msg = io_lib:format("Failed to decode calldata as type ~p\n", [VmType]),
|
||||||
fun(E) -> E end)}
|
{error, [aeso_errors:new(code_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
end;
|
end;
|
||||||
fate ->
|
fate ->
|
||||||
case aeb_fate_abi:decode_calldata(FunName, Calldata) of
|
case aeb_fate_abi:decode_calldata(FunName, Calldata) of
|
||||||
@ -413,30 +411,31 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
|
|||||||
{ok, ArgTypes, AstArgs}
|
{ok, ArgTypes, AstArgs}
|
||||||
catch throw:cannot_translate_to_sophia ->
|
catch throw:cannot_translate_to_sophia ->
|
||||||
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
Type0Str = prettypr:format(aeso_pretty:type(Type0)),
|
||||||
{error, join_errors("Translation error",
|
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s\n",
|
||||||
[lists:flatten(io_lib:format("Cannot translate fate value ~p\n of Sophia type ~s\n",
|
[FateArgs, Type0Str]),
|
||||||
[FateArgs, Type0Str]))],
|
{error, [aeso_errors:new(type_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
fun (E) -> E end)}
|
|
||||||
end;
|
end;
|
||||||
{error, _} ->
|
{error, _} ->
|
||||||
{error, join_errors("Decode errors", ["Failed to decode binary"],
|
Msg = io_lib:format("Failed to decode calldata binary\n", []),
|
||||||
fun(E) -> E end)}
|
{error, [aeso_errors:new(code_error, aeso_errors:pos(0, 0), Msg)]}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
catch
|
catch
|
||||||
throw:{error, Errors} ->
|
throw:{error, Errors} -> {error, Errors}
|
||||||
{error, Errors};
|
|
||||||
error:{badmatch, {error, missing_function}} ->
|
|
||||||
{error, join_errors("Type errors", ["no function: '" ++ FunName ++ "'"],
|
|
||||||
fun (E) -> E end)}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_arg_icode(Funs) ->
|
get_arg_icode(Funs) ->
|
||||||
case [ Args || {[_, ?CALL_NAME], _, _, {funcall, _, Args}, _} <- Funs ] of
|
case [ Args || {[_, ?CALL_NAME], _, _, {funcall, _, Args}, _} <- Funs ] of
|
||||||
[Args] -> Args;
|
[Args] -> Args;
|
||||||
[] -> error({missing_call_function, Funs})
|
[] -> error_missing_call_function()
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-dialyzer({nowarn_function, error_missing_call_function/0}).
|
||||||
|
error_missing_call_function() ->
|
||||||
|
Msg = "Internal error: missing '__call'-function",
|
||||||
|
Pos = aeso_errors:pos(0, 0),
|
||||||
|
aeso_errors:throw(aeso_errors:new(internal_error, Pos, Msg)).
|
||||||
|
|
||||||
get_call_type([{contract, _, _, Defs}]) ->
|
get_call_type([{contract, _, _, Defs}]) ->
|
||||||
case [ {lists:last(QFunName), FunType}
|
case [ {lists:last(QFunName), FunType}
|
||||||
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
|
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
|
||||||
@ -444,13 +443,14 @@ get_call_type([{contract, _, _, Defs}]) ->
|
|||||||
{app, _,
|
{app, _,
|
||||||
{typed, _, {qid, _, QFunName}, FunType}, _}, _}} <- Defs ] of
|
{typed, _, {qid, _, QFunName}, FunType}, _}, _}} <- Defs ] of
|
||||||
[Call] -> {ok, Call};
|
[Call] -> {ok, Call};
|
||||||
[] -> {error, missing_call_function}
|
[] -> error_missing_call_function()
|
||||||
end;
|
end;
|
||||||
get_call_type([_ | Contracts]) ->
|
get_call_type([_ | Contracts]) ->
|
||||||
%% The __call should be in the final contract
|
%% The __call should be in the final contract
|
||||||
get_call_type(Contracts).
|
get_call_type(Contracts).
|
||||||
|
|
||||||
get_decode_type(FunName, [{contract, _, _, Defs}]) ->
|
-dialyzer({nowarn_function, get_decode_type/2}).
|
||||||
|
get_decode_type(FunName, [{contract, Ann, _, Defs}]) ->
|
||||||
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
||||||
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
||||||
(_) -> [] end,
|
(_) -> [] end,
|
||||||
@ -459,7 +459,10 @@ get_decode_type(FunName, [{contract, _, _, Defs}]) ->
|
|||||||
[] ->
|
[] ->
|
||||||
case FunName of
|
case FunName of
|
||||||
"init" -> {ok, [], {tuple_t, [], []}};
|
"init" -> {ok, [], {tuple_t, [], []}};
|
||||||
_ -> {error, missing_function}
|
_ ->
|
||||||
|
Msg = io_lib:format("Function '~s' is missing in contract\n", [FunName]),
|
||||||
|
Pos = aeso_code_errors:pos(Ann),
|
||||||
|
aeso_errors:throw(aeso_errors:new(code_error, Pos, Msg))
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
get_decode_type(FunName, [_ | Contracts]) ->
|
get_decode_type(FunName, [_ | Contracts]) ->
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
}).
|
}).
|
||||||
|
|
||||||
-type pos() :: #pos{}.
|
-type pos() :: #pos{}.
|
||||||
-type error_type() :: type_error | parse_error | code_error | internal_error.
|
-type error_type() :: type_error | parse_error | code_error
|
||||||
|
| file_error | data_error | internal_error.
|
||||||
|
|
||||||
-record(err, { pos = #pos{} :: pos()
|
-record(err, { pos = #pos{} :: pos()
|
||||||
, type :: error_type()
|
, type :: error_type()
|
||||||
@ -71,9 +72,11 @@ str_pos(#pos{file = F, line = L, col = C}) ->
|
|||||||
type(#err{ type = Type }) -> Type.
|
type(#err{ type = Type }) -> Type.
|
||||||
|
|
||||||
pp(#err{ pos = Pos } = Err) ->
|
pp(#err{ pos = Pos } = Err) ->
|
||||||
lists:flatten(io_lib:format("~s\n~s", [pp_pos(Pos), msg(Err)])).
|
lists:flatten(io_lib:format("~s~s", [pp_pos(Pos), msg(Err)])).
|
||||||
|
|
||||||
|
pp_pos(#pos{file = no_file, line = 0, col = 0}) ->
|
||||||
|
"";
|
||||||
pp_pos(#pos{file = no_file, line = L, col = C}) ->
|
pp_pos(#pos{file = no_file, line = L, col = C}) ->
|
||||||
io_lib:format("At line ~p, col ~p:", [L, C]);
|
io_lib:format("At line ~p, col ~p:\n", [L, C]);
|
||||||
pp_pos(#pos{file = F, line = L, col = C}) ->
|
pp_pos(#pos{file = F, line = L, col = C}) ->
|
||||||
io_lib:format("In '~s' at line ~p, col ~p:", [F, L, C]).
|
io_lib:format("In '~s' at line ~p, col ~p:\n", [F, L, C]).
|
||||||
|
@ -72,6 +72,58 @@ encode_decode_sophia_test() ->
|
|||||||
ok = Check("r", "{x = (\"foo\", 0), y = Red}"),
|
ok = Check("r", "{x = (\"foo\", 0), y = Red}"),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
to_sophia_value_neg_test() ->
|
||||||
|
Code = [ "contract Foo =\n"
|
||||||
|
" entrypoint x(y : int) : string = \"hello\"\n" ],
|
||||||
|
|
||||||
|
{error, [Err1]} = aeso_compiler:to_sophia_value(Code, "x", ok, encode(12)),
|
||||||
|
?assertEqual("Failed to decode binary as type string\n", aeso_errors:pp(Err1)),
|
||||||
|
{error, [Err2]} = aeso_compiler:to_sophia_value(Code, "x", ok, encode(12), [{backend, fate}]),
|
||||||
|
?assertEqual("Failed to decode binary as type string\n", aeso_errors:pp(Err2)),
|
||||||
|
|
||||||
|
{error, [Err3]} = aeso_compiler:to_sophia_value(Code, "x", revert, encode(12)),
|
||||||
|
?assertEqual("Could not interpret the revert message\n", aeso_errors:pp(Err3)),
|
||||||
|
{error, [Err4]} = aeso_compiler:to_sophia_value(Code, "x", revert, encode(12), [{backend, fate}]),
|
||||||
|
?assertEqual("Could not deserialize the revert message\n", aeso_errors:pp(Err4)),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
encode_calldata_neg_test() ->
|
||||||
|
Code = [ "contract Foo =\n"
|
||||||
|
" entrypoint x(y : int) : string = \"hello\"\n" ],
|
||||||
|
|
||||||
|
ExpErr1 = "At line 5, col 34:\nCannot unify int\n and bool\n"
|
||||||
|
"when checking the application at line 5, column 34 of\n"
|
||||||
|
" x : (int) => string\nto arguments\n true : bool\n",
|
||||||
|
{error, [Err1]} = aeso_compiler:create_calldata(Code, "x", ["true"]),
|
||||||
|
?assertEqual(ExpErr1, aeso_errors:pp(Err1)),
|
||||||
|
{error, [Err2]} = aeso_compiler:create_calldata(Code, "x", ["true"], [{backend, fate}]),
|
||||||
|
?assertEqual(ExpErr1, aeso_errors:pp(Err2)),
|
||||||
|
|
||||||
|
ok.
|
||||||
|
|
||||||
|
decode_calldata_neg_test() ->
|
||||||
|
Code1 = [ "contract Foo =\n"
|
||||||
|
" entrypoint x(y : int) : string = \"hello\"\n" ],
|
||||||
|
Code2 = [ "contract Foo =\n"
|
||||||
|
" entrypoint x(y : string) : int = 42\n" ],
|
||||||
|
|
||||||
|
{ok, CallDataAEVM} = aeso_compiler:create_calldata(Code1, "x", ["42"]),
|
||||||
|
{ok, CallDataFATE} = aeso_compiler:create_calldata(Code1, "x", ["42"], [{backend, fate}]),
|
||||||
|
|
||||||
|
{error, [Err1]} = aeso_compiler:decode_calldata(Code2, "x", CallDataAEVM),
|
||||||
|
?assertEqual("Failed to decode calldata as type {tuple,[string]}\n", aeso_errors:pp(Err1)),
|
||||||
|
{error, [Err2]} = aeso_compiler:decode_calldata(Code2, "x", <<1,2,3>>, [{backend, fate}]),
|
||||||
|
?assertEqual("Failed to decode calldata binary\n", aeso_errors:pp(Err2)),
|
||||||
|
{error, [Err3]} = aeso_compiler:decode_calldata(Code2, "x", CallDataFATE, [{backend, fate}]),
|
||||||
|
?assertEqual("Cannot translate FATE value \"*\"\n of Sophia type (string)\n", aeso_errors:pp(Err3)),
|
||||||
|
|
||||||
|
{error, [Err4]} = aeso_compiler:decode_calldata(Code2, "y", CallDataAEVM),
|
||||||
|
?assertEqual("At line 1, col 1:\nFunction 'y' is missing in contract\n", aeso_errors:pp(Err4)),
|
||||||
|
{error, [Err5]} = aeso_compiler:decode_calldata(Code2, "y", CallDataFATE, [{backend, fate}]),
|
||||||
|
?assertEqual("At line 1, col 1:\nFunction 'y' is missing in contract\n", aeso_errors:pp(Err5)),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
|
||||||
encode_decode_sophia_string(SophiaType, String) ->
|
encode_decode_sophia_string(SophiaType, String) ->
|
||||||
io:format("String ~p~n", [String]),
|
io:format("String ~p~n", [String]),
|
||||||
Code = [ "contract MakeCall =\n"
|
Code = [ "contract MakeCall =\n"
|
||||||
|
@ -32,6 +32,12 @@ simple_compile_test_() ->
|
|||||||
end
|
end
|
||||||
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
|
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
|
||||||
not lists:member(ContractName, not_yet_compilable(Backend))] ++
|
not lists:member(ContractName, not_yet_compilable(Backend))] ++
|
||||||
|
[ {"Test file not found error",
|
||||||
|
fun() ->
|
||||||
|
{error, Errors} = aeso_compiler:file("does_not_exist.aes"),
|
||||||
|
ExpErr = <<"does_not_exist.aes: no such file or directory">>,
|
||||||
|
check_errors([ExpErr], Errors)
|
||||||
|
end} ] ++
|
||||||
[ {"Testing error messages of " ++ ContractName,
|
[ {"Testing error messages of " ++ ContractName,
|
||||||
fun() ->
|
fun() ->
|
||||||
Errors = compile(aevm, ContractName),
|
Errors = compile(aevm, ContractName),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user