Skip to content

Commit 11b0b62

Browse files
cristianoccknitt
authored andcommitted
Print error message when ? is used for non-optional fields.
1 parent 7ea13a4 commit 11b0b62

File tree

7 files changed

+116
-64
lines changed

7 files changed

+116
-64
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/fieldNotOptional.res:3:19
4+
5+
1 │ type r = {nonopt: int, opt?: string}
6+
2 │
7+
3 │ let v = {nonopt: ?3, opt: ?None}
8+
4 │
9+
5 │ let f = r =>
10+
11+
Field nonopt is not optional in type r. Use without ?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type r = {nonopt: int, opt?: string}
2+
3+
let v = {nonopt: ?3, opt: ?None}
4+
5+
let f = r =>
6+
switch r {
7+
| {nonopt: ?_, opt: ?_} => true
8+
}
9+
10+
type inline = A({nonopt: int, opt?: string})
11+
12+
let vi = A({nonopt: ?3, opt: ?None})
13+
14+
let fi = a =>
15+
switch a {
16+
| A ({nonopt: ?_, opt: ?_}) => true
17+
}

jscomp/ml/typecore.ml

+21-16
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type error =
7373
| Illegal_letrec_pat
7474
| Labels_omitted of string list
7575
| Empty_record_literal
76+
| Field_not_optional of string * type_expr
7677
exception Error of Location.t * Env.t * error
7778
exception Error_forward of Location.error
7879

@@ -308,6 +309,19 @@ let extract_concrete_variant env ty =
308309
| (p0, p, {type_kind=Type_open}) -> (p0, p, [])
309310
| _ -> raise Not_found
310311

312+
let label_is_optional ld =
313+
match ld.lbl_repres with
314+
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
315+
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
316+
| _ -> false
317+
318+
let check_optional_attr env ld attrs loc =
319+
let check_redundant () =
320+
if not (label_is_optional ld) then
321+
raise (Error (loc, env, Field_not_optional (ld.lbl_name, ld.lbl_res)));
322+
true in
323+
Ext_list.exists attrs (fun ({txt}, _) ->
324+
txt = "ns.optional" && check_redundant ())
311325

312326
(* unification inside type_pat*)
313327
let unify_pat_types loc env ty ty' =
@@ -1150,15 +1164,8 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
11501164
Some (p0, p), expected_ty
11511165
with Not_found -> None, newvar ()
11521166
in
1153-
let label_is_optional ld =
1154-
match ld.lbl_repres with
1155-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
1156-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
1157-
| _ -> false in
11581167
let process_optional_label (ld, pat) =
1159-
let exp_optional_attr =
1160-
Ext_list.exists pat.ppat_attributes (fun ({txt },_) -> txt = "ns.optional")
1161-
in
1168+
let exp_optional_attr = check_optional_attr !env ld pat.ppat_attributes pat.ppat_loc in
11621169
let isFromPamatch = match pat.ppat_desc with
11631170
| Ppat_construct ({txt = Lident s}, _) ->
11641171
String.length s >= 2 && s.[0] = '#' && s.[1] = '$'
@@ -1877,15 +1884,8 @@ and type_expect_ ?in_function ?(recarg=Rejected) env sexp ty_expected =
18771884
unify_exp env (re exp) (instance env ty_expected);
18781885
exp
18791886
in
1880-
let label_is_optional ld =
1881-
match ld.lbl_repres with
1882-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
1883-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
1884-
| _ -> false in
18851887
let process_optional_label (id, ld, e) =
1886-
let exp_optional_attr =
1887-
Ext_list.exists e.pexp_attributes (fun ({txt },_) -> txt = "ns.optional")
1888-
in
1888+
let exp_optional_attr = check_optional_attr env ld e.pexp_attributes e.pexp_loc in
18891889
if label_is_optional ld && not exp_optional_attr then
18901890
let lid = mknoloc (Longident.(Ldot (Lident "*predef*", "Some"))) in
18911891
let e = Ast_helper.Exp.construct ~loc:e.pexp_loc lid (Some e)
@@ -3797,6 +3797,11 @@ let report_error env ppf = function
37973797
(String.concat ", " labels)
37983798
| Empty_record_literal ->
37993799
fprintf ppf "Empty record literal {} should be type annotated or used in a record context."
3800+
| Field_not_optional (name, typ) ->
3801+
fprintf ppf
3802+
"Field @{<info>%s@} is not optional in type %a. Use without ?" name
3803+
type_expr typ
3804+
38003805
38013806
let super_report_error_no_wrap_printing_env = report_error
38023807

