Browse Source

Fix call stack positions (#11065)

* Error reporting: keep stack data available until the very end

* fix stack depth

* Fix 'for function argument' very important messages

Well, except for have/want errors

* Fix error position recovery

* [tests] Add simple test for #11055

* Don't process eval exceptions twice

* [tests] update

* Reduce diff

* Whitespace

* Make childErrors private
Rudy Ges 2 years ago
parent
commit
c212924e80

+ 3 - 3
src/codegen/gencommon/gencommon.ml

@@ -730,9 +730,9 @@ let run_filters_from gen t filters =
 		()
 		()
 
 
 let run_filters gen =
 let run_filters gen =
-	let last_error = gen.gcon.error in
+	let last_error = gen.gcon.located_error in
 	let has_errors = ref false in
 	let has_errors = ref false in
-	gen.gcon.error <- (fun ?(depth=0) msg pos -> has_errors := true; last_error ~depth msg pos);
+	gen.gcon.located_error <- (fun ?(depth=0) msg -> has_errors := true; last_error ~depth msg);
 	(* first of all, we have to make sure that the filters won't trigger a major Gc collection *)
 	(* first of all, we have to make sure that the filters won't trigger a major Gc collection *)
 	let t = Timer.timer ["gencommon_filters"] in
 	let t = Timer.timer ["gencommon_filters"] in
 	(if Common.defined gen.gcon Define.GencommonDebug then debug_mode := true else debug_mode := false);
 	(if Common.defined gen.gcon Define.GencommonDebug then debug_mode := true else debug_mode := false);
@@ -820,7 +820,7 @@ let run_filters gen =
 
 
 	reorder_modules gen;
 	reorder_modules gen;
 	t();
 	t();
-	if !has_errors then raise (Abort("Compilation aborted with errors",null_pos))
+	if !has_errors then abort "Compilation aborted with errors" null_pos
 
 
 (* ******************************************* *)
 (* ******************************************* *)
 (* basic generation module that source code compilation implementations can use *)
 (* basic generation module that source code compilation implementations can use *)

+ 2 - 2
src/codegen/java.ml

@@ -35,9 +35,9 @@ type java_lib_ctx = {
 	is_std : bool;
 	is_std : bool;
 }
 }
 
 
-exception ConversionError of string * pos
+exception ConversionError of located
 
 
-let error s p = raise (ConversionError (s, p))
+let error s p = raise (ConversionError (located s p))
 
 
 let is_haxe_keyword = function
 let is_haxe_keyword = function
 	| "cast" | "extern" | "function" | "in" | "typedef" | "using" | "var" | "untyped" | "inline" -> true
 	| "cast" | "extern" | "function" | "in" | "typedef" | "using" | "var" | "untyped" | "inline" -> true

+ 5 - 3
src/compiler/compilationContext.ml

@@ -68,6 +68,8 @@ let error ctx ?(depth=0) msg p =
 	message ctx (make_compiler_message msg p depth DKCompilerMessage Error);
 	message ctx (make_compiler_message msg p depth DKCompilerMessage Error);
 	ctx.has_error <- true
 	ctx.has_error <- true
 
 
-let located_error ctx ?(depth=0) msg =
-	message ctx (make_compiler_message (extract_located_msg msg) (extract_located_pos msg) depth DKCompilerMessage Error);
-	ctx.has_error <- true
+let located_error ctx ?(depth=0) msg = match (extract_located msg) with
+	| [] -> ()
+	| (msg,p) :: tl ->
+		error ~depth ctx msg p;
+		List.iter (fun (msg,p) -> error ~depth:(depth+1) ctx msg p) tl

+ 3 - 2
src/compiler/compiler.ml

@@ -226,6 +226,7 @@ module Setup = struct
 				()
 				()
 		);
 		);
 		com.error <- error ctx;
 		com.error <- error ctx;
