Browse Source

[message reporting] Update sub errors handling (#11595)

* deal with some sub error TODO

* Add misc test for 'Called from macro here' sub error

* Handle more sub error TODO

* usual List.rev List.rev x

* More TODOs

* Handle more sub errors

* Update tests

* Last (?) sub error TODO

* [tests] Update expected result with sub error
Rudy Ges 1 year ago
parent
commit
603f6f6c17
33 changed files with 188 additions and 110 deletions
  1. 2 2
      src/compiler/compiler.ml
  2. 3 3
      src/filters/localStatic.ml
  3. 3 2
      src/optimization/analyzerTexpr.ml
  4. 3 3
      src/optimization/inlineConstructors.ml
  5. 10 12
      src/typing/callUnification.ml
  6. 3 2
      src/typing/fields.ml
  7. 6 6
      src/typing/generic.ml
  8. 16 16
      src/typing/typeload.ml
  9. 26 24
      src/typing/typeloadCheck.ml
  10. 17 13
      src/typing/typeloadFields.ml
  11. 3 2
      src/typing/typeloadModule.ml
  12. 4 4
      src/typing/typer.ml
  13. 2 2
      tests/misc/projects/Issue2969/compile-fail.hxml.stderr
  14. 2 2
      tests/misc/projects/Issue3361/compile1-fail.hxml.stderr
  15. 2 2
      tests/misc/projects/Issue3361/compile2-fail.hxml.stderr
  16. 2 2
      tests/misc/projects/Issue3417/compile-fail.hxml.stderr
  17. 2 2
      tests/misc/projects/Issue4378/compile-fail.hxml.stderr
  18. 3 5
      tests/misc/projects/Issue6584/pretty-fail.hxml.stderr
  19. 2 1
      tests/misc/projects/Issue6796/pretty-fail.hxml.stderr
  20. 0 1
      tests/misc/projects/Issue8283/compile-fail.hxml.stderr
  21. 1 1
      tests/misc/projects/Issue8828/compile2-fail.hxml.stderr
  22. 1 1
      tests/misc/projects/Issue8828/compile3-fail.hxml.stderr
  23. 1 1
      tests/misc/projects/Issue9619/missing-abstract-on-class/compile-fail.hxml.stderr
  24. 11 0
      tests/misc/projects/IssueXXXXX/Main.hx
  25. 19 0
      tests/misc/projects/IssueXXXXX/Main2.hx
  26. 22 0
      tests/misc/projects/IssueXXXXX/Main3.hx
  27. 1 0
      tests/misc/projects/IssueXXXXX/compile-fail.hxml
  28. 2 0
      tests/misc/projects/IssueXXXXX/compile-fail.hxml.stderr
  29. 1 0
      tests/misc/projects/IssueXXXXX/compile2-fail.hxml
  30. 4 0
      tests/misc/projects/IssueXXXXX/compile2-fail.hxml.stderr
  31. 1 0
      tests/misc/projects/IssueXXXXX/compile3-fail.hxml
  32. 12 0
      tests/misc/projects/IssueXXXXX/compile3-fail.hxml.stderr
  33. 1 1
      tests/unit/src/unit/issues/Issue3714.hx

+ 2 - 2
src/compiler/compiler.ml

@@ -430,8 +430,8 @@ with
 			ctx.has_error <- false;
 			ctx.has_error <- false;
 			ctx.messages <- [];
 			ctx.messages <- [];
 		end else begin
 		end else begin
-			error ctx (Printf.sprintf "You cannot access the %s package while %s (for %s)" pack (if pf = "macro" then "in a macro" else "targeting " ^ pf) (s_type_path m) ) p;
-			List.iter (error ~depth:1 ctx (Error.compl_msg "referenced here")) (List.rev pl);
+			let sub = List.map (fun p -> Error.make_error ~depth:1 (Error.Custom (Error.compl_msg "referenced here")) p) pl in
+			error_ext ctx (Error.make_error (Error.Custom (Printf.sprintf "You cannot access the %s package while %s (for %s)" pack (if pf = "macro" then "in a macro" else "targeting " ^ pf) (s_type_path m))) ~sub p)
 		end
 		end
 	| Error.Error err ->
 	| Error.Error err ->
 		error_ext ctx err
 		error_ext ctx err

+ 3 - 3
src/filters/localStatic.ml

@@ -1,4 +1,3 @@
-open Common
 open Type
 open Type
 open Typecore
 open Typecore
 open Error
 open Error
@@ -14,8 +13,9 @@ let promote_local_static lsctx run v eo =
 	let c = lsctx.ctx.c.curclass in
 	let c = lsctx.ctx.c.curclass in
 	begin try
 	begin try
 		let cf = PMap.find name c.cl_statics in
 		let cf = PMap.find name c.cl_statics in
-		display_error lsctx.ctx.com (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name) v.v_pos;
-		raise_typing_error ~depth:1 "Conflicting field was found here" cf.cf_name_pos;
+		raise_typing_error_ext (make_error (Custom (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name)) ~sub:[
+			make_error ~depth:1 (Custom "Conflicting field was found here") cf.cf_name_pos
+		] v.v_pos);
 	with Not_found ->
 	with Not_found ->
 		let cf = mk_field name ~static:true v.v_type v.v_pos v.v_pos in
 		let cf = mk_field name ~static:true v.v_type v.v_pos v.v_pos in
 		cf.cf_meta <- v.v_meta;
 		cf.cf_meta <- v.v_meta;

+ 3 - 2
src/optimization/analyzerTexpr.ml

@@ -1250,8 +1250,9 @@ module Purity = struct
 				begin try
 				begin try
 					apply_to_class com c
 					apply_to_class com c
 				with Purity_conflict(impure,p) ->
 				with Purity_conflict(impure,p) ->
-					com.error "Impure field overrides/implements field which was explicitly marked as @:pure" impure.pn_field.cf_pos;
-					Error.raise_typing_error ~depth:1 (Error.compl_msg "Pure field is here") p;
+					Error.raise_typing_error_ext (Error.make_error (Custom "Impure field overrides/implements field which was explicitly marked as @:pure") ~sub:[
+						Error.make_error ~depth:1 (Custom (Error.compl_msg "Pure field is here")) p
+					] impure.pn_field.cf_pos)
 				end
 				end
 			| _ -> ()
 			| _ -> ()
 		) com.types;
 		) com.types;