jscomp/ml/typecore.mli

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ type error =
109109
| Illegal_letrec_pat
110110
| Labels_omitted of string list
111111
| Empty_record_literal
112+
| Field_not_optional of string * type_expr
112113
exception Error of Location.t * Env.t * error
113114
exception Error_forward of Location.error
114115

lib/4.06.1/unstable/js_compiler.ml

+22-16
Original file line numberDiff line numberDiff line change
@@ -40690,6 +40690,7 @@ type error =
4069040690
| Illegal_letrec_pat
4069140691
| Labels_omitted of string list
4069240692
| Empty_record_literal
40693+
| Field_not_optional of string * type_expr
4069340694
exception Error of Location.t * Env.t * error
4069440695
exception Error_forward of Location.error
4069540696

@@ -40797,6 +40798,7 @@ type error =
4079740798
| Illegal_letrec_pat
4079840799
| Labels_omitted of string list
4079940800
| Empty_record_literal
40801+
| Field_not_optional of string * type_expr
4080040802
exception Error of Location.t * Env.t * error
4080140803
exception Error_forward of Location.error
4080240804

@@ -41032,6 +41034,19 @@ let extract_concrete_variant env ty =
4103241034
| (p0, p, {type_kind=Type_open}) -> (p0, p, [])
4103341035
| _ -> raise Not_found
4103441036

41037+
let label_is_optional ld =
41038+
match ld.lbl_repres with
41039+
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
41040+
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
41041+
| _ -> false
41042+
41043+
let check_optional_attr env ld attrs loc =
41044+
let check_redundant () =
41045+
if not (label_is_optional ld) then
41046+
raise (Error (loc, env, Field_not_optional (ld.lbl_name, ld.lbl_res)));
41047+
true in
41048+
Ext_list.exists attrs (fun ({txt}, _) ->
41049+
txt = "ns.optional" && check_redundant ())
4103541050

4103641051
(* unification inside type_pat*)
4103741052
let unify_pat_types loc env ty ty' =
@@ -41874,15 +41889,8 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
4187441889
Some (p0, p), expected_ty
4187541890
with Not_found -> None, newvar ()
4187641891
in
41877-
let label_is_optional ld =
41878-
match ld.lbl_repres with
41879-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
41880-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
41881-
| _ -> false in
4188241892
let process_optional_label (ld, pat) =
41883-
let exp_optional_attr =
41884-
Ext_list.exists pat.ppat_attributes (fun ({txt },_) -> txt = "ns.optional")
41885-
in
41893+
let exp_optional_attr = check_optional_attr !env ld pat.ppat_attributes pat.ppat_loc in
4188641894
let isFromPamatch = match pat.ppat_desc with
4188741895
| Ppat_construct ({txt = Lident s}, _) ->
4188841896
String.length s >= 2 && s.[0] = '#' && s.[1] = '$'
@@ -42601,15 +42609,8 @@ and type_expect_ ?in_function ?(recarg=Rejected) env sexp ty_expected =
4260142609
unify_exp env (re exp) (instance env ty_expected);
4260242610
exp
4260342611
in
42604-
let label_is_optional ld =
42605-
match ld.lbl_repres with
42606-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
42607-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
42608-
| _ -> false in
4260942612
let process_optional_label (id, ld, e) =
42610-
let exp_optional_attr =
42611-
Ext_list.exists e.pexp_attributes (fun ({txt },_) -> txt = "ns.optional")
42612-
in
42613+
let exp_optional_attr = check_optional_attr env ld e.pexp_attributes e.pexp_loc in
4261342614
if label_is_optional ld && not exp_optional_attr then
4261442615
let lid = mknoloc (Longident.(Ldot (Lident "*predef*", "Some"))) in
4261542616
let e = Ast_helper.Exp.construct ~loc:e.pexp_loc lid (Some e)
@@ -44521,6 +44522,11 @@ let report_error env ppf = function
4452144522
(String.concat ", " labels)
4452244523
| Empty_record_literal ->
4452344524
fprintf ppf "Empty record literal {} should be type annotated or used in a record context."
44525+
| Field_not_optional (name, typ) ->
44526+
fprintf ppf
44527+
"Field @{<info>%s@} is not optional in type %a. Use without ?" name
44528+
type_expr typ
44529+
4452444530

