@@ -43,7 +43,7 @@ type error =
4343 | Method_mismatch of string * type_expr * type_expr
4444 | Unbound_value of Longident .t
4545 | Unbound_constructor of Longident .t
46- | Unbound_label of Longident .t
46+ | Unbound_label of Longident .t * type_expr option
4747 | Unbound_module of Longident .t
4848 | Unbound_modtype of Longident .t
4949 | Ill_typed_functor_application of Longident .t
@@ -129,7 +129,7 @@ let find_all_constructors =
129129 find_component Env. lookup_all_constructors (fun lid ->
130130 Unbound_constructor lid)
131131let find_all_labels =
132- find_component Env. lookup_all_labels (fun lid -> Unbound_label lid)
132+ find_component Env. lookup_all_labels (fun lid -> Unbound_label ( lid, None ) )
133133
134134let find_value env loc lid =
135135 Env. check_value_name (Longident. last lid) loc;
@@ -160,12 +160,14 @@ let find_modtype env loc lid =
160160 Builtin_attributes. check_deprecated loc decl.mtd_attributes (Path. name path);
161161 r
162162
163- let unbound_constructor_error env lid =
163+ let unbound_constructor_error ?from_type env lid =
164+ ignore from_type;
164165 narrow_unbound_lid_error env lid.loc lid.txt (fun lid ->
165166 Unbound_constructor lid)
166167
167- let unbound_label_error env lid =
168- narrow_unbound_lid_error env lid.loc lid.txt (fun lid -> Unbound_label lid)
168+ let unbound_label_error ?from_type env lid =
169+ narrow_unbound_lid_error env lid.loc lid.txt (fun lid ->
170+ Unbound_label (lid, from_type))
169171
170172(* Support for first-class modules. *)
171173
@@ -909,18 +911,49 @@ let report_error env ppf = function
909911 = Bar@}.@]@]"
910912 Printtyp. longident lid Printtyp. longident lid Printtyp. longident lid;
911913 spellcheck ppf fold_constructors env lid
912- | Unbound_label lid ->
914+ | Unbound_label ( lid , from_type ) ->
913915 (* modified *)
914- Format. fprintf ppf
915- " @[<v>@{<info>%a@} refers to a record field, but no corresponding record \
916- type is in scope.@,\
917- @,\
918- If it's defined in another module or file, bring it into scope by:@,\
919- @[- Prefixing the field name with the module name:@ \
920- @{<info>TheModule.%a@}@]@,\
921- @[- Or specifying the record type explicitly:@ @{<info>let theValue: \
922- TheModule.theType = {%a: VALUE}@}@]@]"
923- Printtyp. longident lid Printtyp. longident lid Printtyp. longident lid;
916+ (match from_type with
917+ | Some {desc = Tconstr (p , _ , _ )} when Path. same p Predef. path_option ->
918+ (* TODO: Extend for nullable/null? *)
919+ Format. fprintf ppf
920+ " @[<v>You're trying to access the record field @{<info>%a@}, but the \
921+ value you're trying to access it on is an @{<info>option@}.@ You need \
922+ to unwrap the option first before accessing the record field.@,\
923+ @\n \
924+ Possible solutions:@,\
925+ @[- Use @{<info>Option.map@} to transform the option: \
926+ @{<info>xx->Option.map(field => field.%a)@}@]@,\
927+ @[- Or use @{<info>Option.getOr@} with a default: \
928+ @{<info>xx->Option.getOr(defaultRecord).%a@}@]@]"
929+ Printtyp. longident lid Printtyp. longident lid Printtyp. longident lid
930+ | Some {desc = Tconstr (p , _ , _ )} when Path. same p Predef. path_array ->
931+ Format. fprintf ppf
932+ " @[<v>You're trying to access the record field @{<info>%a@}, but the \
933+ value you're trying to access it on is an @{<info>array@}.@ You need \
934+ to access an individual element of the array if you want to access an \
935+ individual record field.@]"
936+ Printtyp. longident lid
937+ | Some ({desc = Tconstr (_p , _ , _ )} as t1 ) ->
938+ Format. fprintf ppf
939+ " @[<v>You're trying to access the record field @{<info>%a@}, but the \
940+ thing you're trying to access it on is not a record. @,\n \
941+ The type of the thing you're trying to access it on is:@,\n \
942+ %a@,\n \
943+ @,\
944+ Only records have fields that can be accessed with dot notation.@]"
945+ Printtyp. longident lid Error_message_utils. type_expr t1
946+ | None | Some _ ->
947+ Format. fprintf ppf
948+ " @[<v>@{<info>%a@} refers to a record field, but no corresponding \
949+ record type is in scope.@,\
950+ @,\
951+ If it's defined in another module or file, bring it into scope by:@,\
952+ @[- Prefixing the field name with the module name:@ \
953+ @{<info>TheModule.%a@}@]@,\
954+ @[- Or specifying the record type explicitly:@ @{<info>let theValue: \
955+ TheModule.theType = {%a: VALUE}@}@]@]"
956+ Printtyp. longident lid Printtyp. longident lid Printtyp. longident lid);
924957 spellcheck ppf fold_labels env lid
925958 | Unbound_modtype lid ->
926959 fprintf ppf " Unbound module type %a" longident lid;
0 commit comments