+ 3 - 3
src/optimization/inlineConstructors.ml

@@ -131,9 +131,9 @@ let inline_constructors ctx original_e =
 			| IOKCtor(ioc) ->
 			| IOKCtor(ioc) ->
 				List.iter (fun v -> if v.v_id < 0 then cancel_v v p) io.io_dependent_vars;
 				List.iter (fun v -> if v.v_id < 0 then cancel_v v p) io.io_dependent_vars;
 				if ioc.ioc_forced then begin
 				if ioc.ioc_forced then begin
-					(* TODO construct error with sub *)
-					display_error ctx.com "Forced inline constructor could not be inlined" io.io_pos;
-					display_error ~depth:1 ctx.com (compl_msg "Cancellation happened here") p;
+					display_error_ext ctx.com (make_error (Custom "Forced inline constructor could not be inlined") ~sub:([
+						(make_error ~depth:1 (Custom (compl_msg "Cancellation happened here")) p)
+					]) io.io_pos);
 				end
 				end
 			| _ -> ()
 			| _ -> ()
 		end
 		end

+ 10 - 12
src/typing/callUnification.ml

@@ -14,7 +14,6 @@ let unify_call_args ctx el args r callp inline force_inline in_overload =
 		let msg = ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'") in
 		let msg = ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'") in
 		let e = match e.err_message with
 		let e = match e.err_message with
 			| Unify l -> { e with err_message = Unify (l @ [(Unify_custom msg)])}
 			| Unify l -> { e with err_message = Unify (l @ [(Unify_custom msg)])}
-			| Custom parent -> { e with err_message = Custom (parent ^ "\n" ^ msg)}
 			| _ -> { e with err_sub = (make_error (Custom (compl_msg msg)) e.err_pos) :: e.err_sub }
 			| _ -> { e with err_sub = (make_error (Custom (compl_msg msg)) e.err_pos) :: e.err_sub }
 		in
 		in
 		raise_error { e with err_message = (Call_error (Could_not_unify e.err_message)) }
 		raise_error { e with err_message = (Call_error (Could_not_unify e.err_message)) }
@@ -415,9 +414,8 @@ let unify_field_call ctx fa el_typed el p inline =
 							p
 							p
 						) :: acc
 						) :: acc
 					) [] failures in
 					) [] failures in
-
-					display_error_ext ctx.com (make_error ~sub (Custom "Could not find a suitable overload, reasons follow") p);
-					raise_typing_error_ext (make_error ~depth:1 (Custom "End of overload failure reasons") p)
+					let sub = (make_error ~depth:1 (Custom "End of overload failure reasons") p) :: sub in
+					raise_typing_error_ext (make_error ~sub (Custom "Could not find a suitable overload, reasons follow") p)
 				| Some err ->
 				| Some err ->
 					raise_typing_error_ext err
 					raise_typing_error_ext err
 			end
 			end
@@ -429,12 +427,11 @@ let unify_field_call ctx fa el_typed el p inline =
 				maybe_check_access fcc.fc_field;
 				maybe_check_access fcc.fc_field;
 				commit_delayed_display fcc
 				commit_delayed_display fcc
 			| fcc :: l ->
 			| fcc :: l ->
-				(* TODO construct error with sub *)
-				display_error ctx.com "Ambiguous overload, candidates follow" p;
 				let st = s_type (print_context()) in
 				let st = s_type (print_context()) in
-				List.iter (fun fcc ->
-					display_error ~depth:1 ctx.com (compl_msg (st fcc.fc_type)) fcc.fc_field.cf_name_pos;
-				) (fcc :: l);
+				let sub = List.map (fun fcc ->
+					make_error ~depth:1 (Custom (compl_msg (st fcc.fc_type))) fcc.fc_field.cf_name_pos
+				) (fcc :: l) in
+				display_error_ext ctx.com (make_error (Custom "Ambiguous overload, candidates follow") ~sub:(List.rev sub) p);
 				commit_delayed_display fcc
 				commit_delayed_display fcc
 		end else begin match List.rev candidates with
 		end else begin match List.rev candidates with
 			| [] -> fail()
 			| [] -> fail()
@@ -513,9 +510,10 @@ object(self)
 			let ep = err.err_pos in
 			let ep = err.err_pos in
 			(* display additional info in the case the error is not part of our original call *)
 			(* display additional info in the case the error is not part of our original call *)
 			if ep.pfile <> p.pfile || ep.pmax < p.pmin || ep.pmin > p.pmax then begin
 			if ep.pfile <> p.pfile || ep.pmax < p.pmin || ep.pmin > p.pmax then begin
-				old (if (ep = null_pos) then { err with err_pos = p } else err);
-				(* TODO add as sub for above error *)
-				if ep <> null_pos then old (make_error ~depth:(err.err_depth+1) (Custom (compl_msg "Called from macro here")) p);
+				if ep = null_pos then
+					old { err with err_pos = p }
+				else
+					old { err with err_sub = (make_error ~depth:(err.err_depth+1) (Custom (compl_msg "Called from macro here")) p) :: err.err_sub }
 			end else
 			end else
 				old err;
 				old err;
 		);
 		);

+ 3 - 2
src/typing/fields.ml

