Improve error messages in ast_typed/combinators.ml
This commit is contained in:
parent
e67e2098c2
commit
1863cf324b
@ -104,4 +104,5 @@ let%expect_test _ =
|
|||||||
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
|
* Open a gitlab issue: https://gitlab.com/ligolang/ligo/issues/new
|
||||||
* Check the changelog by running 'ligo changelog' |} ] ;
|
* Check the changelog by running 'ligo changelog' |} ] ;
|
||||||
|
|
||||||
|
run_ligo_bad [ "compile-contract" ; "../../test/contracts/negative/id.mligo" ; "main" ] ;
|
||||||
|
[%expect {| ligo: in file "id.mligo", line 45, characters 4-51. Expected a different type: Expected the type option but got the type record[profile -> bytes , owner -> address , controller -> address] |}] ;
|
||||||
|
@ -1,6 +1,28 @@
|
|||||||
open Trace
|
open Trace
|
||||||
open Types
|
open Types
|
||||||
|
|
||||||
|
module Errors = struct
|
||||||
|
let not_a_x_expression expected_expression actual_expression () =
|
||||||
|
let message () =
|
||||||
|
Format.asprintf "Expected a %s expression but got a %a expression"
|
||||||
|
expected_expression
|
||||||
|
PP.expression actual_expression in
|
||||||
|
error (thunk "Expected a different expression") message
|
||||||
|
|
||||||
|
let not_a_x_type expected_type actual_type () =
|
||||||
|
let message () =
|
||||||
|
Format.asprintf "Expected the type %s but got the type %a"
|
||||||
|
expected_type
|
||||||
|
PP.type_value actual_type in
|
||||||
|
error (thunk "Expected a different type") message
|
||||||
|
|
||||||
|
let declaration_not_found expected_declaration () =
|
||||||
|
let message () =
|
||||||
|
Format.asprintf "Could not find a declaration with the name %s"
|
||||||
|
expected_declaration in
|
||||||
|
error (thunk "No declaration with the given name") message
|
||||||
|
end
|
||||||
|
|
||||||
let make_t type_value' simplified = { type_value' ; simplified }
|
let make_t type_value' simplified = { type_value' ; simplified }
|
||||||
let make_a_e ?(location = Location.generated) expression type_annotation environment = {
|
let make_a_e ?(location = Location.generated) expression type_annotation environment = {
|
||||||
expression ;
|
expression ;
|
||||||
@ -61,102 +83,102 @@ let get_expression (x:annotated_expression) = x.expression
|
|||||||
|
|
||||||
let get_lambda e : _ result = match e with
|
let get_lambda e : _ result = match e with
|
||||||
| E_lambda l -> ok l
|
| E_lambda l -> ok l
|
||||||
| _ -> simple_fail "not a lambda"
|
| _ -> fail @@ Errors.not_a_x_expression "lambda" e ()
|
||||||
|
|
||||||
let get_lambda_with_type e =
|
let get_lambda_with_type e =
|
||||||
match (e.expression , e.type_annotation.type_value') with
|
match (e.expression , e.type_annotation.type_value') with
|
||||||
| E_lambda l , T_arrow (i,o) -> ok (l , (i,o))
|
| E_lambda l , T_arrow (i,o) -> ok (l , (i,o))
|
||||||
| _ -> simple_fail "not a lambda with functional type"
|
| _ -> fail @@ Errors.not_a_x_expression "lambda with functional type" e.expression ()
|
||||||
|
|
||||||
let get_t_bool (t:type_value) : unit result = match t.type_value' with
|
let get_t_bool (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_bool) -> ok ()
|
| T_constant (TC_bool) -> ok ()
|
||||||
| _ -> simple_fail "not a bool"
|
| _ -> fail @@ Errors.not_a_x_type "bool" t ()
|
||||||
|
|
||||||
let get_t_int (t:type_value) : unit result = match t.type_value' with
|
let get_t_int (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_int) -> ok ()
|
| T_constant (TC_int) -> ok ()
|
||||||
| _ -> simple_fail "not a int"
|
| _ -> fail @@ Errors.not_a_x_type "int" t ()
|
||||||
|
|
||||||
let get_t_nat (t:type_value) : unit result = match t.type_value' with
|
let get_t_nat (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_nat) -> ok ()
|
| T_constant (TC_nat) -> ok ()
|
||||||
| _ -> simple_fail "not a nat"
|
| _ -> fail @@ Errors.not_a_x_type "nat" t ()
|
||||||
|
|
||||||
let get_t_unit (t:type_value) : unit result = match t.type_value' with
|
let get_t_unit (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_unit) -> ok ()
|
| T_constant (TC_unit) -> ok ()
|
||||||
| _ -> simple_fail "not a unit"
|
| _ -> fail @@ Errors.not_a_x_type "unit" t ()
|
||||||
|
|
||||||
let get_t_mutez (t:type_value) : unit result = match t.type_value' with
|
let get_t_mutez (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_mutez) -> ok ()
|
| T_constant (TC_mutez) -> ok ()
|
||||||
| _ -> simple_fail "not a tez"
|
| _ -> fail @@ Errors.not_a_x_type "tez" t ()
|
||||||
|
|
||||||
let get_t_bytes (t:type_value) : unit result = match t.type_value' with
|
let get_t_bytes (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_bytes) -> ok ()
|
| T_constant (TC_bytes) -> ok ()
|
||||||
| _ -> simple_fail "not a bytes"
|
| _ -> fail @@ Errors.not_a_x_type "bytes" t ()
|
||||||
|
|
||||||
let get_t_string (t:type_value) : unit result = match t.type_value' with
|
let get_t_string (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_string) -> ok ()
|
| T_constant (TC_string) -> ok ()
|
||||||
| _ -> simple_fail "not a string"
|
| _ -> fail @@ Errors.not_a_x_type "string" t ()
|
||||||
|
|
||||||
let get_t_contract (t:type_value) : type_value result = match t.type_value' with
|
let get_t_contract (t:type_value) : type_value result = match t.type_value' with
|
||||||
| T_operator (TC_contract x) -> ok x
|
| T_operator (TC_contract x) -> ok x
|
||||||
| _ -> simple_fail "not a contract"
|
| _ -> fail @@ Errors.not_a_x_type "contract" t ()
|
||||||
|
|
||||||
let get_t_option (t:type_value) : type_value result = match t.type_value' with
|
let get_t_option (t:type_value) : type_value result = match t.type_value' with
|
||||||
| T_operator (TC_option o) -> ok o
|
| T_operator (TC_option o) -> ok o
|
||||||
| _ -> simple_fail "not a option"
|
| _ -> fail @@ Errors.not_a_x_type "option" t ()
|
||||||
|
|
||||||
let get_t_list (t:type_value) : type_value result = match t.type_value' with
|
let get_t_list (t:type_value) : type_value result = match t.type_value' with
|
||||||
| T_operator (TC_list l) -> ok l
|
| T_operator (TC_list l) -> ok l
|
||||||
| _ -> simple_fail "not a list"
|
| _ -> fail @@ Errors.not_a_x_type "list" t ()
|
||||||
|
|
||||||
let get_t_set (t:type_value) : type_value result = match t.type_value' with
|
let get_t_set (t:type_value) : type_value result = match t.type_value' with
|
||||||
| T_operator (TC_set s) -> ok s
|
| T_operator (TC_set s) -> ok s
|
||||||
| _ -> simple_fail "not a set"
|
| _ -> fail @@ Errors.not_a_x_type "set" t ()
|
||||||
|
|
||||||
let get_t_key (t:type_value) : unit result = match t.type_value' with
|
let get_t_key (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_key) -> ok ()
|
| T_constant (TC_key) -> ok ()
|
||||||
| _ -> simple_fail "not a key"
|
| _ -> fail @@ Errors.not_a_x_type "key" t ()
|
||||||
|
|
||||||
let get_t_signature (t:type_value) : unit result = match t.type_value' with
|
let get_t_signature (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_signature) -> ok ()
|
| T_constant (TC_signature) -> ok ()
|
||||||
| _ -> simple_fail "not a signature"
|
| _ -> fail @@ Errors.not_a_x_type "signature" t ()
|
||||||
|
|
||||||
let get_t_key_hash (t:type_value) : unit result = match t.type_value' with
|
let get_t_key_hash (t:type_value) : unit result = match t.type_value' with
|
||||||
| T_constant (TC_key_hash) -> ok ()
|
| T_constant (TC_key_hash) -> ok ()
|
||||||
| _ -> simple_fail "not a key_hash"
|
| _ -> fail @@ Errors.not_a_x_type "key_hash" t ()
|
||||||
|
|
||||||
let get_t_tuple (t:type_value) : type_value list result = match t.type_value' with
|
let get_t_tuple (t:type_value) : type_value list result = match t.type_value' with
|
||||||
| T_tuple lst -> ok lst
|
| T_tuple lst -> ok lst
|
||||||
| _ -> simple_fail "not a tuple"
|
| _ -> fail @@ Errors.not_a_x_type "tuple" t ()
|
||||||
|
|
||||||
let get_t_pair (t:type_value) : (type_value * type_value) result = match t.type_value' with
|
let get_t_pair (t:type_value) : (type_value * type_value) result = match t.type_value' with
|
||||||
| T_tuple lst ->
|
| T_tuple lst ->
|
||||||
let%bind () =
|
let%bind () =
|
||||||
trace_strong (simple_error "not a pair") @@
|
trace_strong (Errors.not_a_x_type "pair (tuple with two elements)" t ()) @@
|
||||||
Assert.assert_list_size lst 2 in
|
Assert.assert_list_size lst 2 in
|
||||||
ok List.(nth lst 0 , nth lst 1)
|
ok List.(nth lst 0 , nth lst 1)
|
||||||
| _ -> simple_fail "not a tuple"
|
| _ -> fail @@ Errors.not_a_x_type "pair (tuple with two elements)" t ()
|
||||||
|
|
||||||
let get_t_function (t:type_value) : (type_value * type_value) result = match t.type_value' with
|
let get_t_function (t:type_value) : (type_value * type_value) result = match t.type_value' with
|
||||||
| T_arrow (a,r) -> ok (a,r)
|
| T_arrow (a,r) -> ok (a,r)
|
||||||
| _ -> simple_fail "not a function"
|
| _ -> fail @@ Errors.not_a_x_type "function" t ()
|
||||||
|
|
||||||
let get_t_sum (t:type_value) : type_value constructor_map result = match t.type_value' with
|
let get_t_sum (t:type_value) : type_value constructor_map result = match t.type_value' with
|
||||||
| T_sum m -> ok m
|
| T_sum m -> ok m
|
||||||
| _ -> simple_fail "not a sum"
|
| _ -> fail @@ Errors.not_a_x_type "sum" t ()
|
||||||
|
|
||||||
let get_t_record (t:type_value) : type_value label_map result = match t.type_value' with
|
let get_t_record (t:type_value) : type_value label_map result = match t.type_value' with
|
||||||
| T_record m -> ok m
|
| T_record m -> ok m
|
||||||
| _ -> simple_fail "not a record type"
|
| _ -> fail @@ Errors.not_a_x_type "record" t ()
|
||||||
|
|
||||||
let get_t_map (t:type_value) : (type_value * type_value) result =
|
let get_t_map (t:type_value) : (type_value * type_value) result =
|
||||||
match t.type_value' with
|
match t.type_value' with
|
||||||
| T_operator (TC_map (k,v)) -> ok (k, v)
|
| T_operator (TC_map (k,v)) -> ok (k, v)
|
||||||
| _ -> simple_fail "get: not a map"
|
| _ -> fail @@ Errors.not_a_x_type "map" t ()
|
||||||
|
|
||||||
let get_t_big_map (t:type_value) : (type_value * type_value) result =
|
let get_t_big_map (t:type_value) : (type_value * type_value) result =
|
||||||
match t.type_value' with
|
match t.type_value' with
|
||||||
| T_operator (TC_big_map (k,v)) -> ok (k, v)
|
| T_operator (TC_big_map (k,v)) -> ok (k, v)
|
||||||
| _ -> simple_fail "get: not a big_map"
|
| _ -> fail @@ Errors.not_a_x_type "big_map" t ()
|
||||||
|
|
||||||
let get_t_map_key : type_value -> type_value result = fun t ->
|
let get_t_map_key : type_value -> type_value result = fun t ->
|
||||||
let%bind (key , _) = get_t_map t in
|
let%bind (key , _) = get_t_map t in
|
||||||
@ -303,5 +325,5 @@ let get_declaration_by_name : program -> string -> declaration result = fun p na
|
|||||||
match declaration with
|
match declaration with
|
||||||
| Declaration_constant (d , _) -> d.name = Var.of_name name
|
| Declaration_constant (d , _) -> d.name = Var.of_name name
|
||||||
in
|
in
|
||||||
trace_option (simple_error "no declaration with given name") @@
|
trace_option (Errors.declaration_not_found name ()) @@
|
||||||
List.find_opt aux @@ List.map Location.unwrap p
|
List.find_opt aux @@ List.map Location.unwrap p
|
||||||
|
115
src/test/contracts/negative/id.mligo
Normal file
115
src/test/contracts/negative/id.mligo
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
type id = int
|
||||||
|
|
||||||
|
type id_details = {
|
||||||
|
owner: address;
|
||||||
|
controller: address;
|
||||||
|
profile: bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
(* The prices kept in storage can be changed by bakers, though they should only be
|
||||||
|
adjusted down over time, not up. *)
|
||||||
|
type storage = (id, id_details) big_map * int * (tez * tez)
|
||||||
|
|
||||||
|
(** Preliminary thoughts on ids:
|
||||||
|
|
||||||
|
I very much like the simplicity of http://gurno.com/adam/mne/.
|
||||||
|
5 three letter words means you have a 15 character identity, not actually more
|
||||||
|
annoying than an IP address and a lot more memorable than the raw digits. This
|
||||||
|
can be stored as a single integer which is then translated into the corresponding
|
||||||
|
series of 5 words.
|
||||||
|
|
||||||
|
I in general like the idea of having a 'skip' mechanism, but it does need to cost
|
||||||
|
something so people don't eat up the address space. 256 ^ 5 means you have a lot
|
||||||
|
of address space, but if people troll by skipping a lot that could be eaten up.
|
||||||
|
Should probably do some napkin calculations for how expensive skipping needs to
|
||||||
|
be to deter people from doing it just to chew up address space.
|
||||||
|
*)
|
||||||
|
|
||||||
|
let buy (parameter, storage: (bytes * address option) * storage) =
|
||||||
|
let void: unit = assert (amount = storage.2.0) in
|
||||||
|
let profile, initial_controller = parameter in
|
||||||
|
let identities, last_id, prices = storage in
|
||||||
|
let controller: address =
|
||||||
|
match initial_controller with
|
||||||
|
| Some addr -> addr
|
||||||
|
| None -> sender
|
||||||
|
in
|
||||||
|
let new_id: id = last_id + 1 in
|
||||||
|
let new_id_details: id_details = {
|
||||||
|
owner = sender ;
|
||||||
|
controller = controller ;
|
||||||
|
profile = profile ;
|
||||||
|
}
|
||||||
|
in
|
||||||
|
let updated_identities: (id, id_details) big_map =
|
||||||
|
Big_map.update new_id new_id_details identities
|
||||||
|
in
|
||||||
|
([]: instruction), (updated_identities, new_id, prices)
|
||||||
|
|
||||||
|
let update_owner (parameter, storage: (id * address) * storage) =
|
||||||
|
let id, new_owner = parameter in
|
||||||
|
let identities, last_id, prices = storage in
|
||||||
|
let current_id_details = Bip_map.find_opt id identities in
|
||||||
|
let is_allowed: bool =
|
||||||
|
if sender = current_id_details.owner
|
||||||
|
then true
|
||||||
|
else failwith "You are not the owner of the ID " ^ (string_of_int id)
|
||||||
|
in
|
||||||
|
let updated_id_details = {
|
||||||
|
owner = new_owner;
|
||||||
|
controller = current_id_details.controller;
|
||||||
|
profile = current_id_details.profile;
|
||||||
|
}
|
||||||
|
in
|
||||||
|
let updated_identities = Big_map.update id updated_id_details identities in
|
||||||
|
([]: instruction), (updated_identities, last_id, prices)
|
||||||
|
|
||||||
|
let update_details (parameter, storage: (id * bytes option * address option) * storage) =
|
||||||
|
let id, new_profile, new_controller = parameter in
|
||||||
|
let identities, last_id, prices = storage in
|
||||||
|
let current_id_details = Big_map.find_opt id identities in
|
||||||
|
let is_allowed: bool =
|
||||||
|
if
|
||||||
|
match current_id_details with
|
||||||
|
| Some id_details -> (sender = id_details.controller) || (sender = id_details.owner)
|
||||||
|
| None -> failwith ("No such ID " + id)
|
||||||
|
then true
|
||||||
|
else failwith ("You are not the owner or controller of the ID " ^ id)
|
||||||
|
in
|
||||||
|
let owner: address = current_id_details.owner in
|
||||||
|
let profile: bytes =
|
||||||
|
match new_profile with
|
||||||
|
| None -> (* Default *) current_id_details.profile
|
||||||
|
| Some new_profile -> new_profile
|
||||||
|
in
|
||||||
|
let controller: address =
|
||||||
|
match new_controller with
|
||||||
|
| None -> (* Default *) current_id_details.controller
|
||||||
|
| Some new_controller -> new_controller
|
||||||
|
in
|
||||||
|
let updated_id_details = {
|
||||||
|
owner = owner;
|
||||||
|
controller = controller;
|
||||||
|
profile = profile;
|
||||||
|
}
|
||||||
|
in
|
||||||
|
let updated_identities = Big_map.update id updated_id_details identities in
|
||||||
|
([]: instruction), (updated_identities, last_id, prices)
|
||||||
|
|
||||||
|
(* Let someone skip the next identity so nobody has to take one that's undesirable *)
|
||||||
|
let skip (p,s: unit * storage) =
|
||||||
|
let void: unit = assert (amount = storage.2.1) in
|
||||||
|
let identities, last_id, prices = storage in
|
||||||
|
([]: instruction), (identities, last_id + 1, prices)
|
||||||
|
|
||||||
|
let whois_id (query, storage: id * storage) =
|
||||||
|
let identities, last_id, _ = storage in
|
||||||
|
let result: (unit -> id_details) =
|
||||||
|
let id_details: id_details =
|
||||||
|
begin
|
||||||
|
match Big_map.find_opt query identities with
|
||||||
|
| Some details -> details
|
||||||
|
| None -> failwith "This ID doesn't exist in the system."
|
||||||
|
end
|
||||||
|
in (fun (x: unit) -> id_details)
|
||||||
|
in ([result]: instruction), storage
|
Loading…
Reference in New Issue
Block a user