4452544531
let super_report_error_no_wrap_printing_env = report_error
4452644532

lib/4.06.1/unstable/js_playground_compiler.ml

+22-16
Original file line numberDiff line numberDiff line change
@@ -40690,6 +40690,7 @@ type error =
4069040690
| Illegal_letrec_pat
4069140691
| Labels_omitted of string list
4069240692
| Empty_record_literal
40693+
| Field_not_optional of string * type_expr
4069340694
exception Error of Location.t * Env.t * error
4069440695
exception Error_forward of Location.error
4069540696

@@ -40797,6 +40798,7 @@ type error =
4079740798
| Illegal_letrec_pat
4079840799
| Labels_omitted of string list
4079940800
| Empty_record_literal
40801+
| Field_not_optional of string * type_expr
4080040802
exception Error of Location.t * Env.t * error
4080140803
exception Error_forward of Location.error
4080240804

@@ -41032,6 +41034,19 @@ let extract_concrete_variant env ty =
4103241034
| (p0, p, {type_kind=Type_open}) -> (p0, p, [])
4103341035
| _ -> raise Not_found
4103441036

41037+
let label_is_optional ld =
41038+
match ld.lbl_repres with
41039+
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
41040+
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
41041+
| _ -> false
41042+
41043+
let check_optional_attr env ld attrs loc =
41044+
let check_redundant () =
41045+
if not (label_is_optional ld) then
41046+
raise (Error (loc, env, Field_not_optional (ld.lbl_name, ld.lbl_res)));
41047+
true in
41048+
Ext_list.exists attrs (fun ({txt}, _) ->
41049+
txt = "ns.optional" && check_redundant ())
4103541050

4103641051
(* unification inside type_pat*)
4103741052
let unify_pat_types loc env ty ty' =
@@ -41874,15 +41889,8 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
4187441889
Some (p0, p), expected_ty
4187541890
with Not_found -> None, newvar ()
4187641891
in
41877-
let label_is_optional ld =
41878-
match ld.lbl_repres with
41879-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
41880-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
41881-
| _ -> false in
4188241892
let process_optional_label (ld, pat) =
41883-
let exp_optional_attr =
41884-
Ext_list.exists pat.ppat_attributes (fun ({txt },_) -> txt = "ns.optional")
41885-
in
41893+
let exp_optional_attr = check_optional_attr !env ld pat.ppat_attributes pat.ppat_loc in
4188641894
let isFromPamatch = match pat.ppat_desc with
4188741895
| Ppat_construct ({txt = Lident s}, _) ->
4188841896
String.length s >= 2 && s.[0] = '#' && s.[1] = '$'
@@ -42601,15 +42609,8 @@ and type_expect_ ?in_function ?(recarg=Rejected) env sexp ty_expected =
4260142609
unify_exp env (re exp) (instance env ty_expected);
4260242610
exp
4260342611
in
42604-
let label_is_optional ld =
42605-
match ld.lbl_repres with
42606-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
42607-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
42608-
| _ -> false in
4260942612
let process_optional_label (id, ld, e) =
42610-
let exp_optional_attr =
42611-
Ext_list.exists e.pexp_attributes (fun ({txt },_) -> txt = "ns.optional")
42612-
in
42613+
let exp_optional_attr = check_optional_attr env ld e.pexp_attributes e.pexp_loc in
4261342614
if label_is_optional ld && not exp_optional_attr then
4261442615
let lid = mknoloc (Longident.(Ldot (Lident "*predef*", "Some"))) in
4261542616
let e = Ast_helper.Exp.construct ~loc:e.pexp_loc lid (Some e)
@@ -44521,6 +44522,11 @@ let report_error env ppf = function
4452144522
(String.concat ", " labels)
4452244523
| Empty_record_literal ->
4452344524
fprintf ppf "Empty record literal {} should be type annotated or used in a record context."
44525+
| Field_not_optional (name, typ) ->
44526+
fprintf ppf
44527+
"Field @{<info>%s@} is not optional in type %a. Use without ?" name
44528+
type_expr typ
44529+
4452444530

4452544531
let super_report_error_no_wrap_printing_env = report_error
4452644532

lib/4.06.1/whole_compiler.ml