@@ -221,8 +221,9 @@ let field_access ctx mode f fh e pfield =
 			if bypass_accessor then (
 			if bypass_accessor then (
 				(match e.eexpr with TLocal _ when Common.defined ctx.com Define.Haxe3Compat -> warning ctx WTemp "Field set has changed here in Haxe 4: call setter explicitly to keep Haxe 3.x behaviour" pfield | _ -> ());
 				(match e.eexpr with TLocal _ when Common.defined ctx.com Define.Haxe3Compat -> warning ctx WTemp "Field set has changed here in Haxe 4: call setter explicitly to keep Haxe 3.x behaviour" pfield | _ -> ());
 				if not (is_physical_field f) then begin
 				if not (is_physical_field f) then begin
-					display_error ctx.com "This field cannot be accessed because it is not a real variable" pfield;
-					display_error ctx.com "Add @:isVar here to enable it" f.cf_pos;
+					display_error_ext ctx.com (make_error (Custom "This field cannot be accessed because it is not a real variable") ~sub:[
+						make_error ~depth:1 (Custom "Add @:isVar here to enable it") f.cf_pos
+					] pfield);
 				end;
 				end;
 				normal false
 				normal false
 			)
 			)

+ 6 - 6
src/typing/generic.ml

@@ -355,9 +355,9 @@ let build_generic_class ctx c p tl =
 							| None ->
 							| None ->
 								begin match cf_old.cf_kind with
 								begin match cf_old.cf_kind with
 									| Method _ when not (has_class_flag c CInterface) && not (has_class_flag c CExtern) && not (has_class_field_flag cf_old CfAbstract) ->
 									| Method _ when not (has_class_flag c CInterface) && not (has_class_flag c CExtern) && not (has_class_field_flag cf_old CfAbstract) ->
-										(* TODO use sub error *)
-										display_error ctx.com (Printf.sprintf "Field %s has no expression (possible typing order issue)" cf_new.cf_name) cf_new.cf_pos;
-										display_error ctx.com (Printf.sprintf "While building %s" (s_type_path cg.cl_path)) p;
+										display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Field %s has no expression (possible typing order issue)" cf_new.cf_name)) ~sub:([
+											(make_error ~depth:1 (Custom (compl_msg (Printf.sprintf "While building %s" (s_type_path cg.cl_path)))) p)
+										]) cf_new.cf_pos);
 									| _ ->
 									| _ ->
 										()
 										()
 								end
 								end
@@ -498,9 +498,9 @@ let type_generic_function ctx fa fcc with_type p =
 			ignore(follow cf.cf_type);
 			ignore(follow cf.cf_type);
 			let rec check e = match e.eexpr with
 			let rec check e = match e.eexpr with
 				| TNew({cl_kind = KTypeParameter _} as c,_,_) when not (TypeloadCheck.is_generic_parameter ctx c) ->
 				| TNew({cl_kind = KTypeParameter _} as c,_,_) when not (TypeloadCheck.is_generic_parameter ctx c) ->
-					(* TODO use sub error *)
-					display_error ctx.com "Only generic type parameters can be constructed" e.epos;
-					display_error ctx.com "While specializing this call" p;
+					display_error_ext ctx.com (make_error (Custom "Only generic type parameters can be constructed") ~sub:([
+						(make_error ~depth:1 (Custom (compl_msg "While specializing this call")) p)
+					]) e.epos);
 				| _ ->
 				| _ ->
 					Type.iter check e
 					Type.iter check e
 			in
 			in

+ 16 - 16
src/typing/typeload.ml

@@ -55,18 +55,18 @@ let check_field_access ctx cff =
 			try
 			try
 				let _,p2 = List.find (fun (access',_) -> access = access') acc in
 				let _,p2 = List.find (fun (access',_) -> access = access') acc in
 				if p1 <> null_pos && p2 <> null_pos then begin
 				if p1 <> null_pos && p2 <> null_pos then begin
-					(* TODO error with sub *)
-					display_error ctx.com (Printf.sprintf "Duplicate access modifier %s" (Ast.s_access access)) p1;
-					display_error ~depth:1 ctx.com (compl_msg "Previously defined here") p2;
+					display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Duplicate access modifier %s" (Ast.s_access access))) ~sub:([
+						(make_error ~depth:1 (Custom (compl_msg "Previously defined here")) p2);
+					]) p1);
 				end;
 				end;
 				loop p1 acc l
 				loop p1 acc l
 			with Not_found -> match access with
 			with Not_found -> match access with
 				| APublic | APrivate ->
 				| APublic | APrivate ->
 					begin try
 					begin try
 						let _,p2 = List.find (fun (access',_) -> match access' with APublic | APrivate -> true | _ -> false) acc in
 						let _,p2 = List.find (fun (access',_) -> match access' with APublic | APrivate -> true | _ -> false) acc in
-						(* TODO error with sub *)
-						display_error ctx.com (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1;
-						display_error ~depth:1 ctx.com (compl_msg "Conflicts with this") p2;
+						display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access))) ~sub:([
+							(make_error ~depth:1 (Custom (compl_msg "Conflicts with this")) p2);
+						]) p1);
 						loop p1 acc l
 						loop p1 acc l
 					with Not_found ->
 					with Not_found ->
 						loop p1 ((access,p1) :: acc) l
 						loop p1 ((access,p1) :: acc) l
@@ -244,10 +244,10 @@ let is_redefined ctx cf1 fields p =
 		let cf2 = PMap.find cf1.cf_name fields in
 		let cf2 = PMap.find cf1.cf_name fields in
 		let st = s_type (print_context()) in
 		let st = s_type (print_context()) in
 		if not (type_iseq cf1.cf_type cf2.cf_type) then begin
 		if not (type_iseq cf1.cf_type cf2.cf_type) then begin
-			(* TODO construct error with sub? *)
-			display_error ctx.com ("Cannot redefine field " ^ cf1.cf_name ^ " with different type") p;
-			display_error ctx.com ("First type was " ^ (st cf1.cf_type)) cf1.cf_pos;
-			raise_typing_error ("Second type was " ^ (st cf2.cf_type)) cf2.cf_pos
+			raise_typing_error_ext (make_error (Custom ("Cannot redefine field " ^ cf1.cf_name ^ " with different type")) ~sub:([
+				(make_error ~depth:1 (Custom (compl_msg ("Second type was " ^ (st cf2.cf_type)))) cf2.cf_pos);
+				(make_error ~depth:1 (Custom (compl_msg ("First type was " ^ (st cf1.cf_type)))) cf1.cf_pos);
+			]) p)
 		end else
 		end else
 			true
 			true
 	with Not_found ->
 	with Not_found ->
