Merge pull request #190 from aeternity/GH-189-parse-error-crash

Fix parse errors causing crashes instead of nice errors
This commit is contained in:
Ulf Norell 2019-12-09 10:27:20 +01:00 committed by GitHub
commit c7b846cbfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 7 deletions

View File

@ -1290,6 +1290,16 @@ infer_expr(Env, {block, Attrs, Stmts}) ->
BlockType = fresh_uvar(Attrs), BlockType = fresh_uvar(Attrs),
NewStmts = infer_block(Env, Attrs, Stmts, BlockType), NewStmts = infer_block(Env, Attrs, Stmts, BlockType),
{typed, Attrs, {block, Attrs, NewStmts}, BlockType}; {typed, Attrs, {block, Attrs, NewStmts}, BlockType};
infer_expr(_Env, {record_or_map_error, Attrs, Fields}) ->
type_error({mixed_record_and_map, {record, Attrs, Fields}}),
Type = fresh_uvar(Attrs),
{typed, Attrs, {record, Attrs, []}, Type};
infer_expr(Env, {record_or_map_error, Attrs, Expr, []}) ->
type_error({empty_record_or_map_update, {record, Attrs, Expr, []}}),
infer_expr(Env, Expr);
infer_expr(Env, {record_or_map_error, Attrs, Expr, Fields}) ->
type_error({mixed_record_and_map, {record, Attrs, Expr, Fields}}),
infer_expr(Env, Expr);
infer_expr(Env, {lam, Attrs, Args, Body}) -> infer_expr(Env, {lam, Attrs, Args, Body}) ->
ArgTypes = [fresh_uvar(As) || {arg, As, _, _} <- Args], ArgTypes = [fresh_uvar(As) || {arg, As, _, _} <- Args],
ArgPatterns = [{typed, As, Pat, check_type(Env, T)} || {arg, As, Pat, T} <- Args], ArgPatterns = [{typed, As, Pat, check_type(Env, T)} || {arg, As, Pat, T} <- Args],
@ -2456,6 +2466,14 @@ mk_error({compiler_version_mismatch, Ann, Version, Op, Bound}) ->
"because it does not satisfy the constraint" "because it does not satisfy the constraint"
" ~s ~s ~s\n", [PrintV(Version), Op, PrintV(Bound)]), " ~s ~s ~s\n", [PrintV(Version), Op, PrintV(Bound)]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({empty_record_or_map_update, Expr}) ->
Msg = io_lib:format("Empty record/map update\n~s",
[pp_expr(" ", Expr)]),
mk_t_err(pos(Expr), Msg);
mk_error({mixed_record_and_map, Expr}) ->
Msg = io_lib:format("Mixed record fields and map keys in\n~s",
[pp_expr(" ", Expr)]),
mk_t_err(pos(Expr), Msg);
mk_error(Err) -> mk_error(Err) ->
Msg = io_lib:format("Unknown error: ~p\n", [Err]), Msg = io_lib:format("Unknown error: ~p\n", [Err]),
mk_t_err(pos(0, 0), Msg). mk_t_err(pos(0, 0), Msg).

View File

@ -349,7 +349,9 @@ record(Fs) ->
bad_expr_err("Cannot use '@' in map construction", infix({lvalue, FAnn, LV}, {'@', Ann}, Id)); bad_expr_err("Cannot use '@' in map construction", infix({lvalue, FAnn, LV}, {'@', Ann}, Id));
({field, FAnn, LV, _}) -> ({field, FAnn, LV, _}) ->
bad_expr_err("Cannot use nested fields or keys in map construction", {lvalue, FAnn, LV}) end, bad_expr_err("Cannot use nested fields or keys in map construction", {lvalue, FAnn, LV}) end,
{map, Ann, lists:map(KV, Fs)} {map, Ann, lists:map(KV, Fs)};
record_or_map_error ->
{record_or_map_error, get_ann(hd(Fs)), Fs}
end. end.
record_or_map(Fields) -> record_or_map(Fields) ->
@ -361,9 +363,7 @@ record_or_map(Fields) ->
case lists:usort(lists:map(Kind, Fields)) of case lists:usort(lists:map(Kind, Fields)) of
[proj] -> record; [proj] -> record;
[map_get] -> map; [map_get] -> map;
_ -> _ -> record_or_map_error %% Defer error until type checking
[{field, Ann, _, _} | _] = Fields,
bad_expr_err("Mixed record fields and map keys in", {record, Ann, Fields})
end. end.
field_assignment() -> field_assignment() ->

View File

@ -100,9 +100,8 @@
| {list, ann(), [expr()]} | {list, ann(), [expr()]}
| {list_comp, ann(), expr(), [comprehension_exp()]} | {list_comp, ann(), expr(), [comprehension_exp()]}
| {typed, ann(), expr(), type()} | {typed, ann(), expr(), type()}
| {record, ann(), [field(expr())]} | {record_or_map(), ann(), [field(expr())]}
| {record, ann(), expr(), [field(expr())]} %% record update | {record_or_map(), ann(), expr(), [field(expr())]} %% record/map update
| {map, ann(), expr(), [field(expr())]} %% map update
| {map, ann(), [{expr(), expr()}]} | {map, ann(), [{expr(), expr()}]}
| {map_get, ann(), expr(), expr()} | {map_get, ann(), expr(), expr()}
| {map_get, ann(), expr(), expr(), expr()} | {map_get, ann(), expr(), expr(), expr()}
@ -111,6 +110,8 @@
| id() | qid() | con() | qcon() | id() | qid() | con() | qcon()
| constant(). | constant().
-type record_or_map() :: record | map | record_or_map_error.
-type comprehension_exp() :: [ {comprehension_bind, id(), expr()} -type comprehension_exp() :: [ {comprehension_bind, id(), expr()}
| {comprehension_if, ann(), expr()} | {comprehension_if, ann(), expr()}
| letbind() ]. | letbind() ].

View File

@ -595,6 +595,17 @@ failing_contracts() ->
[<<?Pos(5, 28) [<<?Pos(5, 28)
"Invalid call to contract entrypoint 'Foo.foo'.\n" "Invalid call to contract entrypoint 'Foo.foo'.\n"
"It must be called as 'c.foo' for some c : Foo.">>]) "It must be called as 'c.foo' for some c : Foo.">>])
, ?TYPE_ERROR(bad_records,
[<<?Pos(3, 16)
"Mixed record fields and map keys in\n"
" {x = 0, [0] = 1}">>,
<<?Pos(4, 6)
"Mixed record fields and map keys in\n"
" r {x = 0, [0] = 1}">>,
<<?Pos(5, 6)
"Empty record/map update\n"
" r {}">>
])
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).

View File

@ -0,0 +1,5 @@
contract BadRecord =
entrypoint foo() =
let r = {x = 0, [0] = 1}
r{x = 0, [0] = 1}
r{}