+		com.located_error <- located_error ctx;
 		let filter_messages = (fun keep_errors predicate -> (List.filter (fun cm ->
 		let filter_messages = (fun keep_errors predicate -> (List.filter (fun cm ->
 			(match cm.cm_severity with
 			(match cm.cm_severity with
 			| MessageSeverity.Error -> keep_errors;
 			| MessageSeverity.Error -> keep_errors;
@@ -336,8 +337,8 @@ with
 		()
 		()
 	| Error.Fatal_error (m,depth) ->
 	| Error.Fatal_error (m,depth) ->
 		located_error ~depth ctx m
 		located_error ~depth ctx m
-	| Common.Abort (m,p) ->
-		error ctx m p
+	| Common.Abort msg ->
+		located_error ctx msg
 	| Lexer.Error (m,p) ->
 	| Lexer.Error (m,p) ->
 		error ctx (Lexer.error_msg m) p
 		error ctx (Lexer.error_msg m) p
 	| Parser.Error (m,p) ->
 	| Parser.Error (m,p) ->

+ 3 - 3
src/compiler/displayOutput.ml

@@ -350,8 +350,8 @@ let handle_type_path_exception ctx p c is_import pos =
 			| Some (c,cur_package) ->
 			| Some (c,cur_package) ->
 				let ctx = Typer.create com in
 				let ctx = Typer.create com in
 				DisplayPath.TypePathHandler.complete_type_path_inner ctx p c cur_package is_import
 				DisplayPath.TypePathHandler.complete_type_path_inner ctx p c cur_package is_import
-		end with Common.Abort(msg,p) ->
-			error ctx msg p;
+		end with Common.Abort msg ->
+			located_error ctx msg;
 			None
 			None
 	in
 	in
 	begin match ctx.com.json_out,fields with
 	begin match ctx.com.json_out,fields with
@@ -381,4 +381,4 @@ let emit_diagnostics com =
 let emit_statistics tctx =
 let emit_statistics tctx =
 	let stats = Statistics.collect_statistics tctx [SFFile (DisplayPosition.display_position#get).pfile] true in
 	let stats = Statistics.collect_statistics tctx [SFFile (DisplayPosition.display_position#get).pfile] true in
 	let s = Statistics.Printer.print_statistics stats in
 	let s = Statistics.Printer.print_statistics stats in
-	raise (Completion s)
+	raise (Completion s)

+ 16 - 6
src/context/common.ml

@@ -363,6 +363,7 @@ type context = {
 	(* communication *)
 	(* communication *)
 	mutable print : string -> unit;
 	mutable print : string -> unit;
 	mutable error : ?depth:int -> string -> pos -> unit;
 	mutable error : ?depth:int -> string -> pos -> unit;
+	mutable located_error : ?depth:int -> located -> unit;
 	mutable info : ?depth:int -> string -> pos -> unit;
 	mutable info : ?depth:int -> string -> pos -> unit;
 	mutable warning : ?depth:int -> warning -> Warning.warning_option list list -> string -> pos -> unit;
 	mutable warning : ?depth:int -> warning -> Warning.warning_option list list -> string -> pos -> unit;
 	mutable warning_options : Warning.warning_option list list;
 	mutable warning_options : Warning.warning_option list list;
@@ -414,7 +415,7 @@ type context = {
 	memory_marker : float array;
 	memory_marker : float array;
 }
 }
 
 
-exception Abort of string * pos
+exception Abort of located
 
 
 let ignore_error com =
 let ignore_error com =
 	let b = com.display.dms_error_policy = EPIgnore in
 	let b = com.display.dms_error_policy = EPIgnore in
@@ -835,6 +836,7 @@ let create compilation_step cs version args =
 		warning = (fun ?depth _ _ _ -> die "" __LOC__);
 		warning = (fun ?depth _ _ _ -> die "" __LOC__);
 		warning_options = [];
 		warning_options = [];
 		error = (fun ?depth _ _ -> die "" __LOC__);
 		error = (fun ?depth _ _ -> die "" __LOC__);
+		located_error = (fun ?depth _ -> die "" __LOC__);
 		get_messages = (fun() -> []);
 		get_messages = (fun() -> []);
 		filter_messages = (fun _ -> ());
 		filter_messages = (fun _ -> ());
 		pass_debug_messages = DynArray.create();
 		pass_debug_messages = DynArray.create();
@@ -1022,7 +1024,8 @@ let allow_package ctx s =
 	with Not_found ->
 	with Not_found ->
 		()
 		()
 
 
-let abort ?depth msg p = raise (Abort (msg,p))
+let abort_located ?depth msg = raise (Abort msg)
+let abort ?depth msg p = abort_located ~depth (located msg p)
 
 
 let platform ctx p = ctx.platform = p
 let platform ctx p = ctx.platform = p
 
 
@@ -1227,17 +1230,24 @@ let utf16_to_utf8 str =
 	Buffer.contents b
 	Buffer.contents b
 
 
 let add_diagnostics_message com msg kind sev =
 let add_diagnostics_message com msg kind sev =
-	let p = Globals.extract_located_pos msg in
-	let s = Globals.extract_located_msg msg in
 	if sev = MessageSeverity.Error then com.has_error <- true;
 	if sev = MessageSeverity.Error then com.has_error <- true;
 	let di = com.shared.shared_display_information in
 	let di = com.shared.shared_display_information in
-	di.diagnostics_messages <- (s,p,kind,sev) :: di.diagnostics_messages
+	match (extract_located msg) with
+	| [] -> ()
+	| (s,p) :: [] ->
+		di.diagnostics_messages <- (s,p,kind,sev) :: di.diagnostics_messages
+	| (s,p) :: stack ->
+		(* TODO send full stack data as diagnostics attribute for better handling by editors *)
+		let s = List.fold_left (fun acc (s,p) ->
+			Printf.sprintf "%s%s\n" acc (Lexer.get_error_pos (Printf.sprintf "%s:%d: ") p)
+		) (s ^ "\n") stack in
+		di.diagnostics_messages <- (s,p,kind,sev) :: di.diagnostics_messages
 
 
 let located_display_error com ?(depth = 0) msg =
 let located_display_error com ?(depth = 0) msg =
 	if is_diagnostics com then
 	if is_diagnostics com then
 		add_diagnostics_message com msg MessageKind.DKCompilerMessage MessageSeverity.Error
 		add_diagnostics_message com msg MessageKind.DKCompilerMessage MessageSeverity.Error
 	else
 	else
-		com.error (Globals.extract_located_msg msg) (Globals.extract_located_pos msg) ~depth
+		com.located_error msg ~depth
 
 
 let display_error com ?(depth = 0) msg p =
 let display_error com ?(depth = 0) msg p =
 	located_display_error com ~depth (Globals.located msg p)
 	located_display_error com ~depth (Globals.located msg p)

+ 9 - 5
src/core/error.ml

@@ -105,6 +105,7 @@ module BetterErrors = struct
 		mutable acc_expected : TType.t;
 		mutable acc_expected : TType.t;
 		mutable acc_actual : TType.t;
 		mutable acc_actual : TType.t;
 		mutable acc_messages : unify_error list;
 		mutable acc_messages : unify_error list;
+		mutable acc_extra : unify_error list;
 		mutable acc_next : access option;
 		mutable acc_next : access option;
 	}
 	}
 
 
@@ -121,6 +122,7 @@ module BetterErrors = struct
 			acc_expected = expected;
 			acc_expected = expected;
 			acc_actual = actual;
 			acc_actual = actual;
 			acc_messages = [];
 			acc_messages = [];
+			acc_extra = [];
 			acc_next = None;
 			acc_next = None;
 		} in
 		} in
 		let root_acc = make_acc Root t_dynamic t_dynamic in
 		let root_acc = make_acc Root t_dynamic t_dynamic in
@@ -128,6 +130,9 @@ module BetterErrors = struct
 		let add_message msg =
 		let add_message msg =
 			!current_acc.acc_messages <- msg :: !current_acc.acc_messages
 			!current_acc.acc_messages <- msg :: !current_acc.acc_messages
 		in
 		in
+		let add_extra msg =
+			!current_acc.acc_extra <- msg :: !current_acc.acc_extra
+		in
 		let add_access kind =
 		let add_access kind =
 			let acc = make_acc kind t_dynamic t_dynamic in
 			let acc = make_acc kind t_dynamic t_dynamic in
 			!current_acc.acc_next <- Some acc;
 			!current_acc.acc_next <- Some acc;
@@ -146,6 +151,8 @@ module BetterErrors = struct
 				add_access FunctionReturn;
 				add_access FunctionReturn;
 			| Invariant_parameter i ->
 			| Invariant_parameter i ->
 				add_access (TypeParameter i);
 				add_access (TypeParameter i);
+			| Unify_custom _ ->
+				add_extra err
 			| _ ->
 			| _ ->
 				add_message err
 				add_message err
 		) l;
 		) l;
@@ -267,7 +274,7 @@ module BetterErrors = struct
 		in
 		in
 		match access.acc_next with
 		match access.acc_next with
 		| None ->
 		| None ->
-			String.concat "\n" (List.rev_map (unify_error_msg ctx) access.acc_messages)
+			String.concat "\n" (List.rev_map (unify_error_msg ctx) (access.acc_extra @ access.acc_messages))
 		| Some access_next ->
 		| Some access_next ->
 			let slhs,srhs = loop access_next access  in
 			let slhs,srhs = loop access_next access  in
 			Printf.sprintf "error: %s\nhave: %s\nwant: %s" (Buffer.contents message_buffer) slhs srhs
 			Printf.sprintf "error: %s\nhave: %s\nwant: %s" (Buffer.contents message_buffer) slhs srhs
@@ -297,13 +304,10 @@ let typing_error ?(depth=0) msg p = raise (Error (Custom msg,p,depth))
 let located_typing_error ?(depth=0) msg =
 let located_typing_error ?(depth=0) msg =
 	let err = match msg with
 	let err = match msg with
 		| Message (msg,p) -> Custom msg
 		| Message (msg,p) -> Custom msg
-		| Stack stack -> Stack (List.map (fun msg -> (Custom (extract_located_msg msg),(extract_located_pos msg))) stack)
+		| Stack _ -> Stack (List.map (fun (msg,p) -> (Custom msg,p)) (extract_located msg))
 	in
 	in
 	raise (Error (err,(extract_located_pos msg),depth))
 	raise (Error (err,(extract_located_pos msg),depth))
 
 
-let call_stack_error ?(depth=0) msg stack p =
-	raise (Error (Stack (((Custom ("Uncaught exception " ^ msg)),p) :: (List.map (fun p -> ((Custom "Called from here"),p)) stack)),p,depth))
-
 let raise_typing_error ?(depth=0) err p = raise (Error(err,p,depth))
 let raise_typing_error ?(depth=0) err p = raise (Error(err,p,depth))
 
 
 let error_require r p =
 let error_require r p =

+ 11 - 6
src/core/globals.ml

@@ -38,14 +38,19 @@ let null_pos = { pfile = "?"; pmin = -1; pmax = -1 }
 let located msg p = Message (msg,p)
 let located msg p = Message (msg,p)
 let located_stack stack = Stack stack
 let located_stack stack = Stack stack
 
 
-let rec extract_located_msg = function
-	 | Message (msg,p) -> msg
-	 | Stack stack -> String.concat "\n" (List.map extract_located_msg stack)
+let rec extract_located = function
+	| Message (msg,p) -> [(msg, p)]
+	| Stack stack -> List.fold_left (fun acc s -> acc @ (extract_located s)) [] stack
+
+let rec relocate msg p = match msg with
+	| Message (msg,_) -> Message (msg,p)
+	| Stack [] -> Stack []
+	| Stack (hd :: tl) -> Stack ((relocate hd p) :: tl)
 
 
 let rec extract_located_pos = function
 let rec extract_located_pos = function
-	 | Message (_,p) -> p
-	 | Stack [] -> null_pos
-	 | Stack (hd :: _) -> extract_located_pos hd
+	| Message (_,p) -> p
+	| Stack [] -> null_pos
+	| Stack (hd :: _) -> extract_located_pos hd
 
 
 let macro_platform = ref Neko
 let macro_platform = ref Neko
 
 

+ 3 - 3
src/generators/genpy.ml

@@ -1492,9 +1492,9 @@ module Printer = struct
 				let interpolate () =
 				let interpolate () =
 					Codegen.interpolate_code pctx.pc_com code tl (Buffer.add_string buf) (fun e -> Buffer.add_string buf (print_expr pctx e)) ecode.epos
 					Codegen.interpolate_code pctx.pc_com code tl (Buffer.add_string buf) (fun e -> Buffer.add_string buf (print_expr pctx e)) ecode.epos
 				in
 				in
-				let old = pctx.pc_com.error in
-				pctx.pc_com.error <- abort;
-				Std.finally (fun() -> pctx.pc_com.error <- old) interpolate ();
+				let old = pctx.pc_com.located_error in
+				pctx.pc_com.located_error <- abort_located;
+				Std.finally (fun() -> pctx.pc_com.located_error <- old) interpolate ();
 				Buffer.contents buf
 				Buffer.contents buf
 			| ("python_Syntax._pythonCode"), [e] ->
 			| ("python_Syntax._pythonCode"), [e] ->
 				print_expr pctx e
 				print_expr pctx e

+ 27 - 4
src/macro/eval/evalExceptions.ml

@@ -119,13 +119,29 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p =
 		final();
 		final();
 		Some v
 		Some v
 	with
 	with
-	| RunTimeException(v,stack,p') ->
+	| RunTimeException(v,eval_stack,p') ->
 		eval.caught_exception <- vnull;
 		eval.caught_exception <- vnull;
 		Option.may (build_exception_stack ctx) env;
 		Option.may (build_exception_stack ctx) env;
 		eval.env <- env;
 		eval.env <- env;
 		if is v key_haxe_macro_Error then begin
 		if is v key_haxe_macro_Error then begin
 			let v1 = field v key_exception_message in
 			let v1 = field v key_exception_message in
 			let v2 = field v key_pos in
 			let v2 = field v key_pos in
+			let v3 = field v key_child_errors in
+			let stack = match v3 with
+				| VArray sub ->
+						List.map (fun v ->
+							if is v key_haxe_macro_Error then begin
+								let v1 = field v key_exception_message in
+								let v2 = match (field v key_pos) with
+									| VInstance {ikind=IPos p} -> p
+									| _ -> null_pos
+								in
+								(Error.Custom (value_string v1), v2)
+							end else
+								Error.typing_error "Something went wrong" null_pos
+						) (EvalArray.to_list sub)
+				| _ -> []
+			in
 			reset_ctx();
 			reset_ctx();
 			final();
 			final();
 			match v1 with
 			match v1 with
@@ -146,12 +162,15 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p =
 							)
 							)
 						| _ -> null_pos
 						| _ -> null_pos
 					in
 					in
-					raise (Error.Error (Error.Custom s.sstring,p,0))
+					(match stack with
+						| [] -> raise (Error.Error (Error.Custom s.sstring,p,0))
+						| _ -> raise (Error.Error (Stack ((Error.Custom (s.sstring),p) :: stack),p,0))
+					);
 				| _ ->
 				| _ ->
 					Error.typing_error "Something went wrong" null_pos
 					Error.typing_error "Something went wrong" null_pos
 		end else begin
 		end else begin
 			(* Careful: We have to get the message before resetting the context because toString() might access it. *)
 			(* Careful: We have to get the message before resetting the context because toString() might access it. *)
-			let stack = match stack with
+			let stack = match eval_stack with
 				| [] -> []
 				| [] -> []
 				| l when p' = null_pos -> l (* If the exception position is null_pos, we're "probably" in a built-in function. *)
 				| l when p' = null_pos -> l (* If the exception position is null_pos, we're "probably" in a built-in function. *)
 				| _ :: l -> l (* Otherwise, ignore topmost frame position. *)
 				| _ :: l -> l (* Otherwise, ignore topmost frame position. *)
@@ -159,7 +178,11 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p =
 			let stack = get_exc_error_stack ctx stack in
 			let stack = get_exc_error_stack ctx stack in
 			reset_ctx();
 			reset_ctx();
 			final();
 			final();
-			Error.call_stack_error (value_string v) stack (if p' = null_pos then p else p')
+			let p = if p' = null_pos then p else p' in
+			raise (Error.Error (Stack (
+				(Error.Custom ("Uncaught exception " ^ (value_string v)),p)
+				:: (List.map (fun p -> ((Error.Custom "Called from here"),p)) stack)
+			),p,0))
 		end
 		end
 	| MacroApi.Abort ->
 	| MacroApi.Abort ->
 		final();
 		final();

+ 1 - 0
src/macro/eval/evalHash.ml

@@ -203,6 +203,7 @@ let key_fspare = hash "fspare"
 let key_kind = hash "kind"
 let key_kind = hash "kind"
 let key_end = hash "end"
 let key_end = hash "end"
 let key_events = hash "events"
 let key_events = hash "events"
+let key_child_errors = hash "childErrors"
 let key_isInternal = hash "isInternal"
 let key_isInternal = hash "isInternal"
 let key_physical = hash "physical"
 let key_physical = hash "physical"
 let key_address = hash "address"
 let key_address = hash "address"

+ 10 - 1
src/macro/eval/evalLuv.ml

@@ -550,7 +550,16 @@ let uv_error_fields = [
 					(* Eval interpreter rethrows runtime exceptions as `Custom "Exception message\nException stack"` *)
 					(* Eval interpreter rethrows runtime exceptions as `Custom "Exception message\nException stack"` *)
 					(try fst (ExtString.String.split msg "\n")
 					(try fst (ExtString.String.split msg "\n")
 					with _ -> msg)
 					with _ -> msg)
-				| HaxeError.Error (err,p,_) -> extract_located_msg (HaxeError.error_msg p err)
+				| HaxeError.Error (err,p,_) ->
+						(* TODO hook global error reporting *)
+						(match (extract_located (HaxeError.error_msg p err)) with
+						| [] -> ""
+						| (s,_) :: [] -> s
+						| (s,_) :: stack ->
+							List.fold_left (fun acc (s,p) ->
+								Printf.sprintf "%s%s\n" acc (Lexer.get_error_pos (Printf.sprintf "%s:%d: ") p)
+							) (s ^ "\n") stack
+						);
 				| _ -> Printexc.to_string ex
 				| _ -> Printexc.to_string ex
 			in
 			in
 			let e = create_haxe_exception ~stack:(get_ctx()).exception_stack msg in
 			let e = create_haxe_exception ~stack:(get_ctx()).exception_stack msg in

+ 40 - 15
src/macro/eval/evalMain.ml

@@ -151,7 +151,16 @@ let create com api is_macro =
 		| _ ->
 		| _ ->
 			let msg =
 			let msg =
 				match ex with
 				match ex with
-				| Error.Error (err,p,_) -> extract_located_msg (Error.error_msg p err)
+				| Error.Error (err,p,_) ->
+						(* TODO hook global error reporting *)
+						(match (extract_located (Error.error_msg p err)) with
+						| [] -> ""
+						| (s,_) :: [] -> s
+						| (s,_) :: stack ->
+							List.fold_left (fun acc (s,p) ->
+								Printf.sprintf "%s%s\n" acc (Lexer.get_error_pos (Printf.sprintf "%s:%d: ") p)
+							) (s ^ "\n") stack
+						);
 				| _ -> Printexc.to_string ex
 				| _ -> Printexc.to_string ex
 			in
 			in
 			Printf.eprintf "%s\n" msg;
 			Printf.eprintf "%s\n" msg;
@@ -401,27 +410,43 @@ let set_error ctx b =
 let add_types ctx types ready =
 let add_types ctx types ready =
 	if not ctx.had_error then ignore(catch_exceptions ctx (fun () -> ignore(add_types ctx types ready)) null_pos)
 	if not ctx.had_error then ignore(catch_exceptions ctx (fun () -> ignore(add_types ctx types ready)) null_pos)
 
 
-let compiler_error msg =
-	let pos = extract_located_pos msg in
-	let msg = extract_located_msg msg in
+let make_runtime_error msg pos =
 	let vi = encode_instance key_haxe_macro_Error in
 	let vi = encode_instance key_haxe_macro_Error in
 	match vi with
 	match vi with
 	| VInstance i ->
 	| VInstance i ->
-		let msg = EvalString.create_unknown msg in
-		set_instance_field i key_exception_message msg;
+		let s = EvalString.create_unknown msg in
+		set_instance_field i key_exception_message s;
 		set_instance_field i key_pos (encode_pos pos);
 		set_instance_field i key_pos (encode_pos pos);
-		set_instance_field i key_native_exception msg;
-		let ctx = get_ctx() in
-		let eval = get_eval ctx in
-		(match eval.env with
-		| Some _ ->
-			let stack = EvalStackTrace.make_stack_value (call_stack eval) in
-			set_instance_field i key_native_stack stack;
-		| None -> ());
-		exc vi
+		set_instance_field i key_native_exception s;
+		vi
 	| _ ->
 	| _ ->
 		die "" __LOC__
 		die "" __LOC__
 
 
+let compiler_error msg =
+	let pos = extract_located_pos msg in
+	let items = extract_located msg in
+		let vi = make_runtime_error (fst (List.hd items)) pos in
+		match vi with
+		| VInstance i ->
+			(match items with
+			| [] | _ :: [] ->
+				let ctx = get_ctx() in
+				let eval = get_eval ctx in
+				(match eval.env with
+				| Some _ ->
+					let stack = EvalStackTrace.make_stack_value (call_stack eval) in
+					set_instance_field i key_native_stack stack;
+				| None -> ());
+
+			| (hd :: stack) ->
+				let stack = List.map (fun (s,p) -> make_runtime_error s p) stack in
+				set_instance_field i key_child_errors (encode_array stack);
+			);
+
+			exc vi
+		| _ ->
+			die "" __LOC__
+
 let rec value_to_expr v p =
 let rec value_to_expr v p =
 	let path i =
 	let path i =
 		let mt = IntMap.find i (get_ctx()).type_cache in
 		let mt = IntMap.find i (get_ctx()).type_cache in

+ 15 - 8
src/typing/callUnification.ml

@@ -18,7 +18,13 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 		raise (Error (Call_error err,p,0))
 		raise (Error (Call_error err,p,0))
 	in
 	in
 	let arg_error ul name opt p =
 	let arg_error ul name opt p =
-		let err = Stack [(ul,p); (Custom ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'"), p)] in
+		let msg = ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'") in
+		let err = match ul with
+			| Stack s -> Stack (s @ [(Custom msg,p)])
+			| Unify l -> Unify (l @ [(Unify_custom msg)])
+			| Custom parent -> Custom (parent ^ "\n" ^ msg)
+			| _ -> Stack [(ul,p); (Custom (compl_msg msg), p)]
+		in
 		call_error (Could_not_unify err) p
 		call_error (Could_not_unify err) p
 	in
 	in
 	let mk_pos_infos t =
 	let mk_pos_infos t =
@@ -470,26 +476,27 @@ object(self)
 		in
 		in
 		ctx.macro_depth <- ctx.macro_depth - 1;
 		ctx.macro_depth <- ctx.macro_depth - 1;
 		ctx.with_type_stack <- List.tl ctx.with_type_stack;
 		ctx.with_type_stack <- List.tl ctx.with_type_stack;
-		let old = ctx.com.error in
-		ctx.com.error <- (fun ?(depth=0) msg ep ->
+		let old = ctx.com.located_error in
+		ctx.com.located_error <- (fun ?(depth=0) msg ->
+			let ep = extract_located_pos msg 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
 				locate_macro_error := false;
 				locate_macro_error := false;
-				old msg (if ep = null_pos then p else ep);
+				old (if ep = null_pos then (relocate msg p) else msg);
 				locate_macro_error := true;
 				locate_macro_error := true;
-				if ep <> null_pos then old ~depth:(depth+1) (compl_msg "Called from macro here") p;
+				if ep <> null_pos then old ~depth:(depth+1) (located (compl_msg "Called from macro here") p);
 			end else
 			end else
-				old msg ep;
+				old msg;
 		);
 		);
 		let e = try
 		let e = try
 			f()
 			f()
 		with exc ->
 		with exc ->
-			ctx.com.error <- old;
+			ctx.com.located_error <- old;
 			!ethis_f();
 			!ethis_f();
 			raise exc
 			raise exc
 		in
 		in
 		let e = Diagnostics.secure_generated_code ctx e in
 		let e = Diagnostics.secure_generated_code ctx e in
-		ctx.com.error <- old;
+		ctx.com.located_error <- old;
 		!ethis_f();
 		!ethis_f();
 		e
 		e
 
 

+ 15 - 7
src/typing/macroContext.ml

@@ -83,20 +83,28 @@ let macro_timer ctx l =
 
 
 let typing_timer ctx need_type f =
 let typing_timer ctx need_type f =
 	let t = Timer.timer ["typing"] in
 	let t = Timer.timer ["typing"] in
-	let old = ctx.com.error and oldp = ctx.pass and oldlocals = ctx.locals in
+	let old = ctx.com.located_error and oldp = ctx.pass and oldlocals = ctx.locals in
 	let restore_report_mode = disable_report_mode ctx.com in
 	let restore_report_mode = disable_report_mode ctx.com in
 	(*
 	(*
 		disable resumable errors... unless we are in display mode (we want to reach point of completion)
 		disable resumable errors... unless we are in display mode (we want to reach point of completion)
 	*)
 	*)
-	(*if ctx.com.display = DMNone then ctx.com.error <- (fun e p -> raise (Error(Custom e,p)));*) (* TODO: review this... *)
-	ctx.com.error <- (fun ?(depth=0) e p -> raise (Error(Custom e,p,depth)));
+	(*if ctx.com.display = DMNone then ctx.com.located_error <- (fun e p -> raise (Error(Custom e,p)));*) (* TODO: review this... *)
+	let rec located_to_error = function
+		| Message (e,p) -> (Custom e,p)
+		| Stack stack -> (Stack (List.map located_to_error stack),null_pos)
+	in
+
+	ctx.com.located_error <- (fun ?(depth=0) msg ->
+		let (e,p) = located_to_error msg in
+		raise (Error (e,p,depth)));
+
 	if need_type && ctx.pass < PTypeField then begin
 	if need_type && ctx.pass < PTypeField then begin
 		ctx.pass <- PTypeField;
 		ctx.pass <- PTypeField;
 		flush_pass ctx PBuildClass "typing_timer";
 		flush_pass ctx PBuildClass "typing_timer";
 	end;
 	end;
 	let exit() =
 	let exit() =
 		t();
 		t();
-		ctx.com.error <- old;
+		ctx.com.located_error <- old;
 		ctx.pass <- oldp;
 		ctx.pass <- oldp;
 		ctx.locals <- oldlocals;
 		ctx.locals <- oldlocals;
 		restore_report_mode ();
 		restore_report_mode ();
@@ -528,11 +536,11 @@ let create_macro_interp ctx mctx =
 			Interp.do_reuse mint (make_macro_api ctx null_pos);
 			Interp.do_reuse mint (make_macro_api ctx null_pos);
 			mint, (fun() -> ())
 			mint, (fun() -> ())
 	) in
 	) in
-	let on_error = com2.error in
-	com2.error <- (fun ?depth e p ->
+	let on_error = com2.located_error in
+	com2.located_error <- (fun ?depth msg ->
 		Interp.set_error (Interp.get_ctx()) true;
 		Interp.set_error (Interp.get_ctx()) true;
 		macro_interp_cache := None;
 		macro_interp_cache := None;
-		on_error e p
+		on_error msg
 	);
 	);
 	let macro = ((fun() -> Interp.select mint), mctx) in
 	let macro = ((fun() -> Interp.select mint), mctx) in
 	ctx.g.macros <- Some macro;
 	ctx.g.macros <- Some macro;

+ 3 - 3
src/typing/matcher.ml

@@ -269,14 +269,14 @@ module Pattern = struct
 			if pctx.is_postfix_match then DKMarked else DKPattern toplevel
 			if pctx.is_postfix_match then DKMarked else DKPattern toplevel
 		in
 		in
 		let catch_errors () =
 		let catch_errors () =
-			let old = ctx.com.error in
+			let old = ctx.com.located_error in
 			let restore_report_mode = disable_report_mode ctx.com in
 			let restore_report_mode = disable_report_mode ctx.com in
-			ctx.com.error <- (fun ?depth _ _ ->
+			ctx.com.located_error <- (fun ?depth _ ->
 				raise Exit
 				raise Exit
 			);
 			);
 			(fun () ->
 			(fun () ->
 				restore_report_mode();
 				restore_report_mode();
-				ctx.com.error <- old
+				ctx.com.located_error <- old
 			)
 			)
 		in
 		in
 		let try_typing e =
 		let try_typing e =

+ 5 - 0
std/haxe/macro/Expr.hx

@@ -1011,6 +1011,11 @@ class Error extends Exception {
 	**/
 	**/
 	public var pos:Position;
 	public var pos:Position;
 
 
+	/**
+		Child error messages, if any.
+	**/
+	private var childErrors:Array<Error>;
+
 	/**
 	/**
 		Instantiates an error with given message and position.
 		Instantiates an error with given message and position.
 	**/
 	**/

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

@@ -3,4 +3,3 @@ Main.hx:6: characters 9-10 : Warning : { foo : Null<Unknown<0>>, ?b : Null<Int>,
 Main.hx:7: characters 10-11 : error: Int should be String
 Main.hx:7: characters 10-11 : error: Int should be String
 Main.hx:7: characters 10-11 : ... have: { b: Int }
 Main.hx:7: characters 10-11 : ... have: { b: Int }
 Main.hx:7: characters 10-11 : ... want: { b: String }
 Main.hx:7: characters 10-11 : ... want: { b: String }
-Main.hx:7: characters 10-11 : ... For function argument 'v'

+ 14 - 0
tests/misc/projects/Issue11055/Main.hx

@@ -0,0 +1,14 @@
+macro function test() {
+	foo();
+	return macro null;
+}
+
+#if macro
+function foo() {
+	throw "up";
+}
+#end
+
+function main() {
+	test();
+}

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

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

+ 3 - 0
tests/misc/projects/Issue11055/compile-fail.hxml.stderr

@@ -0,0 +1,3 @@
+Main.hx:8: characters 2-7 : Uncaught exception up
+Main.hx:2: characters 2-7 : Called from here
+Main.hx:13: characters 2-8 : Called from here

+ 3 - 0
tests/misc/projects/Issue11055/compile-pretty-fail.hxml

@@ -0,0 +1,3 @@
+compile-fail.hxml
+-D message-reporting=pretty
+-D no-color

+ 14 - 0
tests/misc/projects/Issue11055/compile-pretty-fail.hxml.stderr

@@ -0,0 +1,14 @@
+[ERROR] Main.hx:8: characters 2-7
+
+ 8 |  throw "up";
+   |  ^^^^^
+   | Uncaught exception up
+
+     2 |  foo();
+       |  ^^^^^
+       | Called from here
+
+    13 |  test();
+       |  ^^^^^^
+       | Called from here
+

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

@@ -1,8 +1,6 @@
 Fail.hx:7: characters 8-12 : error: Void should be haxe.NotVoid
 Fail.hx:7: characters 8-12 : error: Void should be haxe.NotVoid
 Fail.hx:7: characters 8-12 : ... have: (...) -> Void
 Fail.hx:7: characters 8-12 : ... have: (...) -> Void
 Fail.hx:7: characters 8-12 : ... want: (...) -> haxe.NotVoid
 Fail.hx:7: characters 8-12 : ... want: (...) -> haxe.NotVoid
-Fail.hx:7: characters 8-12 : ... For function argument 'f'
 Fail.hx:8: characters 8-16 : error: FakeVoid should be haxe.NotVoid
 Fail.hx:8: characters 8-16 : error: FakeVoid should be haxe.NotVoid
 Fail.hx:8: characters 8-16 : ... have: (...) -> FakeVoid
 Fail.hx:8: characters 8-16 : ... have: (...) -> FakeVoid
 Fail.hx:8: characters 8-16 : ... want: (...) -> haxe.NotVoid
 Fail.hx:8: characters 8-16 : ... want: (...) -> haxe.NotVoid
-Fail.hx:8: characters 8-16 : ... For function argument 'f'

+ 0 - 2
tests/misc/projects/Issue6810/indent-fail.hxml.stderr

@@ -1,8 +1,6 @@
 Fail.hx:7: characters 8-12 : error: Void should be haxe.NotVoid
 Fail.hx:7: characters 8-12 : error: Void should be haxe.NotVoid
   Fail.hx:7: characters 8-12 : have: (...) -> Void
   Fail.hx:7: characters 8-12 : have: (...) -> Void
   Fail.hx:7: characters 8-12 : want: (...) -> haxe.NotVoid
   Fail.hx:7: characters 8-12 : want: (...) -> haxe.NotVoid
-  Fail.hx:7: characters 8-12 : For function argument 'f'
 Fail.hx:8: characters 8-16 : error: FakeVoid should be haxe.NotVoid
 Fail.hx:8: characters 8-16 : error: FakeVoid should be haxe.NotVoid
   Fail.hx:8: characters 8-16 : have: (...) -> FakeVoid
   Fail.hx:8: characters 8-16 : have: (...) -> FakeVoid
   Fail.hx:8: characters 8-16 : want: (...) -> haxe.NotVoid
   Fail.hx:8: characters 8-16 : want: (...) -> haxe.NotVoid
-  Fail.hx:8: characters 8-16 : For function argument 'f'

+ 0 - 2
tests/misc/projects/Issue6810/pretty-fail.hxml.stderr

@@ -5,7 +5,6 @@
    | error: Void should be haxe.NotVoid
    | error: Void should be haxe.NotVoid
    | have: (...) -> Void
    | have: (...) -> Void
    | want: (...) -> haxe.NotVoid
    | want: (...) -> haxe.NotVoid
-   | For function argument 'f'
 
 
 [ERROR] Fail.hx:8: characters 8-16
 [ERROR] Fail.hx:8: characters 8-16
 
 
@@ -14,5 +13,4 @@
    | error: FakeVoid should be haxe.NotVoid
    | error: FakeVoid should be haxe.NotVoid
    | have: (...) -> FakeVoid
    | have: (...) -> FakeVoid
    | want: (...) -> haxe.NotVoid
    | want: (...) -> haxe.NotVoid
-   | For function argument 'f'
 
 

+ 6 - 1
tests/unit/src/unit/HelperMacros.hx

@@ -48,11 +48,16 @@ class HelperMacros {
 		return { pos: currentPos(), expr: haxe.macro.Expr.ExprDef.EConst(haxe.macro.Expr.Constant.CIdent(result)) };
 		return { pos: currentPos(), expr: haxe.macro.Expr.ExprDef.EConst(haxe.macro.Expr.Constant.CIdent(result)) };
 	}
 	}
 
 
+	@:access(haxe.macro.Error.childErrors)
 	static public macro function typeErrorText(e:haxe.macro.Expr) {
 	static public macro function typeErrorText(e:haxe.macro.Expr) {
 		var result = try {
 		var result = try {
 			typeof(e);
 			typeof(e);
 			null;
 			null;
-		} catch (e:haxe.macro.Expr.Error) e.message;
+		} catch (e:haxe.macro.Expr.Error) {
+			var msg = e.message;
+			if (e.childErrors != null) for (c in e.childErrors) msg += "\n" + c.message;
+			msg;
+		}
 		return {
 		return {
 			pos: currentPos(),
 			pos: currentPos(),
 			expr: if (result == null)
 			expr: if (result == null)