@@ -844,9 +844,9 @@ let init_core_api ctx c =
 				| Invalid_argument _ ->
 				| Invalid_argument _ ->
 					raise_typing_error "Type parameters must have the same number of constraints as core type" c.cl_pos
 					raise_typing_error "Type parameters must have the same number of constraints as core type" c.cl_pos
 				| Unify_error l ->
 				| Unify_error l ->
-					(* TODO send as one call with sub errors *)
-					display_error ctx.com ("Type parameter " ^ ttp2.ttp_name ^ " has different constraint than in core type") c.cl_pos;
-					display_error ctx.com (error_msg (Unify l)) c.cl_pos;
+					display_error_ext ctx.com (make_error (Custom ("Type parameter " ^ ttp2.ttp_name ^ " has different constraint than in core type")) ~sub:([
+						(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) c.cl_pos);
+					]) c.cl_pos);
 		) ccore.cl_params c.cl_params;
 		) ccore.cl_params c.cl_params;
 	with Invalid_argument _ ->
 	with Invalid_argument _ ->
 		raise_typing_error "Class must have the same number of type parameters as core type" c.cl_pos
 		raise_typing_error "Class must have the same number of type parameters as core type" c.cl_pos
@@ -859,9 +859,9 @@ let init_core_api ctx c =
 		(try
 		(try
 			type_eq EqCoreType (apply_params ccore.cl_params (extract_param_types c.cl_params) f.cf_type) f2.cf_type
 			type_eq EqCoreType (apply_params ccore.cl_params (extract_param_types c.cl_params) f.cf_type) f2.cf_type
 		with Unify_error l ->
 		with Unify_error l ->
-			(* TODO send as one call with sub errors *)
-			display_error ctx.com ("Field " ^ f.cf_name ^ " has different type than in core type") p;
-			display_error ctx.com (error_msg (Unify l)) p);
+			display_error_ext ctx.com (make_error (Custom ("Field " ^ f.cf_name ^ " has different type than in core type")) ~sub:([
+				(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) p);
+			]) p));
 		if (has_class_field_flag f2 CfPublic) <> (has_class_field_flag f CfPublic) then raise_typing_error ("Field " ^ f.cf_name ^ " has different visibility than core type") p;
 		if (has_class_field_flag f2 CfPublic) <> (has_class_field_flag f CfPublic) then raise_typing_error ("Field " ^ f.cf_name ^ " has different visibility than core type") p;
 		(match f2.cf_doc with
 		(match f2.cf_doc with
 		| None -> f2.cf_doc <- f.cf_doc
 		| None -> f2.cf_doc <- f.cf_doc

+ 26 - 24
src/typing/typeloadCheck.ml

@@ -132,9 +132,9 @@ let copy_meta meta_src meta_target sl =
 
 
 let check_native_name_override ctx child base =
 let check_native_name_override ctx child base =
 	let error base_pos child_pos =
 	let error base_pos child_pos =
-		(* TODO construct error *)
-		display_error ctx.com ("Field " ^ child.cf_name ^ " has different @:native value than in superclass") child_pos;
-		display_error ~depth:1 ctx.com (compl_msg "Base field is defined here") base_pos
+		display_error_ext ctx.com (make_error (Custom ("Field " ^ child.cf_name ^ " has different @:native value than in superclass")) ~sub:([
+			(make_error ~depth:1 (Custom (compl_msg "Base field is defined here")) base_pos)
+		]) child_pos);
 	in
 	in
 	try
 	try
 		let child_name, child_pos = Naming.get_native_name child.cf_meta in
 		let child_name, child_pos = Naming.get_native_name child.cf_meta in
@@ -189,10 +189,10 @@ let check_override_field ctx p rctx =
 		valid_redefinition rctx.map rctx.map rctx.cf_new rctx.cf_new.cf_type rctx.cf_old rctx.t_old;
 		valid_redefinition rctx.map rctx.map rctx.cf_new rctx.cf_new.cf_type rctx.cf_old rctx.t_old;
 	with
 	with
 		Unify_error l ->
 		Unify_error l ->
-			(* TODO construct error with sub *)
-			display_error ctx.com ("Field " ^ i ^ " overrides parent class with different or incomplete type") p;
-			display_error ~depth:1 ctx.com (compl_msg "Base field is defined here") rctx.cf_old.cf_name_pos;
-			display_error ~depth:1 ctx.com (compl_msg (error_msg (Unify l))) p
+			display_error_ext ctx.com (make_error (Custom ("Field " ^ i ^ " overrides parent class with different or incomplete type")) ~sub:([
+				(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) p);
+				(make_error ~depth:1 (Custom (compl_msg "Base field is defined here")) rctx.cf_old.cf_name_pos);
+			]) p)
 
 
 let find_override_field ctx c_new cf_new c_old tl get_super_field is_overload p =
 let find_override_field ctx c_new cf_new c_old tl get_super_field is_overload p =
 	let i = cf_new.cf_name in
 	let i = cf_new.cf_name in
@@ -400,10 +400,10 @@ module Inheritance = struct
 					with
 					with
 						Unify_error l ->
 						Unify_error l ->
 							if not ((has_class_flag c CExtern)) then begin
 							if not ((has_class_flag c CExtern)) then begin
-								(* TODO construct error with sub *)
-								display_error com ("Field " ^ f.cf_name ^ " has different type than in " ^ s_type_path intf.cl_path) p;
-								display_error ~depth:1 com (compl_msg "Interface field is defined here") f.cf_pos;
-								display_error ~depth:1 com (compl_msg (error_msg (Unify l))) p;
+								display_error_ext com (make_error (Custom ("Field " ^ f.cf_name ^ " has different type than in " ^ s_type_path intf.cl_path)) ~sub:([
+									(make_error ~depth:1 (Custom (compl_msg (error_msg (Unify l)))) p);
+									(make_error ~depth:1 (Custom (compl_msg "Interface field is defined here")) f.cf_name_pos);
+								]) p)
 							end
 							end
 				)
 				)
 			with Not_found ->
 			with Not_found ->