+22-16
Original file line numberDiff line numberDiff line change
@@ -216862,6 +216862,7 @@ type error =
216862216862
| Illegal_letrec_pat
216863216863
| Labels_omitted of string list
216864216864
| Empty_record_literal
216865+
| Field_not_optional of string * type_expr
216865216866
exception Error of Location.t * Env.t * error
216866216867
exception Error_forward of Location.error
216867216868

@@ -216969,6 +216970,7 @@ type error =
216969216970
| Illegal_letrec_pat
216970216971
| Labels_omitted of string list
216971216972
| Empty_record_literal
216973+
| Field_not_optional of string * type_expr
216972216974
exception Error of Location.t * Env.t * error
216973216975
exception Error_forward of Location.error
216974216976

@@ -217204,6 +217206,19 @@ let extract_concrete_variant env ty =
217204217206
| (p0, p, {type_kind=Type_open}) -> (p0, p, [])
217205217207
| _ -> raise Not_found
217206217208

217209+
let label_is_optional ld =
217210+
match ld.lbl_repres with
217211+
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
217212+
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
217213+
| _ -> false
217214+
217215+
let check_optional_attr env ld attrs loc =
217216+
let check_redundant () =
217217+
if not (label_is_optional ld) then
217218+
raise (Error (loc, env, Field_not_optional (ld.lbl_name, ld.lbl_res)));
217219+
true in
217220+
Ext_list.exists attrs (fun ({txt}, _) ->
217221+
txt = "ns.optional" && check_redundant ())
217207217222

217208217223
(* unification inside type_pat*)
217209217224
let unify_pat_types loc env ty ty' =
@@ -218046,15 +218061,8 @@ and type_pat_aux ~constrs ~labels ~no_existentials ~mode ~explode ~env
218046218061
Some (p0, p), expected_ty
218047218062
with Not_found -> None, newvar ()
218048218063
in
218049-
let label_is_optional ld =
218050-
match ld.lbl_repres with
218051-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
218052-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
218053-
| _ -> false in
218054218064
let process_optional_label (ld, pat) =
218055-
let exp_optional_attr =
218056-
Ext_list.exists pat.ppat_attributes (fun ({txt },_) -> txt = "ns.optional")
218057-
in
218065+
let exp_optional_attr = check_optional_attr !env ld pat.ppat_attributes pat.ppat_loc in
218058218066
let isFromPamatch = match pat.ppat_desc with
218059218067
| Ppat_construct ({txt = Lident s}, _) ->
218060218068
String.length s >= 2 && s.[0] = '#' && s.[1] = '$'
@@ -218773,15 +218781,8 @@ and type_expect_ ?in_function ?(recarg=Rejected) env sexp ty_expected =
218773218781
unify_exp env (re exp) (instance env ty_expected);
218774218782
exp
218775218783
in
218776-
let label_is_optional ld =
218777-
match ld.lbl_repres with
218778-
| Record_optional_labels lbls -> Ext_list.mem_string lbls ld.lbl_name
218779-
| Record_inlined {optional_labels} -> Ext_list.mem_string optional_labels ld.lbl_name
218780-
| _ -> false in
218781218784
let process_optional_label (id, ld, e) =
218782-
let exp_optional_attr =
218783-
Ext_list.exists e.pexp_attributes (fun ({txt },_) -> txt = "ns.optional")
218784-
in
218785+
let exp_optional_attr = check_optional_attr env ld e.pexp_attributes e.pexp_loc in
218785218786
if label_is_optional ld && not exp_optional_attr then
218786218787
let lid = mknoloc (Longident.(Ldot (Lident "*predef*", "Some"))) in
218787218788
let e = Ast_helper.Exp.construct ~loc:e.pexp_loc lid (Some e)
@@ -220693,6 +220694,11 @@ let report_error env ppf = function
220693220694
(String.concat ", " labels)
220694220695
| Empty_record_literal ->
220695220696
fprintf ppf "Empty record literal {} should be type annotated or used in a record context."
220697+
| Field_not_optional (name, typ) ->
220698+
fprintf ppf
220699+
"Field @{<info>%s@} is not optional in type %a. Use without ?" name
220700+
type_expr typ
220701+
220696220702

220697220703
let super_report_error_no_wrap_printing_env = report_error
220698220704

0 commit comments

Comments
 (0)