@@ -490,20 +490,19 @@ module Inheritance = struct
 			let display = ctx.com.display_information in
 			let display = ctx.com.display_information in
 			display.module_diagnostics <- MissingFields diag :: display.module_diagnostics
 			display.module_diagnostics <- MissingFields diag :: display.module_diagnostics
 		| l ->
 		| l ->
-			let singular = match l with [_] -> true | _ -> false in
-			display_error ctx.com (Printf.sprintf "This class extends abstract class %s but doesn't implement the following method%s" (s_type_path csup.cl_path) (if singular then "" else "s")) c.cl_name_pos;
-			(* TODO sub error ? *)
-			display_error ctx.com (Printf.sprintf "Implement %s or make %s abstract as well" (if singular then "it" else "them") (s_type_path c.cl_path)) c.cl_name_pos;
 			let pctx = print_context() in
 			let pctx = print_context() in
-			List.iter (fun (cf,_) ->
+			let sub = List.map (fun (cf,_) ->
 				let s = match follow cf.cf_type with
 				let s = match follow cf.cf_type with
 					| TFun(tl,tr) ->
 					| TFun(tl,tr) ->
 						String.concat ", " (List.map (fun (n,o,t) -> Printf.sprintf "%s:%s" n (s_type pctx t)) tl)
 						String.concat ", " (List.map (fun (n,o,t) -> Printf.sprintf "%s:%s" n (s_type pctx t)) tl)
 					| t ->
 					| t ->
 						s_type pctx t
 						s_type pctx t
 				in
 				in
-				display_error ~depth:1 ctx.com (compl_msg (Printf.sprintf "%s(%s)" cf.cf_name s)) cf.cf_name_pos
-			) (List.rev !missing)
+				make_error ~depth:1 (Custom (compl_msg (Printf.sprintf "%s(%s)" cf.cf_name s))) cf.cf_name_pos
+			) !missing in
+			let singular = match l with [_] -> true | _ -> false in
+			let sub = [make_error (Custom (Printf.sprintf "Implement %s or make %s abstract as well" (if singular then "it" else "them") (s_type_path c.cl_path))) ~sub c.cl_name_pos] in
+			display_error_ext ctx.com (make_error (Custom (Printf.sprintf "This class extends abstract class %s but doesn't implement the following method%s" (s_type_path csup.cl_path) (if singular then "" else "s"))) ~sub c.cl_name_pos)
 
 
 	let set_heritance ctx c herits p =
 	let set_heritance ctx c herits p =
 		let is_lib = Meta.has Meta.LibType c.cl_meta in
 		let is_lib = Meta.has Meta.LibType c.cl_meta in
@@ -644,10 +643,13 @@ let check_final_vars ctx e =
 				Type.iter find_inits e
 				Type.iter find_inits e
 		in
 		in
 		find_inits e;
 		find_inits e;
-		if Hashtbl.length final_vars > 0 then
-			display_error ctx.com "Some final fields are uninitialized in this class" ctx.c.curclass.cl_name_pos;
-		DynArray.iter (fun (c,cf) ->
-			if Hashtbl.mem final_vars cf.cf_name then
-				display_error ~depth:1 ctx.com "Uninitialized field" cf.cf_name_pos
-		) ordered_fields
+		if Hashtbl.length final_vars > 0 then begin
+			let sub = List.filter_map (fun (c,cf) ->
+				if Hashtbl.mem final_vars cf.cf_name then
+					Some (make_error ~depth:1 (Custom "Uninitialized field") cf.cf_name_pos)
+				else
+					None
+			) (DynArray.to_list ordered_fields) in
+			display_error_ext ctx.com (make_error (Custom "Some final fields are uninitialized in this class") ~sub:(List.rev sub) ctx.c.curclass.cl_name_pos)
+		end
 	end
 	end

+ 17 - 13
src/typing/typeloadFields.ml

@@ -313,8 +313,9 @@ let build_enum_abstract ctx c a fields p =
 				| VUnknown ->
 				| VUnknown ->
 					()
 					()
 				| VPublic(access,p2) | VPrivate(access,p2) ->
 				| VPublic(access,p2) | VPrivate(access,p2) ->
-					display_error ctx.com (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1;
-					display_error ~depth:1 ctx.com "Conflicts with this" p2;
+					display_error_ext ctx.com (make_error (Custom (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access))) ~sub:[
+						make_error ~depth:1 (Custom (compl_msg "Conflicts with this")) p2;
+					] p1)
 			in
 			in
 			let rec loop visibility acc = match acc with
 			let rec loop visibility acc = match acc with
 				| (AExtern,p) :: acc ->
 				| (AExtern,p) :: acc ->
@@ -551,8 +552,9 @@ let create_typer_context_for_field ctx cctx fctx cff =
 		else if fctx.is_inline then
 		else if fctx.is_inline then
 			invalid_modifier_combination fctx ctx.com fctx "abstract" "inline" (pos cff.cff_name)
 			invalid_modifier_combination fctx ctx.com fctx "abstract" "inline" (pos cff.cff_name)
 		else if not (has_class_flag c CAbstract) then begin
 		else if not (has_class_flag c CAbstract) then begin
-			display_error ctx.com "This class should be declared abstract because it has at least one abstract field" c.cl_name_pos;
-			display_error ctx.com "First abstract field was here" (pos cff.cff_name);
+			display_error_ext ctx.com (make_error (Custom "This class should be declared abstract because it has at least one abstract field") ~sub:[
+				make_error ~depth:1 (Custom (compl_msg "First abstract field was here")) (pos cff.cff_name);
+			] c.cl_name_pos);
 			add_class_flag c CAbstract;
 			add_class_flag c CAbstract;
 		end;
 		end;
 	end;
 	end;
@@ -1399,8 +1401,9 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p =
 				try
 				try
 					(match f2.cf_kind with
 					(match f2.cf_kind with
 						| Method MethMacro ->
 						| Method MethMacro ->
-							display_error ctx.com (f2.cf_name ^ ": Macro methods cannot be used as property accessor") p;
-							display_error ~depth:1 ctx.com (compl_msg (f2.cf_name ^ ": Accessor method is here")) f2.cf_pos;
+							display_error_ext ctx.com (make_error (Custom (f2.cf_name ^ ": Macro methods cannot be used as property accessor")) ~sub:[
+								make_error ~depth:1 (Custom (compl_msg (f2.cf_name ^ ": Accessor method is here"))) f2.cf_pos;
+							] p);
 						| _ -> ());
 						| _ -> ());
 					unify_raise t2 t f2.cf_pos;
 					unify_raise t2 t f2.cf_pos;
 					if (fctx.is_abstract_member && not (has_class_field_flag f2 CfImpl)) || (has_class_field_flag f2 CfImpl && not (fctx.is_abstract_member)) then
 					if (fctx.is_abstract_member && not (has_class_field_flag f2 CfImpl)) || (has_class_field_flag f2 CfImpl && not (fctx.is_abstract_member)) then
@@ -1556,8 +1559,9 @@ let check_overload ctx f fs is_extern_class =
 				Overloads.same_overload_args f.cf_type f2.cf_type f f2
 				Overloads.same_overload_args f.cf_type f2.cf_type f f2
 			) fs
 			) fs
 		in
 		in
-		display_error ctx.com ("Another overloaded field of same signature was already declared : " ^ f.cf_name) f.cf_pos;
-		display_error ~depth:1 ctx.com (compl_msg "The second field is declared here") f2.cf_pos;
+		display_error_ext ctx.com (make_error (Custom ("Another overloaded field of same signature was already declared : " ^ f.cf_name)) ~sub:[
+			make_error ~depth:1 (Custom (compl_msg "The second field is declared here")) f2.cf_pos;
+		] f.cf_pos);
 		false
 		false
 	with Not_found -> try
 	with Not_found -> try
 		if ctx.com.platform <> Jvm || is_extern_class then raise Not_found;
 		if ctx.com.platform <> Jvm || is_extern_class then raise Not_found;
@@ -1570,12 +1574,11 @@ let check_overload ctx f fs is_extern_class =
 		in
 		in
 		(* Don't bother checking this on externs and assume the users know what they're doing (issue #11131) *)
 		(* Don't bother checking this on externs and assume the users know what they're doing (issue #11131) *)
 		if has_class_field_flag f CfExtern && has_class_field_flag f2 CfExtern then raise Not_found;
 		if has_class_field_flag f CfExtern && has_class_field_flag f2 CfExtern then raise Not_found;
-		display_error ctx.com (
+		display_error_ext ctx.com (make_error (Custom (
 			"Another overloaded field of similar signature was already declared : " ^
 			"Another overloaded field of similar signature was already declared : " ^
 			f.cf_name ^
 			f.cf_name ^
 			"\nThe signatures are different in Haxe, but not in the target language"
 			"\nThe signatures are different in Haxe, but not in the target language"
-		) f.cf_pos;
-		display_error ~depth:1 ctx.com (compl_msg "The second field is declared here") f2.cf_pos;
+		)) ~sub:[make_error ~depth:1 (Custom (compl_msg "The second field is declared here")) f2.cf_pos] f.cf_pos);
 		false
 		false
 	with Not_found ->
 	with Not_found ->
 		true
 		true
@@ -1767,8 +1770,9 @@ let init_class ctx_c cctx c p herits fields =
 				let display = com.display_information in
 				let display = com.display_information in
 				display.module_diagnostics <- MissingFields diag :: display.module_diagnostics
 				display.module_diagnostics <- MissingFields diag :: display.module_diagnostics
 			end else begin
 			end else begin
-				display_error com "This class has uninitialized final vars, which requires a constructor" p;
-				display_error com "Example of an uninitialized final var" cf.cf_name_pos;
+				display_error_ext com (make_error (Custom "This class has uninitialized final vars, which requires a constructor") ~sub:[
+					make_error ~depth:1 (Custom "Example of an uninitialized final var") cf.cf_name_pos;
+				] p);
 			end
 			end
 		| _ ->
 		| _ ->
 			()
 			()

+ 3 - 2
src/typing/typeloadModule.ml

@@ -77,8 +77,9 @@ module ModuleLevel = struct
 		let check_name name meta also_statics p =
 		let check_name name meta also_statics p =
 			DeprecationCheck.check_is com ctx_m.m.curmod meta [] name meta p;
 			DeprecationCheck.check_is com ctx_m.m.curmod meta [] name meta p;
 			let error prev_pos =
 			let error prev_pos =
-				display_error com ("Name " ^ name ^ " is already defined in this module") p;
-				raise_typing_error ~depth:1 (compl_msg "Previous declaration here") prev_pos;
+				raise_typing_error_ext (make_error (Custom ("Name " ^ name ^ " is already defined in this module")) ~sub:[
+					make_error ~depth:1 (Custom (compl_msg "Previous declaration here")) prev_pos
+				] p);
 			in
 			in
 			DynArray.iter (fun t2 ->
 			DynArray.iter (fun t2 ->
 				if snd (t_path t2) = name then error (t_infos t2).mt_name_pos
 				if snd (t_path t2) = name then error (t_infos t2).mt_name_pos

+ 4 - 4
src/typing/typer.ml

@@ -1159,8 +1159,9 @@ and type_map_declaration ctx e1 el with_type p =
 	let check_key e_key =
 	let check_key e_key =
 		try
 		try
 			let p = Hashtbl.find keys e_key.eexpr in
 			let p = Hashtbl.find keys e_key.eexpr in
-			display_error ctx.com "Duplicate key" e_key.epos;
-			raise_typing_error ~depth:1 (compl_msg "Previously defined here") p
+			raise_typing_error_ext (make_error (Custom "Duplicate key") ~sub:[
+				make_error ~depth:1 (Custom (compl_msg "Previously defined here")) p
+			] e_key.epos);
 		with Not_found ->
 		with Not_found ->
 			begin match e_key.eexpr with
 			begin match e_key.eexpr with
 			| TConst _ -> Hashtbl.add keys e_key.eexpr e_key.epos;
 			| TConst _ -> Hashtbl.add keys e_key.eexpr e_key.epos;
@@ -1431,8 +1432,7 @@ and type_array_decl ctx el with_type p =
 			if !allow_array_dynamic || ctx.f.untyped || ignore_error ctx.com then
 			if !allow_array_dynamic || ctx.f.untyped || ignore_error ctx.com then
 				t_dynamic
 				t_dynamic
 			else begin
 			else begin
-				display_error ctx.com "Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>" err.err_pos;
-				raise_error err
+				raise_typing_error_ext (make_error (Custom "Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>") ~sub:[err] err.err_pos)
 			end
 			end
 		in
 		in
 		mk (TArrayDecl el) (ctx.t.tarray t) p
 		mk (TArrayDecl el) (ctx.t.tarray t) p

+ 2 - 2
tests/misc/projects/Issue2969/compile-fail.hxml.stderr

@@ -1,5 +1,5 @@
 Main.hx:10: characters 18-19 : Field a has different type than in A
 Main.hx:10: characters 18-19 : Field a has different type than in A
-Main.hx:2: characters 2-29 : ... Interface field is defined here
+Main.hx:2: characters 18-19 : ... Interface field is defined here
 Main.hx:10: characters 18-19 : ... error: Int should be String
 Main.hx:10: characters 18-19 : ... error: Int should be String
 Main.hx:10: characters 18-19 : ... have: (...) -> Int
 Main.hx:10: characters 18-19 : ... have: (...) -> Int
-Main.hx:10: characters 18-19 : ... want: (...) -> String
+Main.hx:10: characters 18-19 : ... want: (...) -> String

+ 2 - 2
tests/misc/projects/Issue3361/compile1-fail.hxml.stderr

@@ -1,5 +1,5 @@
 Main.hx:6: characters 13-14 : Field v has different type than in I
 Main.hx:6: characters 13-14 : Field v has different type than in I
-Main.hx:2: characters 2-29 : ... Interface field is defined here
+Main.hx:2: characters 13-14 : ... Interface field is defined here
 Main.hx:6: characters 13-14 : ... error: String should be Dynamic
 Main.hx:6: characters 13-14 : ... error: String should be Dynamic
 Main.hx:6: characters 13-14 : ... have: (Dynamic) -> ...
 Main.hx:6: characters 13-14 : ... have: (Dynamic) -> ...
-Main.hx:6: characters 13-14 : ... want: (String) -> ...
+Main.hx:6: characters 13-14 : ... want: (String) -> ...

+ 2 - 2
tests/misc/projects/Issue3361/compile2-fail.hxml.stderr

@@ -1,5 +1,5 @@
 Main2.hx:6: characters 26-27 : Field f has different type than in I
 Main2.hx:6: characters 26-27 : Field f has different type than in I
-Main2.hx:2: characters 2-44 : ... Interface field is defined here
+Main2.hx:2: characters 26-27 : ... Interface field is defined here
 Main2.hx:6: characters 26-27 : ... error: String should be Dynamic
 Main2.hx:6: characters 26-27 : ... error: String should be Dynamic
 Main2.hx:6: characters 26-27 : ... have: (Dynamic) -> ...
 Main2.hx:6: characters 26-27 : ... have: (Dynamic) -> ...
-Main2.hx:6: characters 26-27 : ... want: (String) -> ...
+Main2.hx:6: characters 26-27 : ... want: (String) -> ...

+ 2 - 2
tests/misc/projects/Issue3417/compile-fail.hxml.stderr

@@ -1,3 +1,3 @@
 Main.hx:6: characters 21-22 : Field f has different type than in I
 Main.hx:6: characters 21-22 : Field f has different type than in I
-Main.hx:2: characters 5-28 : ... Interface field is defined here
-Main.hx:6: characters 21-22 : ... Different number of function arguments
+Main.hx:2: characters 14-15 : ... Interface field is defined here
+Main.hx:6: characters 21-22 : ... Different number of function arguments

+ 2 - 2
tests/misc/projects/Issue4378/compile-fail.hxml.stderr

@@ -1,5 +1,5 @@
 Main.hx:5: characters 18-22 : Field test has different type than in I
 Main.hx:5: characters 18-22 : Field test has different type than in I
-Main.hx:16: characters 2-32 : ... Interface field is defined here
+Main.hx:16: characters 11-15 : ... Interface field is defined here
 Main.hx:5: characters 18-22 : ... error: String should be Dynamic
 Main.hx:5: characters 18-22 : ... error: String should be Dynamic
 Main.hx:5: characters 18-22 : ... have: (Dynamic) -> ...
 Main.hx:5: characters 18-22 : ... have: (Dynamic) -> ...
-Main.hx:5: characters 18-22 : ... want: (String) -> ...
+Main.hx:5: characters 18-22 : ... want: (String) -> ...

+ 3 - 5
tests/misc/projects/Issue6584/pretty-fail.hxml.stderr

@@ -6,9 +6,7 @@
    |
    |
    | This class has uninitialized final vars, which requires a constructor
    | This class has uninitialized final vars, which requires a constructor
 
 
-[ERROR] Main5.hx:2: characters 8-9
-
- 2 |  final v:Int;
-   |        ^
-   | Example of an uninitialized final var
+    2 |  final v:Int;
+      |        ^
+      | Example of an uninitialized final var
 
 

+ 2 - 1
tests/misc/projects/Issue6796/pretty-fail.hxml.stderr

@@ -3,5 +3,6 @@
  3 |         Sys.println(main["foo"]);
  3 |         Sys.println(main["foo"]);
    |                     ^^^^
    |                     ^^^^
    | Array access is not allowed on () -> Unknown<0>
    | Array access is not allowed on () -> Unknown<0>
-   | For function argument 'v'
+
+      | For function argument 'v'
 
 

+ 0 - 1
tests/misc/projects/Issue8283/compile-fail.hxml.stderr

@@ -1,4 +1,3 @@
 Main.hx:9: characters 35-48 : Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>
 Main.hx:9: characters 35-48 : Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>
-Main.hx:9: characters 35-48 : Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>
 Main.hx:9: characters 35-48 : String should be Int
 Main.hx:9: characters 35-48 : String should be Int
 Main.hx:9: characters 35-48 : ... For optional function argument 'arr'
 Main.hx:9: characters 35-48 : ... For optional function argument 'arr'

+ 1 - 1
tests/misc/projects/Issue8828/compile2-fail.hxml.stderr

@@ -1,2 +1,2 @@
 Main2.hx:2: characters 9-16 : Conflicting access modifier public
 Main2.hx:2: characters 9-16 : Conflicting access modifier public
-Main2.hx:2: characters 2-8 : Conflicts with this
+Main2.hx:2: characters 2-8 : ... Conflicts with this

+ 1 - 1
tests/misc/projects/Issue8828/compile3-fail.hxml.stderr

@@ -1,2 +1,2 @@
 Main3.hx:2: characters 10-16 : Conflicting access modifier private
 Main3.hx:2: characters 10-16 : Conflicting access modifier private
-Main3.hx:2: characters 2-9 : Conflicts with this
+Main3.hx:2: characters 2-9 : ... Conflicts with this

+ 1 - 1
tests/misc/projects/Issue9619/missing-abstract-on-class/compile-fail.hxml.stderr

@@ -1,2 +1,2 @@
 Main.hx:1: characters 7-11 : This class should be declared abstract because it has at least one abstract field
 Main.hx:1: characters 7-11 : This class should be declared abstract because it has at least one abstract field
-Main.hx:6: characters 20-36 : First abstract field was here
+Main.hx:6: characters 20-36 : ... First abstract field was here

+ 11 - 0
tests/misc/projects/IssueXXXXX/Main.hx

@@ -0,0 +1,11 @@
+class Main {
+	static function main() {
+		foo();
+	}
+
+	static macro function foo() {
+		return macro {
+			var a:String = 42;
+		};
+	}
+}

+ 19 - 0
tests/misc/projects/IssueXXXXX/Main2.hx

@@ -0,0 +1,19 @@
+import haxe.Constraints;
+
+class Main2 {
+	static function main() {}
+}
+
+class GenericTest<T:(Main & Constructible<String->Void>)> {
+	public function new(){}
+
+	public function someTask():Void {
+		var instance:T = getInstance("foo");
+	}
+
+	@:generic
+	private function getInstance<S:(Main & Constructible<String->Void>)>(arg:String):S {
+		return new S(arg);
+	}
+}
+

+ 22 - 0
tests/misc/projects/IssueXXXXX/Main3.hx

@@ -0,0 +1,22 @@
+class Main3 extends Base {
+	public static function main() {}
+	override function foo(a) "";
+}
+
+private class Base {
+	function foo(a:Int):String return "";
+}
+
+private class Foo {
+	public public var foo:String;
+	public private var bar:String;
+}
+
+typedef Bar = {
+	var bar:String;
+}
+
+typedef Baz = Bar & {
+	var bar:Int;
+	var baz:Int;
+}

+ 1 - 0
tests/misc/projects/IssueXXXXX/compile-fail.hxml

@@ -0,0 +1 @@
+-main Main

+ 2 - 0
tests/misc/projects/IssueXXXXX/compile-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Main.hx:8: characters 4-22 : Int should be String
+Main.hx:3: characters 3-8 : ... Called from macro here

+ 1 - 0
tests/misc/projects/IssueXXXXX/compile2-fail.hxml

@@ -0,0 +1 @@
+-main Main2

+ 4 - 0
tests/misc/projects/IssueXXXXX/compile2-fail.hxml.stderr

@@ -0,0 +1,4 @@
+Main.hx:8: characters 4-22 : Int should be String
+Main.hx:3: characters 3-8 : ... Called from macro here
+Main2.hx:16: characters 10-20 : Only generic type parameters can be constructed
+Main2.hx:11: characters 20-38 : ... While specializing this call

+ 1 - 0
tests/misc/projects/IssueXXXXX/compile3-fail.hxml

@@ -0,0 +1 @@
+-main Main3

+ 12 - 0
tests/misc/projects/IssueXXXXX/compile3-fail.hxml.stderr

@@ -0,0 +1,12 @@
+Main3.hx:11: characters 9-15 : Duplicate access modifier public
+Main3.hx:11: characters 2-8 : ... Previously defined here
+Main3.hx:12: characters 9-16 : Conflicting access modifier private
+Main3.hx:12: characters 2-8 : ... Conflicts with this
+Main3.hx:3: characters 20-23 : Field foo overrides parent class with different or incomplete type
+Main3.hx:7: characters 11-14 : ... Base field is defined here
+Main3.hx:3: characters 20-23 : ... error: Void should be String
+Main3.hx:3: characters 20-23 : ... have: (...) -> Void
+Main3.hx:3: characters 20-23 : ... want: (...) -> String
+Main3.hx:19: lines 19-22 : Cannot redefine field bar with different type
+Main3.hx:20: characters 2-14 : ... First type was Int
+Main3.hx:16: characters 2-17 : ... Second type was String

+ 1 - 1
tests/unit/src/unit/issues/Issue3714.hx

@@ -10,6 +10,6 @@ class Issue3714 extends Test {
 		eq(unit.HelperMacros.typeErrorText(A.f), "Cannot access private field f");
 		eq(unit.HelperMacros.typeErrorText(A.f), "Cannot access private field f");
 		eq(unit.HelperMacros.typeErrorText(@:privateAccess A.f), null);
 		eq(unit.HelperMacros.typeErrorText(@:privateAccess A.f), null);
 		eq(unit.HelperMacros.typeErrorText(@:privateAccess A.f(A.a)), null);
 		eq(unit.HelperMacros.typeErrorText(@:privateAccess A.f(A.a)), null);
-		eq(unit.HelperMacros.typeErrorText(@:privateAccess A.f(@:noPrivateAccess A.a)), "Cannot access private field a\nFor function argument 'a'");
+		eq(unit.HelperMacros.typeErrorText(@:privateAccess A.f(@:noPrivateAccess A.a)), "Cannot access private field a\n... For function argument 'a'");
 	}
 	}
 }
 }