Browse Source

Alternative message reporting modes (#10863)

* Avoid some 'Called from macro here' errors

* Add nesting level data to errors

* Add verbose error reporting

* Use ExtString.String.starts_with

* Rename to pretty-errors

* Some cleanup

* Expose nesting level parameter to message emiting API

* Better handling of invalid positions

* Rewrite into generic -D message-reporting=[format] feature

Also add logging capabilities with -D messages-log-file=[path]

* Handle non-ascii characters in sources

* Implement -D message-reporting=diagnostics

* Add Info prefix to info messages

* Properly close log file

* Only add 'Info : ' prefix in diagnostics mode

* Error handling and directory creation for log file

* Whitespace

* Error reporting specific defines shouldn't trigger new context

* Rename indented message display mode

* [tests] Add pretty test output for 6065

* Avoid adding useless whitespace on empty lines

* Cleanup source resolution, fix files not ending with empty line

* [tests] Add pretty test output for 5644

* Avoid adding useless whitespace with multiline positions

* [tests] Add pretty test output for 6584

* [tests] Add pretty test output for 6790

* [tests] Add pretty test output for 6796

* [tests] Add pretty test output for 7968

* Fix indent reporting mode, add error message

* [tests] Add indent test output for 6065

* [tests] Add pretty and indent test output for 5949

* Improve conditions for displaying error headings

* [tests] Add pretty test output for 8471

* Indent message reporting: indent su errors instead of prefixing with ...

* [tests] Add pretty and indent tests output for 6810

* [tests] Add pretty and indent tests output for 10844

* [tests] Update indent test output for 6065

* [test] remove haxe -version output check

* Also remove '...' prefixes to single line errors in indent mode

* Carry position with compiler errors

Takes care of pretty printing Stack errors, including call stack

Commit this ugly thing while it works, cleanup tomorrow

* [tests] Add pretty and indent tests output for 8303

* Position marker for single char positions

* Don't nest first element of Stack errors

* [tests] update pretty/indent tests

* nesting_level -> depth

* A bit of cleanup

* Cleanup typing_error

* Cleanup display_error

* Add std path resolution to misc tests output checks

* More nesting level -> depth changes

* [tests] only normalize root of std path

* Fix missing depth

* Update doc

* Add another test for 4803

* Internal errors handling

* [tests] don't resolve real path of std root since eval doesn't

* [tests] remove test with different outcome on all platforms

* [ci] use predictable std path for misc tests

* [tests] No need for Context

* [tests] add log file tests

* Handle stack in located_typing_error

* Reduce diff

* Whitespace

* Add comment

* Reduce diff

* Change compiler_message from tuple to record

* [tests] Add missing test output for 10844

(needed std path being exposed to misc tests)

* [skip ci] add comments
Rudy Ges 2 years ago
parent
commit
17e6b0c88c
100 changed files with 1093 additions and 307 deletions
  1. 2 0
      .github/workflows/main.yml
  2. 2 0
      extra/github-actions/workflows/main.yml
  3. 22 0
      src-json/define.json
  4. 1 1
      src/codegen/gencommon/gencommon.ml
  5. 7 3
      src/compiler/compilationContext.ml
  6. 24 18
      src/compiler/compiler.ml
  7. 2 2
      src/compiler/displayProcessing.ml
  8. 1 1
      src/compiler/retyper.ml
  9. 355 27
      src/compiler/server.ml
  10. 6 6
      src/context/abstractCast.ml
  11. 16 11
      src/context/common.ml
  12. 2 2
      src/context/display/diagnostics.ml
  13. 1 1
      src/context/display/displayFields.ml
  14. 14 10
      src/context/typecore.ml
  15. 1 0
      src/core/define.ml
  16. 34 22
      src/core/error.ml
  17. 32 3
      src/core/globals.ml
  18. 1 1
      src/core/texpr.ml
  19. 1 1
      src/filters/filters.ml
  20. 8 5
      src/macro/eval/evalExceptions.ml
  21. 2 2
      src/macro/eval/evalLuv.ml
  22. 4 2
      src/macro/eval/evalMain.ml
  23. 22 17
      src/macro/macroApi.ml
  24. 1 1
      src/optimization/analyzerTexpr.ml
  25. 1 1
      src/optimization/inlineConstructors.ml
  26. 2 2
      src/optimization/optimizer.ml
  27. 32 32
      src/typing/callUnification.ml
  28. 1 1
      src/typing/calls.ml
  29. 3 3
      src/typing/fields.ml
  30. 5 5
      src/typing/forLoop.ml
  31. 7 7
      src/typing/generic.ml
  32. 13 13
      src/typing/macroContext.ml
  33. 1 1
      src/typing/magicTypes.ml
  34. 1 1
      src/typing/matcher.ml
  35. 6 6
      src/typing/operators.ml
  36. 15 15
      src/typing/typeload.ml
  37. 8 8
      src/typing/typeloadCheck.ml
  38. 12 12
      src/typing/typeloadFields.ml
  39. 4 4
      src/typing/typeloadModule.ml
  40. 1 1
      src/typing/typeloadParse.ml
  41. 24 24
      src/typing/typer.ml
  42. 7 7
      src/typing/typerDisplay.ml
  43. 1 1
      src/typing/typerDotPath.ml
  44. 12 12
      std/haxe/macro/Context.hx
  45. 2 0
      tests/misc/projects/Issue10623/indent-fail.hxml
  46. 2 0
      tests/misc/projects/Issue10623/indent-fail.hxml.stderr
  47. 3 0
      tests/misc/projects/Issue10623/pretty-fail.hxml
  48. 10 0
      tests/misc/projects/Issue10623/pretty-fail.hxml.stderr
  49. 3 0
      tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml.stderr
  50. 2 0
      tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml
  51. 2 0
      tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml.stderr
  52. 3 0
      tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml.stderr
  53. 2 0
      tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml
  54. 3 0
      tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml.stderr
  55. 4 0
      tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml
  56. 12 0
      tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml.stderr
  57. 4 0
      tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml
  58. 5 0
      tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml.stderr
  59. 3 0
      tests/misc/projects/Issue10863/logmode-fail.hxml
  60. 1 0
      tests/misc/projects/Issue10863/logmode-fail.hxml.stderr
  61. 2 0
      tests/misc/projects/Issue10863/reporting-fail.hxml
  62. 1 0
      tests/misc/projects/Issue10863/reporting-fail.hxml.stderr
  63. 8 0
      tests/misc/projects/Issue4803/Main2.hx
  64. 3 0
      tests/misc/projects/Issue4803/compile2-fail.hxml
  65. 2 0
      tests/misc/projects/Issue4803/compile2-fail.hxml.stderr
  66. 3 0
      tests/misc/projects/Issue5644/pretty-fail.hxml
  67. 6 0
      tests/misc/projects/Issue5644/pretty-fail.hxml.stderr
  68. 2 0
      tests/misc/projects/Issue5949/indent-fail.hxml
  69. 2 0
      tests/misc/projects/Issue5949/indent-fail.hxml.stderr
  70. 3 0
      tests/misc/projects/Issue5949/pretty-fail.hxml
  71. 10 0
      tests/misc/projects/Issue5949/pretty-fail.hxml.stderr
  72. 2 0
      tests/misc/projects/Issue6065/indent-fail.hxml
  73. 14 0
      tests/misc/projects/Issue6065/indent-fail.hxml.stderr
  74. 3 0
      tests/misc/projects/Issue6065/pretty-fail.hxml
  75. 40 0
      tests/misc/projects/Issue6065/pretty-fail.hxml.stderr
  76. 3 0
      tests/misc/projects/Issue6584/pretty-fail.hxml
  77. 14 0
      tests/misc/projects/Issue6584/pretty-fail.hxml.stderr
  78. 3 0
      tests/misc/projects/Issue6790/pretty-fail.hxml
  79. 7 0
      tests/misc/projects/Issue6790/pretty-fail.hxml.stderr
  80. 3 0
      tests/misc/projects/Issue6796/pretty-fail.hxml
  81. 7 0
      tests/misc/projects/Issue6796/pretty-fail.hxml.stderr
  82. 3 0
      tests/misc/projects/Issue6810/.gitignore
  83. 2 0
      tests/misc/projects/Issue6810/indent-fail.hxml
  84. 8 0
      tests/misc/projects/Issue6810/indent-fail.hxml.stderr
  85. 2 0
      tests/misc/projects/Issue6810/logfile-01-fail.hxml
  86. 4 0
      tests/misc/projects/Issue6810/logfile-02-fail.hxml
  87. 4 0
      tests/misc/projects/Issue6810/logfile-03-fail.hxml
  88. 1 0
      tests/misc/projects/Issue6810/logfile-04-fail.hxml
  89. 3 0
      tests/misc/projects/Issue6810/pretty-fail.hxml
  90. 18 0
      tests/misc/projects/Issue6810/pretty-fail.hxml.stderr
  91. 3 0
      tests/misc/projects/Issue7968/pretty-fail.hxml
  92. 10 0
      tests/misc/projects/Issue7968/pretty-fail.hxml.stderr
  93. 2 0
      tests/misc/projects/Issue8303/indent-fail.hxml
  94. 21 0
      tests/misc/projects/Issue8303/indent-fail.hxml.stderr
  95. 3 0
      tests/misc/projects/Issue8303/pretty-fail.hxml
  96. 54 0
      tests/misc/projects/Issue8303/pretty-fail.hxml.stderr
  97. 3 0
      tests/misc/projects/Issue8471/compile2-pretty.hxml
  98. 22 0
      tests/misc/projects/Issue8471/compile2-pretty.hxml.stderr
  99. 7 14
      tests/misc/projects/issue5002/compile2-fail.hxml.stderr
  100. 7 1
      tests/misc/src/Main.hx

+ 2 - 0
.github/workflows/main.yml

@@ -347,6 +347,7 @@ jobs:
       PLATFORM: linux64
       TEST: ${{matrix.target}}
       HXCPP_COMPILE_CACHE: ~/hxcache
+      HAXE_STD_PATH: /usr/local/share/haxe/std
     strategy:
       fail-fast: false
       matrix:
@@ -852,6 +853,7 @@ jobs:
       PLATFORM: mac
       TEST: ${{matrix.target}}
       HXCPP_COMPILE_CACHE: ~/hxcache
+      HAXE_STD_PATH: /usr/local/share/haxe/std
     strategy:
       fail-fast: false
       matrix:

+ 2 - 0
extra/github-actions/workflows/main.yml

@@ -157,6 +157,7 @@ jobs:
       PLATFORM: linux64
       TEST: ${{matrix.target}}
       HXCPP_COMPILE_CACHE: ~/hxcache
+      HAXE_STD_PATH: /usr/local/share/haxe/std
     strategy:
       fail-fast: false
       matrix:
@@ -406,6 +407,7 @@ jobs:
       PLATFORM: mac
       TEST: ${{matrix.target}}
       HXCPP_COMPILE_CACHE: ~/hxcache
+      HAXE_STD_PATH: /usr/local/share/haxe/std
     strategy:
       fail-fast: false
       matrix:

+ 22 - 0
src-json/define.json

@@ -718,5 +718,27 @@
 		"name": "NoTre",
 		"define": "no-tre",
 		"doc": "Disable tail recursion elimination."
+	},
+	{
+		"name": "MessageReporting",
+		"define": "message-reporting",
+		"doc": "Select message reporting mode for compiler output. (default: classic)",
+		"params": ["mode: classic | pretty | indent"]
+	},
+	{
+		"name": "NoColor",
+		"define": "no-color",
+		"doc": "Disable ANSI color codes when using rich output."
+	},
+	{
+		"name": "MessagesLogFile",
+		"define": "messages-log-file",
+		"doc": "Path to a text file to write messages log to, in addition to regular output."
+	},
+	{
+		"name": "MessagesLogFormat",
+		"define": "messages-log-format",
+		"doc": "Select message reporting mode for messages log output. (default: indent)",
+		"params": ["format: classic | pretty | indent"]
 	}
 ]

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

@@ -732,7 +732,7 @@ let run_filters_from gen t filters =
 let run_filters gen =
 	let last_error = gen.gcon.error in
 	let has_errors = ref false in
-	gen.gcon.error <- (fun msg pos -> has_errors := true; last_error msg pos);
+	gen.gcon.error <- (fun ?(depth=0) msg pos -> has_errors := true; last_error ~depth msg pos);
 	(* 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
 	(if Common.defined gen.gcon Define.GencommonDebug then debug_mode := true else debug_mode := false);

+ 7 - 3
src/compiler/compilationContext.ml

@@ -64,6 +64,10 @@ type server_api = {
 let message ctx msg =
 	ctx.messages <- msg :: ctx.messages
 
-let error ctx msg p =
-	message ctx (msg,p,DKCompilerMessage,Error);
-	ctx.has_error <- true
+let error ctx ?(depth=0) msg p =
+	message ctx (make_compiler_message msg p depth DKCompilerMessage Error);
+	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

+ 24 - 18
src/compiler/compiler.ml

@@ -4,20 +4,20 @@ open CompilationContext
 
 let run_or_diagnose ctx f arg =
 	let com = ctx.com in
-	let handle_diagnostics msg p kind =
+	let handle_diagnostics msg kind =
 		ctx.has_error <- true;
-		add_diagnostics_message com msg p kind Error;
+		add_diagnostics_message com msg kind Error;
 		DisplayOutput.emit_diagnostics ctx.com
 	in
 	if is_diagnostics com then begin try
 			f arg
 		with
-		| Error.Error(msg,p) ->
-			handle_diagnostics (Error.error_msg msg) p DKCompilerMessage
+		| Error.Error(msg,p,_) ->
+			handle_diagnostics (Error.error_msg p msg) DKCompilerMessage
 		| Parser.Error(msg,p) ->
-			handle_diagnostics (Parser.error_msg msg) p DKParserError
+			handle_diagnostics (located (Parser.error_msg msg) p) DKParserError
 		| Lexer.Error(msg,p) ->
-			handle_diagnostics (Lexer.error_msg msg) p DKParserError
+			handle_diagnostics (located (Lexer.error_msg msg) p) DKParserError
 		end
 	else
 		f arg
@@ -211,8 +211,8 @@ module Setup = struct
 		Common.define_value com Define.Haxe s_version;
 		Common.raw_define com "true";
 		Common.define_value com Define.Dce "std";
-		com.info <- (fun msg p -> message ctx (msg,p,DKCompilerMessage,Information));
-		com.warning <- (fun w options msg p ->
+		com.info <- (fun ?(depth=0) msg p -> message ctx (make_compiler_message msg p depth DKCompilerMessage Information));
+		com.warning <- (fun ?(depth=0) w options msg p ->
 			match Warning.get_mode w (com.warning_options @ options) with
 			| WMEnable ->
 				let wobj = Warning.warning_obj w in
@@ -221,18 +221,18 @@ module Setup = struct
 				else
 					Printf.sprintf "(%s) %s" wobj.w_name msg
 				in
-				message ctx (msg,p,DKCompilerMessage,Warning)
+				message ctx (make_compiler_message msg p depth DKCompilerMessage Warning)
 			| WMDisable ->
 				()
 		);
 		com.error <- error ctx;
-		let filter_messages = (fun keep_errors predicate -> (List.filter (fun ((_,_,_,sev) as cm) ->
-			(match sev with
+		let filter_messages = (fun keep_errors predicate -> (List.filter (fun cm ->
+			(match cm.cm_severity with
 			| MessageSeverity.Error -> keep_errors;
 			| Information | Warning | Hint -> predicate cm;)
 		) (List.rev ctx.messages))) in
-		com.get_messages <- (fun () -> (List.map (fun ((_,_,_,sev) as cm) ->
-			(match sev with
+		com.get_messages <- (fun () -> (List.map (fun cm ->
+			(match cm.cm_severity with
 			| MessageSeverity.Error -> die "" __LOC__;
 			| Information | Warning | Hint -> cm;)
 		) (filter_messages false (fun _ -> true))));
@@ -334,8 +334,8 @@ try
 with
 	| Abort ->
 		()
-	| Error.Fatal_error (m,p) ->
-		error ctx m p
+	| Error.Fatal_error (m,depth) ->
+		located_error ~depth ctx m
 	| Common.Abort (m,p) ->
 		error ctx m p
 	| Lexer.Error (m,p) ->
@@ -348,10 +348,16 @@ with
 			ctx.messages <- [];
 		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 ctx (Error.compl_msg "referenced here")) (List.rev pl);
+			List.iter (error ~depth:1 ctx (Error.compl_msg "referenced here")) (List.rev pl);
 		end
-	| Error.Error (m,p) ->
-		error ctx (Error.error_msg m) p
+	| Error.Error (Stack stack,_,depth) -> (match stack with
+		| [] -> ()
+		| (e,p) :: stack -> begin
+			located_error ~depth ctx (Error.error_msg p e);
+			List.iter (fun (e,p) -> located_error ~depth:(depth+1) ctx (Error.error_msg p e)) stack;
+		end)
+	| Error.Error (m,p,depth) ->
+		located_error ~depth ctx (Error.error_msg p m)
 	| Generic.Generic_Exception(m,p) ->
 		error ctx m p
 	| Arg.Bad msg ->

+ 2 - 2
src/compiler/displayProcessing.ml

@@ -92,10 +92,10 @@ let process_display_arg ctx actx =
 let process_display_configuration ctx =
 	let com = ctx.com in
 	if is_diagnostics com then begin
-		com.warning <- (fun w options s p ->
+		com.warning <- (fun ?depth w options s p ->
 			match Warning.get_mode w (com.warning_options @ options) with
 			| WMEnable ->
-				add_diagnostics_message com s p DKCompilerMessage Warning
+				add_diagnostics_message com (located s p) DKCompilerMessage Warning
 			| WMDisable ->
 				()
 		);

+ 1 - 1
src/compiler/retyper.ml

@@ -21,7 +21,7 @@ let disable_typeloading rctx ctx f =
 	ctx.g.load_only_cached_modules <- true;
 	try
 		Std.finally (fun () -> ctx.g.load_only_cached_modules <- old) f ()
-	with (Error.Error (Module_not_found path,_)) ->
+	with (Error.Error (Module_not_found path,_,_)) ->
 		fail rctx (Printf.sprintf "Could not load [Module %s]" (s_type_path path))
 
 let pair_type th t = match th with

+ 355 - 27
src/compiler/server.ml

@@ -1,3 +1,4 @@
+open Extlib_leftovers
 open Printf
 open Globals
 open Ast
@@ -19,19 +20,19 @@ let has_error ctx =
 let check_display_flush ctx f_otherwise = match ctx.com.json_out with
 	| None ->
 		if is_diagnostics ctx.com then begin
-			List.iter (fun (msg,p,kind,sev) ->
-				add_diagnostics_message ctx.com msg p kind sev
+			List.iter (fun cm ->
+				add_diagnostics_message ctx.com (located cm.cm_message cm.cm_pos) cm.cm_kind cm.cm_severity
 			) (List.rev ctx.messages);
 			raise (Completion (Diagnostics.print ctx.com))
 		end else
 			f_otherwise ()
 	| Some api ->
 		if has_error ctx then begin
-			let errors = List.map (fun (msg,p,_,sev) ->
+			let errors = List.map (fun cm ->
 				JObject [
-					"severity",JInt (MessageSeverity.to_int sev);
-					"location",Genjson.generate_pos_as_location p;
-					"message",JString msg;
+					"severity",JInt (MessageSeverity.to_int cm.cm_severity);
+					"location",Genjson.generate_pos_as_location cm.cm_pos;
+					"message",JString cm.cm_message;
 				]
 			) (List.rev ctx.messages) in
 			api.send_error errors
@@ -86,17 +87,248 @@ let parse_file cs com file p =
 open ServerCompilationContext
 
 module Communication = struct
+	type error_context = {
+		mutable last_positions : pos IntMap.t;
+		mutable max_lines : int IntMap.t;
+		mutable gutter : int IntMap.t;
+		mutable previous : (pos * MessageSeverity.t * int) option;
+	}
+
+	let create_error_context () = {
+		last_positions = IntMap.empty;
+		max_lines = IntMap.empty;
+		gutter = IntMap.empty;
+		previous = None;
+	}
+
+	let error_printer file line = Printf.sprintf "%s:%d:" file line
+
+	let resolve_source file l1 p1 l2 p2 =
+		let ch = open_in_bin file in
+		let curline = ref 1 in
+		let lines = ref [] in
+		let rec loop p line =
+			let inc i line =
+				if (!curline >= l1) && (!curline <= l2) then lines := (!curline, line) :: !lines;
+				curline := !curline + 1;
+				(i, "")
+			in
+
+			let input_char_or_done ch line =
+				try input_char ch with End_of_file -> begin
+					ignore(inc 0 line);
+					raise End_of_file
+				end
+			in
+
+			try
+				let read_char line = match input_char_or_done ch line with
+					| '\n' -> inc 1 line
+					| '\r' ->
+						ignore(input_char_or_done ch line);
+						inc 2 line
+					| c -> begin
+						let line = ref (line ^ (String.make 1 c)) in
+						let rec skip n =
+							if n > 0 then begin
+								let c = input_char_or_done ch !line in
+								line := !line ^ (String.make 1 c);
+								skip (n - 1)
+							end
+						in
+
+						let code = int_of_char c in
+						if code < 0xC0 then ()
+						else if code < 0xE0 then skip 1
+						else if code < 0xF0 then skip 2
+						else skip 3;
+
+						(1, !line)
+					end
+				in
+
+				let (delta, line) = read_char line in
+				loop (p + delta) line
+			with End_of_file ->
+				close_in ch;
+		in
+
+		loop 0 "";
+		List.rev !lines
+
+	let compiler_pretty_message_string ctx ectx cm =
+		match cm.cm_message with
+		(* Filter some messages that don't add much when using this message renderer *)
+		| "End of overload failure reasons" -> None
+		| _ -> begin
+			ectx.last_positions <- (IntMap.add cm.cm_depth cm.cm_pos ectx.last_positions);
+			let is_null_pos = cm.cm_pos = null_pos || cm.cm_pos.pmin = -1 in
+			let is_unknown_file f = f = "" || f = "?" in
+
+			(* Extract informations from position *)
+			let l1, p1, l2, p2, epos, lines =
+				if is_null_pos then begin
+					let epos = if is_unknown_file cm.cm_pos.pfile then "(unknown position)" else cm.cm_pos.pfile in
+					(-1, -1, -1, -1, epos, [])
+				end else begin
+					let f =
+						try Common.find_file ctx.com cm.cm_pos.pfile
+						with Not_found -> failwith ("File not found '" ^ cm.cm_pos.pfile ^ "'")
+						in
+
+					let l1, p1, l2, p2 = Lexer.get_pos_coords cm.cm_pos in
+					let lines = resolve_source f l1 p1 l2 p2 in
+					let epos = Lexer.get_error_pos error_printer cm.cm_pos in
+					(l1, p1, l2, p2, epos, lines)
+				end in
+
+			(* If 4 lines or less, display all; if more, crop the middle *)
+			let lines = match lines with
+				| _ :: (_ :: (_ :: (_ :: []))) -> lines
+				| hd :: (_ :: (_ :: (_ :: l))) ->
+					let _,line = hd in
+					let indent = ref 0 in
+					let found = ref false in
+
+					while (not !found) && (!indent < (String.length line - 1)) do
+						found := not (Lexer.is_whitespace (String.unsafe_get line !indent));
+						indent := !indent + 1
+					done;
+
+					[hd; (0, (String.make (!indent+1) ' ') ^ "[...]"); List.hd (List.rev l)]
+				| _ -> lines
+			in
+
+			let parent_pos =
+				if cm.cm_depth = 0 then null_pos
+				else (try IntMap.find (cm.cm_depth-1) ectx.last_positions with Not_found -> null_pos)
+			in
+
+			let prev_pos,prev_sev,prev_nl = match ectx.previous with
+				| None -> (None, None, 0)
+				| Some (p, sev, depth) -> (Some p, Some sev, depth)
+			in
+
+			let sev_changed = prev_sev = None || Some cm.cm_severity <> prev_sev in
+			let pos_changed = (prev_pos = None || cm.cm_pos <> Option.get prev_pos || (cm.cm_depth <> prev_nl && cm.cm_depth <> prev_nl + 1)) && (parent_pos = null_pos || cm.cm_pos <> parent_pos) in
+			let file_changed = prev_pos = None || (pos_changed && match (cm.cm_pos.pfile, (Option.get prev_pos).pfile) with
+				| (f1, f2) when (is_unknown_file f1) && (is_unknown_file f2) -> false
+				| (f1, f2) -> f1 <> f2
+			) in
+
+			let display_heading = cm.cm_depth = 0 || sev_changed || file_changed in
+			let display_source = cm.cm_depth = 0 || sev_changed || pos_changed in
+			let display_pos_marker = (not is_null_pos) && (cm.cm_depth = 0 || sev_changed || pos_changed) in
+
+			let gutter_len = (try String.length (Printf.sprintf "%d" (IntMap.find cm.cm_depth ectx.max_lines)) with Not_found -> 0) + 2 in
+
+			let no_color = Define.defined ctx.com.defines Define.NoColor in
+			let c_reset = if no_color then "" else "\x1b[0m" in
+			let c_bold = if no_color then "" else "\x1b[1m" in
+			let c_dim = if no_color then "" else "\x1b[2m" in
+
+			let (c_sev, c_sev_bg) = if no_color then ("", "") else match cm.cm_severity with
+				| MessageSeverity.Warning -> ("\x1b[33m", "\x1b[30;43m")
+				| Information | Hint -> ("\x1b[34m", "\x1b[30;44m")
+				| Error -> ("\x1b[31m", "\x1b[30;41m")
+			in
+
+			let sev_label = if cm.cm_depth > 0 then " -> " else Printf.sprintf
+				(if no_color then "[%s]" else " %s ")
+				(match cm.cm_severity with
+					| MessageSeverity.Warning -> "WARNING"
+					| Information -> "INFO"
+					| Hint -> "HINT"
+					| Error -> "ERROR"
+				) in
+
+			let out = ref "" in
+
+			if display_heading then
+				out := Printf.sprintf "%s%s\n\n"
+					(* Severity heading *)
+					(c_sev_bg ^ sev_label ^ c_reset ^ " ")
+					(* File + line pointer *)
+					epos;
+
+			(* Error source *)
+			if display_source then out := List.fold_left (fun out (l, line) ->
+				let nb_len = String.length (string_of_int l) in
+
+				(* Replace tabs with 1 space to avoid column misalignments *)
+				let line = String.concat " " (ExtString.String.nsplit line "\t") in
+				let len = String.length line in
 
-	let compiler_message_string (str,p,_,sev) =
-		let str = match sev with
-			| MessageSeverity.Warning -> "Warning : " ^ str
-			| Information | Error | Hint -> str
+				out ^ Printf.sprintf "%s%s | %s\n"
+					(* left-padded line number *)
+					(String.make (gutter_len-nb_len-1) ' ')
+					(if l = 0 then "-" else Printf.sprintf "%d" l)
+					(* Source code at that line *)
+					(
+						if l = 0 then
+							c_dim ^ line ^ c_reset
+						else if l1 = l2 then
+							(if p1 > 1 then c_dim ^ (String.sub line 0 (p1-1)) else "")
+							^ c_reset ^ c_bold ^ (String.sub line (p1-1) (p2-p1))
+							^ c_reset ^ c_dim ^ (String.sub line (p2-1) (len - p2 + 1))
+							^ c_reset
+						else begin
+							(if (l = l1) then
+								(if p1 > 1 then c_dim ^ (String.sub line 0 (p1-1)) else "")
+								^ c_reset ^ c_bold ^ (String.sub line (p1-1) (len-p1+1))
+								^ c_reset
+							else if (l = l2) then
+								(if p2 > 1 then c_bold ^ (String.sub line 0 (p2-1)) else "")
+								^ c_reset ^ c_dim ^ (String.sub line (p2-1) (len-p2+1))
+								^ c_reset
+							else c_bold ^ line ^ c_reset)
+						end
+					)
+			) !out lines;
+
+			(* Error position marker *)
+			if display_pos_marker then
+				out := Printf.sprintf "%s%s|%s\n"
+					!out
+					(String.make gutter_len ' ')
+					(if l1 = l2 then String.make p1 ' ' ^ c_sev ^ String.make (if p1 = p2 then 1 else p2-p1) '^' ^ c_reset else "");
+
+			(* Error message *)
+			out := List.fold_left (fun out str -> Printf.sprintf "%s%s| %s\n"
+				out
+				(String.make gutter_len ' ')
+				(* Remove "... " prefix *)
+				(if (ExtString.String.starts_with str "... ") then String.sub str 4 ((String.length str) - 4) else str)
+			) !out (ExtString.String.nsplit cm.cm_message "\n");
+
+			ectx.previous <- Some ((if is_null_pos then null_pos else cm.cm_pos), cm.cm_severity, cm.cm_depth);
+			ectx.gutter <- (IntMap.add cm.cm_depth gutter_len ectx.gutter);
+
+			(* Indent sub errors *)
+			let rec indent ?(acc=0) depth =
+				if depth = 0 then acc
+				else indent ~acc:(acc + try IntMap.find (depth-1) ectx.gutter with Not_found -> 3) (depth-1)
+			in
+
+			Some (
+				if cm.cm_depth > 0 then String.concat "\n" (List.map (fun str -> match str with
+					| "" -> ""
+					| _ -> (String.make (indent cm.cm_depth) ' ') ^ str
+				) (ExtString.String.nsplit !out "\n"))
+				else !out
+			)
+		end
+
+	let compiler_message_string ctx ectx cm =
+		let str = match cm.cm_severity with
+			| MessageSeverity.Warning -> "Warning : " ^ cm.cm_message
+			| Information | Error | Hint -> cm.cm_message
 		in
-		if p = null_pos then
-			str
+
+		if cm.cm_pos = null_pos then
+			Some str
 		else begin
-			let error_printer file line = Printf.sprintf "%s:%d:" file line in
-			let epos = Lexer.get_error_pos error_printer p in
+			let epos = Lexer.get_error_pos error_printer cm.cm_pos in
 			let str =
 				let lines =
 					match (ExtString.String.nsplit str "\n") with
@@ -105,9 +337,105 @@ module Communication = struct
 				in
 				String.concat ("\n" ^ epos ^ " : ") lines
 			in
-			Printf.sprintf "%s : %s" epos str
+			Some (Printf.sprintf "%s : %s" epos str)
 		end
 
+	let compiler_indented_message_string ctx ectx cm =
+		match cm.cm_message with
+		(* Filter some messages that don't add much when using this message renderer *)
+		| "End of overload failure reasons" -> None
+		| _ ->
+			let str = match cm.cm_severity with
+				| MessageSeverity.Warning -> "Warning : " ^ cm.cm_message
+				| Information -> "Info : " ^ cm.cm_message
+				| Error | Hint -> cm.cm_message
+			in
+
+			if cm.cm_pos = null_pos then
+				Some str
+			else begin
+				let epos = Lexer.get_error_pos error_printer cm.cm_pos in
+				let lines =
+					match (ExtString.String.nsplit str "\n") with
+					| first :: rest -> (cm.cm_depth, first) :: List.map (fun msg -> (cm.cm_depth+1, msg)) rest
+					| l -> [(cm.cm_depth, List.hd l)]
+				in
+				let rm_prefix str = if (ExtString.String.starts_with str "... ") then String.sub str 4 ((String.length str) - 4) else str in
+				Some (String.concat "\n" (List.map (fun (depth, msg) -> (String.make (depth*2) ' ') ^ epos ^ " : " ^ (rm_prefix msg)) lines))
+			end
+
+	let get_max_line max_lines messages =
+		List.fold_left (fun max_lines cm ->
+			let _,_,l2,_ = Lexer.get_pos_coords cm.cm_pos in
+			let old = try IntMap.find cm.cm_depth max_lines with Not_found -> 0 in
+
+			if l2 > old then IntMap.add cm.cm_depth l2 max_lines
+			else max_lines
+		) max_lines messages
+
+	let display_messages ctx on_message = begin
+		let ectx = create_error_context () in
+		ectx.max_lines <- get_max_line ectx.max_lines ctx.messages;
+
+		let get_formatter def default =
+			let format_mode = Define.defined_value_safe ~default ctx.com.defines def in
+			match format_mode with
+				| "pretty" -> compiler_pretty_message_string ctx ectx
+				| "indent" -> compiler_indented_message_string ctx ectx
+				| "classic" -> compiler_message_string ctx ectx
+				| m -> begin
+					let def = Define.get_define_key def in
+					error ctx (Printf.sprintf "Invalid message reporting mode: \"%s\", expected classic | pretty | indent (for -D %s)." m def) null_pos;
+					compiler_message_string ctx ectx
+				end
+			in
+
+		let message_formatter = get_formatter Define.MessageReporting "classic" in
+		let log_formatter = get_formatter Define.MessagesLogFormat "indent" in
+
+		let log_messages = ref (Define.defined ctx.com.defines Define.MessagesLogFile) in
+		let log_message = ref None in
+		let close_logs = ref None in
+
+		if !log_messages then begin
+			try begin
+				let buf = Rbuffer.create 16000 in
+
+				let file = Define.defined_value ctx.com.defines Define.MessagesLogFile in
+				let chan =
+					Path.mkdir_from_path file;
+					open_out_bin file
+				in
+
+				log_message := (Some (fun msg ->
+					match (log_formatter msg) with
+						| None -> ()
+						| Some str -> Rbuffer.add_string buf (str ^ "\n")));
+
+				close_logs := (Some (fun () ->
+					Rbuffer.output_buffer chan buf;
+					Rbuffer.clear buf;
+					close_out chan
+				));
+			end with
+				| Failure e | Sys_error e -> begin
+					let def = Define.get_define_key Define.MessagesLogFile in
+					error ctx (Printf.sprintf "Error opening log file: %s. Logging to file disabled (-D %s)" e def) null_pos;
+					log_messages := false;
+				end
+		end;
+
+		List.iter (fun cm ->
+			if !log_messages then (Option.get !log_message) cm;
+
+			match (message_formatter cm) with
+				| None -> ()
+				| Some str -> on_message cm.cm_severity str
+		) (List.rev ctx.messages);
+
+		if !log_messages then (Option.get !close_logs) ();
+	end
+
 	let create_stdio () =
 		let rec self = {
 			write_out = (fun s ->
@@ -118,10 +446,12 @@ module Communication = struct
 				prerr_string s;
 			);
 			flush = (fun ctx ->
-				List.iter (fun ((_,_,_,sev) as cm) -> match sev with
-					| MessageSeverity.Information -> print_endline (compiler_message_string cm)
-					| Warning | Error | Hint -> prerr_endline (compiler_message_string cm)
-				) (List.rev ctx.messages);
+				display_messages ctx (fun sev output ->
+					match sev with
+						| MessageSeverity.Information -> print_endline output
+						| Warning | Error | Hint -> prerr_endline output
+				);
+
 				if has_error ctx && !Helper.prompt then begin
 					print_endline "Press enter to exit...";
 					ignore(read_line());
@@ -148,13 +478,11 @@ module Communication = struct
 		);
 		flush = (fun ctx ->
 			check_display_flush ctx (fun () ->
-				List.iter
-					(fun msg ->
-						let s = compiler_message_string msg in
-						write (s ^ "\n");
-						ServerMessage.message s;
-					)
-					(List.rev ctx.messages);
+				display_messages ctx (fun _ output ->
+					write (output ^ "\n");
+					ServerMessage.message output;
+				);
+
 				sctx.was_compilation <- ctx.com.display.dms_full_typing;
 				if has_error ctx then begin
 					measure_times := false;
@@ -766,4 +1094,4 @@ and init_wait_socket host port =
 		let close() = Unix.close sin in
 		false, read, write, close
 	) in
-	accept
+	accept

+ 6 - 6
src/context/abstractCast.ml

@@ -19,7 +19,7 @@ let rec make_static_call ctx c cf a pl args t p =
 					| None ->  type_expr ctx (EConst (Ident "null"),p) WithType.value
 				in
 				ctx.with_type_stack <- List.tl ctx.with_type_stack;
-				let e = try cast_or_unify_raise ctx t e p with Error(Unify _,_) -> raise Not_found in
+				let e = try cast_or_unify_raise ctx t e p with Error(Unify _,_,_) -> raise Not_found in
 				f();
 				e
 			| _ -> die "" __LOC__
@@ -38,7 +38,7 @@ and do_check_cast ctx uctx tleft eright p =
 				(try
 					Type.unify_custom uctx eright.etype tleft;
 				with Unify_error l ->
-					raise (Error (Unify l, eright.epos)))
+					raise (Error (Unify l, eright.epos,0)))
 			| _ -> ()
 		end;
 		if cf == ctx.curfield || rec_stack_memq cf cast_stack then typing_error "Recursive implicit cast" p;
@@ -108,7 +108,7 @@ and cast_or_unify_raise ctx ?(uctx=None) tleft eright p =
 and cast_or_unify ctx tleft eright p =
 	try
 		cast_or_unify_raise ctx tleft eright p
-	with Error (Unify l,p) ->
+	with Error (Unify l,p,_) ->
 		raise_or_display ctx l p;
 		eright
 
@@ -142,7 +142,7 @@ let find_array_read_access_raise ctx a pl e1 p =
 					let e1 = cast_or_unify_raise ctx ta1 e1 p in
 					check_constraints();
 					cf,tf,r,e1
-				with Unify_error _ | Error (Unify _,_) ->
+				with Unify_error _ | Error (Unify _,_,_) ->
 					loop cfl
 				end
 			| _ -> loop cfl
@@ -163,7 +163,7 @@ let find_array_write_access_raise ctx a pl e1 e2  p =
 					let e2 = cast_or_unify_raise ctx ta2 e2 p in
 					check_constraints();
 					cf,tf,r,e1,e2
-				with Unify_error _ | Error (Unify _,_) ->
+				with Unify_error _ | Error (Unify _,_,_) ->
 					loop cfl
 				end
 			| _ -> loop cfl
@@ -345,4 +345,4 @@ let handle_abstract_casts ctx e =
 	in
 	loop ctx e
 ;;
-Typecore.cast_or_unify_raise_ref := cast_or_unify_raise
+Typecore.cast_or_unify_raise_ref := cast_or_unify_raise

+ 16 - 11
src/context/common.ml

@@ -353,9 +353,9 @@ type context = {
 	mutable report_mode : report_mode;
 	(* communication *)
 	mutable print : string -> unit;
-	mutable error : string -> pos -> unit;
-	mutable info : string -> pos -> unit;
-	mutable warning : warning -> Warning.warning_option list list -> string -> pos -> unit;
+	mutable error : ?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_options : Warning.warning_option list list;
 	mutable get_messages : unit -> compiler_message list;
 	mutable filter_messages : (compiler_message -> bool) -> unit;
@@ -822,10 +822,10 @@ let create compilation_step cs version args =
 		user_defines = Hashtbl.create 0;
 		user_metas = Hashtbl.create 0;
 		get_macros = (fun() -> None);
-		info = (fun _ _ -> die "" __LOC__);
-		warning = (fun _ _ _ -> die "" __LOC__);
+		info = (fun ?depth _ _ -> die "" __LOC__);
+		warning = (fun ?depth _ _ _ -> die "" __LOC__);
 		warning_options = [];
-		error = (fun _ _ -> die "" __LOC__);
+		error = (fun ?depth _ _ -> die "" __LOC__);
 		get_messages = (fun() -> []);
 		filter_messages = (fun _ -> ());
 		pass_debug_messages = DynArray.create();
@@ -1013,7 +1013,7 @@ let allow_package ctx s =
 	with Not_found ->
 		()
 
-let abort msg p = raise (Abort (msg,p))
+let abort ?depth msg p = raise (Abort (msg,p))
 
 let platform ctx p = ctx.platform = p
 
@@ -1217,16 +1217,21 @@ let utf16_to_utf8 str =
 	loop 0;
 	Buffer.contents b
 
-let add_diagnostics_message com s p 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;
 	let di = com.shared.shared_display_information in
 	di.diagnostics_messages <- (s,p,kind,sev) :: di.diagnostics_messages
 
-let display_error com msg p =
+let located_display_error com ?(depth = 0) msg =
 	if is_diagnostics com then
-		add_diagnostics_message com msg p MessageKind.DKCompilerMessage MessageSeverity.Error
+		add_diagnostics_message com msg MessageKind.DKCompilerMessage MessageSeverity.Error
 	else
-		com.error msg p
+		com.error (Globals.extract_located_msg msg) (Globals.extract_located_pos msg) ~depth
+
+let display_error com ?(depth = 0) msg p =
+	located_display_error com ~depth (Globals.located msg p)
 
 open Printer
 

+ 2 - 2
src/context/display/diagnostics.ml

@@ -43,10 +43,10 @@ let find_unused_variables com e =
 let check_other_things com e =
 	let had_effect = ref false in
 	let no_effect p =
-		add_diagnostics_message com "This code has no effect" p DKCompilerMessage Warning;
+		add_diagnostics_message com (located "This code has no effect" p) DKCompilerMessage Warning;
 	in
 	let pointless_compound s p =
-		add_diagnostics_message com (Printf.sprintf "This %s has no effect, but some of its sub-expressions do" s) p DKCompilerMessage Warning;
+		add_diagnostics_message com (located (Printf.sprintf "This %s has no effect, but some of its sub-expressions do" s) p) DKCompilerMessage Warning;
 	in
 	let rec compound s el p =
 		let old = !had_effect in

+ 1 - 1
src/context/display/displayFields.ml

@@ -75,7 +75,7 @@ let collect_static_extensions ctx items e p =
 					let item = make_ci_class_field (CompletionClassField.make f CFSMember origin true) (f.cf_type,ct) in
 					PMap.add f.cf_name item acc
 				end
-			with Error (Unify _,_) | Unify_error _ ->
+			with Error (Unify _,_,_) | Unify_error _ ->
 				acc
 			end
 		| _ ->

+ 14 - 10
src/context/typecore.ml

@@ -197,7 +197,7 @@ type dot_path_part = {
 
 exception Forbid_package of (string * path * pos) * pos list * string
 
-exception WithTypeError of error_msg * pos
+exception WithTypeError of error_msg * pos * int (* depth *)
 
 let memory_marker = [|Unix.time()|]
 
@@ -221,9 +221,9 @@ let pass_name = function
 	| PForce -> "force"
 	| PFinal -> "final"
 
-let warning ctx w msg p =
+let warning ?(depth=0) ctx w msg p =
 	let options = (Warning.from_meta ctx.curclass.cl_meta) @ (Warning.from_meta ctx.curfield.cf_meta) in
-	ctx.com.warning w options msg p
+	ctx.com.warning ~depth w options msg p
 
 let make_call ctx e el t p = (!make_call_ref) ctx e el t p
 
@@ -256,11 +256,11 @@ let make_static_call ctx c cf map args t p =
 
 let raise_or_display ctx l p =
 	if ctx.untyped then ()
-	else if ctx.in_call_args then raise (WithTypeError(Unify l,p))
-	else display_error ctx.com (error_msg (Unify l)) p
+	else if ctx.in_call_args then raise (WithTypeError(Unify l,p,0))
+	else located_display_error ctx.com (error_msg p (Unify l))
 
 let raise_or_display_message ctx msg p =
-	if ctx.in_call_args then raise (WithTypeError (Custom msg,p))
+	if ctx.in_call_args then raise (WithTypeError (Custom msg,p,0))
 	else display_error ctx.com msg p
 
 let unify ctx t1 t2 p =
@@ -276,7 +276,7 @@ let unify_raise_custom uctx t1 t2 p =
 	with
 		Unify_error l ->
 			(* no untyped check *)
-			raise (Error (Unify l,p))
+			raise (Error (Unify l,p,0))
 
 let unify_raise = unify_raise_custom default_unification_context
 
@@ -294,7 +294,7 @@ let add_local ctx k n t p =
 				(* ignore std lib *)
 				if not (List.exists (ExtLib.String.starts_with p.pfile) ctx.com.std_path) then begin
 					warning ctx WVarShadow "This variable shadows a previously declared variable" p;
-					warning ctx WVarShadow (compl_msg "Previous variable was here") v'.v_pos
+					warning ~depth:1 ctx WVarShadow (compl_msg "Previous variable was here") v'.v_pos
 				end
 			with Not_found ->
 				()
@@ -420,8 +420,8 @@ let exc_protect ?(force=true) ctx f (where:string) =
 			r := lazy_available t;
 			t
 		with
-			| Error (m,p) ->
-				raise (Fatal_error ((error_msg m),p))
+			| Error (m,p,nl) ->
+				raise (Fatal_error ((error_msg p m),nl))
 	);
 	if force then delay ctx PForce (fun () -> ignore(lazy_type r));
 	r
@@ -795,6 +795,10 @@ let display_error ctx.com msg p =
 	debug ctx ("ERROR " ^ msg);
 	display_error ctx.com msg p
 
+let located_display_error ctx.com msg =
+	debug ctx ("ERROR " ^ msg);
+	located_display_error ctx.com msg
+
 let make_pass ?inf ctx f =
 	let inf = (match inf with None -> pass_infos ctx ctx.pass | Some inf -> inf) in
 	(fun v ->

+ 1 - 0
src/core/define.ml

@@ -117,6 +117,7 @@ let get_signature def =
 			   Note that we should removed flags like use_rtti_doc here.
 			*)
 			| "display" | "use_rtti_doc" | "macro_times" | "display_details" | "no_copt" | "display_stdin"
+			| "message_reporting" | "messages_log_file" | "messages_log_format" | "no_color"
 			| "dump" | "dump_dependencies" | "dump_ignore_var_ids" -> acc
 			| _ -> (k ^ "=" ^ v) :: acc
 		) def.values [] in

+ 34 - 22
src/core/error.ml

@@ -17,7 +17,7 @@ and error_msg =
 	| Unify of unify_error list
 	| Custom of string
 	| Unknown_ident of string
-	| Stack of error_msg * error_msg
+	| Stack of (error_msg * Globals.pos) list
 	| Call_error of call_error
 	| No_constructor of module_type
 	| Abstract_class of module_type
@@ -26,8 +26,8 @@ and type_not_found_reason =
 	| Private_type
 	| Not_defined
 
-exception Fatal_error of string * Globals.pos
-exception Error of error_msg * Globals.pos
+exception Fatal_error of Globals.located * int (* depth *)
+exception Error of error_msg * Globals.pos * int (* depth *)
 
 let string_source t = match follow t with
 	| TInst(c,tl) -> PMap.foldi (fun s _ acc -> s :: acc) (TClass.get_all_fields c tl) []
@@ -43,6 +43,9 @@ let short_type ctx t =
 	Should be called for each complementary error message.
 *)
 let compl_msg s = "... " ^ s
+let rec compl_located_msg = function
+	 | Message (s,p) -> Message (compl_msg s,p)
+	 | Stack stack -> Stack (List.map compl_located_msg stack)
 
 let unify_error_msg ctx err = match err with
 	| Cannot_unify (t1,t2) ->
@@ -270,29 +273,38 @@ module BetterErrors = struct
 			Printf.sprintf "error: %s\nhave: %s\nwant: %s" (Buffer.contents message_buffer) slhs srhs
 end
 
-let rec error_msg = function
-	| Module_not_found m -> "Type not found : " ^ s_type_path m
-	| Type_not_found (m,t,Private_type) -> "Cannot access private type " ^ t ^ " in module " ^ s_type_path m
-	| Type_not_found (m,t,Not_defined) -> "Module " ^ s_type_path m ^ " does not define type " ^ t
-	| Unify l -> BetterErrors.better_error_message l
-	| Unknown_ident s -> "Unknown identifier : " ^ s
-	| Custom s -> s
-	| Stack (m1,m2) -> error_msg m1 ^ "\n" ^ error_msg m2
-	| Call_error err -> s_call_error err
-	| No_constructor mt -> (s_type_path (t_infos mt).mt_path ^ " does not have a constructor")
-	| Abstract_class mt -> (s_type_path (t_infos mt).mt_path) ^ " is abstract and cannot be constructed"
+let rec error_msg p = function
+	| Module_not_found m -> located ("Type not found : " ^ s_type_path m) p
+	| Type_not_found (m,t,Private_type) -> located ("Cannot access private type " ^ t ^ " in module " ^ s_type_path m) p
+	| Type_not_found (m,t,Not_defined) -> located ("Module " ^ s_type_path m ^ " does not define type " ^ t) p
+	| Unify l -> located (BetterErrors.better_error_message l) p
+	| Unknown_ident s -> located ("Unknown identifier : " ^ s) p
+	| Custom s -> located s p
+	| Stack stack -> located_stack (List.map (fun (e,p) -> error_msg p e) stack)
+	| Call_error err -> s_call_error p err
+	| No_constructor mt -> located (s_type_path (t_infos mt).mt_path ^ " does not have a constructor") p
+	| Abstract_class mt -> located (s_type_path (t_infos mt).mt_path ^ " is abstract and cannot be constructed") p
 
-and s_call_error = function
+and s_call_error p = function
 	| Not_enough_arguments tl ->
 		let pctx = print_context() in
-		"Not enough arguments, expected " ^ (String.concat ", " (List.map (fun (n,_,t) -> n ^ ":" ^ (short_type pctx t)) tl))
-	| Too_many_arguments -> "Too many arguments"
-	| Could_not_unify err -> error_msg err
-	| Cannot_skip_non_nullable s -> "Cannot skip non-nullable argument " ^ s
+		located ("Not enough arguments, expected " ^ (String.concat ", " (List.map (fun (n,_,t) -> n ^ ":" ^ (short_type pctx t)) tl))) p
+	| Too_many_arguments -> located "Too many arguments" p
+	| Could_not_unify err -> error_msg p err
+	| Cannot_skip_non_nullable s -> located ("Cannot skip non-nullable argument " ^ s) p
 
-let typing_error msg p = raise (Error (Custom msg,p))
+let typing_error ?(depth=0) msg p = raise (Error (Custom msg,p,depth))
+let located_typing_error ?(depth=0) msg =
+	let err = match msg with
+		| Message (msg,p) -> Custom msg
+		| Stack stack -> Stack (List.map (fun msg -> (Custom (extract_located_msg msg),(extract_located_pos msg))) stack)
+	in
+	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 err p = raise (Error(err,p))
+let raise_typing_error ?(depth=0) err p = raise (Error(err,p,depth))
 
 let error_require r p =
 	if r = "" then
@@ -309,4 +321,4 @@ let error_require r p =
 	in
 	typing_error ("Accessing this field requires " ^ r) p
 
-let invalid_assign p = typing_error "Invalid assign" p
+let invalid_assign p = typing_error "Invalid assign" p

+ 32 - 3
src/core/globals.ml

@@ -5,6 +5,9 @@ type pos = {
 }
 
 type path = string list * string
+type located =
+	| Message of string * pos
+	| Stack of located list
 
 module IntMap = Ptmap
 module StringMap = Map.Make(struct type t = string let compare = String.compare end)
@@ -30,6 +33,20 @@ let version_minor = (version mod 1000) / 100
 let version_revision = (version mod 100)
 let version_pre = Some "rc.1"
 
+let null_pos = { pfile = "?"; pmin = -1; pmax = -1 }
+
+let located msg p = Message (msg,p)
+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_pos = function
+	 | Message (_,p) -> p
+	 | Stack [] -> null_pos
+	 | Stack (hd :: _) -> extract_located_pos hd
+
 let macro_platform = ref Neko
 
 let return_partial_type = ref false
@@ -85,8 +102,6 @@ let platform_list_help = function
 	| [p] -> " (" ^ platform_name p ^ " only)"
 	| pl -> " (for " ^ String.concat "," (List.map platform_name pl) ^ ")"
 
-let null_pos = { pfile = "?"; pmin = -1; pmax = -1 }
-
 let mk_zero_range_pos p = { p with pmax = p.pmin }
 
 let s_type_path (p,s) = match p with [] -> s | _ -> String.concat "." p ^ "." ^ s
@@ -171,6 +186,20 @@ module MessageKind = struct
 		| DKMissingFields -> 7
 end
 
-type compiler_message = string * pos * MessageKind.t * MessageSeverity.t
+type compiler_message = {
+	cm_message : string;
+	cm_pos : pos;
+	cm_depth : int;
+	cm_kind : MessageKind.t;
+	cm_severity : MessageSeverity.t;
+}
+
+let make_compiler_message msg p depth kind sev = {
+		cm_message = msg;
+		cm_pos = p;
+		cm_depth = depth;
+		cm_kind = kind;
+		cm_severity = sev;
+}
 
 let i32_31 = Int32.of_int 31

+ 1 - 1
src/core/texpr.ml

@@ -597,7 +597,7 @@ let rec type_constant_value basic (e,p) =
 		typing_error "Constant value expected" p
 
 let is_constant_value basic e =
-	try (ignore (type_constant_value basic e); true) with Error (Custom _,_) -> false
+	try (ignore (type_constant_value basic e); true) with Error (Custom _,_,_) -> false
 
 let for_remap basic v e1 e2 p =
 	let v' = alloc_var v.v_kind v.v_name e1.etype e1.epos in

+ 1 - 1
src/filters/filters.ml

@@ -72,7 +72,7 @@ module LocalStatic = struct
 		begin try
 			let cf = PMap.find name ctx.curclass.cl_statics in
 			display_error ctx.com (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name) v.v_pos;
-			typing_error "Conflicting field was found here" cf.cf_name_pos;
+			typing_error ~depth:1 "Conflicting field was found here" cf.cf_name_pos;
 		with Not_found ->
 			let cf = mk_field name ~static:true v.v_type v.v_pos v.v_pos in
 			begin match eo with

+ 8 - 5
src/macro/eval/evalExceptions.ml

@@ -67,9 +67,12 @@ let format_pos p =
 let uncaught_exception_string v p extra =
 	(Printf.sprintf "%s : Uncaught exception %s%s" (format_pos p) (value_string v) extra)
 
-let get_exc_error_message ctx v stack p =
+let get_exc_error_stack ctx stack =
 	let pl = List.map (fun env -> {pfile = rev_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax}) stack in
-	let pl = List.filter (fun p -> p <> null_pos) pl in
+	List.filter (fun p -> p <> null_pos) pl
+
+let get_exc_error_message ctx v stack p =
+	let pl = get_exc_error_stack ctx stack in
 	match pl with
 	| [] ->
 		uncaught_exception_string v p ""
@@ -143,7 +146,7 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p =
 							)
 						| _ -> null_pos
 					in
-					raise (Error.Error (Error.Custom s.sstring,p))
+					raise (Error.Error (Error.Custom s.sstring,p,0))
 				| _ ->
 					Error.typing_error "Something went wrong" null_pos
 		end else begin
@@ -153,10 +156,10 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p =
 				| 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. *)
 			in
-			let msg = get_exc_error_message ctx v stack (if p' = null_pos then p else p') in
+			let stack = get_exc_error_stack ctx stack in
 			reset_ctx();
 			final();
-			Error.typing_error msg null_pos
+			Error.call_stack_error (value_string v) stack (if p' = null_pos then p else p')
 		end
 	| MacroApi.Abort ->
 		final();

+ 2 - 2
src/macro/eval/evalLuv.ml

@@ -546,11 +546,11 @@ let uv_error_fields = [
 		Error.set_on_unhandled_exception (fun ex ->
 			let msg =
 				match ex with
-				| HaxeError.Error (Custom msg,_) ->
+				| HaxeError.Error (Custom msg,_,_) ->
 					(* Eval interpreter rethrows runtime exceptions as `Custom "Exception message\nException stack"` *)
 					(try fst (ExtString.String.split msg "\n")
 					with _ -> msg)
-				| HaxeError.Error (err,_) -> HaxeError.error_msg err
+				| HaxeError.Error (err,p,_) -> extract_located_msg (HaxeError.error_msg p err)
 				| _ -> Printexc.to_string ex
 			in
 			let e = create_haxe_exception ~stack:(get_ctx()).exception_stack msg in

+ 4 - 2
src/macro/eval/evalMain.ml

@@ -151,7 +151,7 @@ let create com api is_macro =
 		| _ ->
 			let msg =
 				match ex with
-				| Error.Error (err,_) -> Error.error_msg err
+				| Error.Error (err,p,_) -> extract_located_msg (Error.error_msg p err)
 				| _ -> Printexc.to_string ex
 			in
 			Printf.eprintf "%s\n" msg;
@@ -401,7 +401,9 @@ let set_error ctx b =
 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)
 
-let compiler_error msg pos =
+let compiler_error msg =
+	let pos = extract_located_pos msg in
+	let msg = extract_located_msg msg in
 	let vi = encode_instance key_haxe_macro_Error in
 	match vi with
 	| VInstance i ->

+ 22 - 17
src/macro/macroApi.ml

@@ -61,10 +61,10 @@ type 'value compiler_api = {
 	encode_ctype : Ast.type_hint -> 'value;
 	decode_type : 'value -> t;
 	flush_context : (unit -> t) -> t;
-	display_error : (string -> pos -> unit);
+	display_error : ?depth:int -> (string -> pos -> unit);
 	with_imports : 'a . import list -> placed_name list list -> (unit -> 'a) -> 'a;
 	with_options : 'a . compiler_options -> (unit -> 'a) -> 'a;
-	warning : Warning.warning -> string -> pos -> unit;
+	warning : ?depth:int -> Warning.warning -> string -> pos -> unit;
 }
 
 
@@ -152,7 +152,7 @@ module type InterpApi = sig
 	val encode_ref : 'a -> ('a -> value) -> (unit -> string) -> value
 	val decode_ref : value -> 'a
 
-	val compiler_error : string -> Globals.pos -> 'a
+	val compiler_error : Globals.located -> 'a
 	val error_message : string -> 'a
 	val value_to_expr : value -> Globals.pos -> Ast.expr
 	val value_signature : value -> string
@@ -493,10 +493,10 @@ and encode_package_rule pr =
 	in
 	encode_enum ~pos:None IPackageRule tag pl
 
-and encode_message (msg,p,_,sev) =
-	let tag, pl = match sev with
-		| Globals.MessageSeverity.Information -> 0, [(encode_string msg); (encode_pos p)]
-		| Warning | Hint -> 1, [(encode_string msg); (encode_pos p)]
+and encode_message cm =
+	let tag, pl = match cm.cm_severity with
+		| Globals.MessageSeverity.Information -> 0, [(encode_string cm.cm_message); (encode_pos cm.cm_pos)]
+		| Warning | Hint -> 1, [(encode_string cm.cm_message); (encode_pos cm.cm_pos)]
 		| Error -> Globals.die "" __LOC__
 	in
 	encode_enum ~pos:None IMessage tag pl
@@ -1689,33 +1689,38 @@ let macro_api ccom get_api =
 		"current_pos", vfun0 (fun() ->
 			encode_pos (get_api()).pos
 		);
-		"error", vfun2 (fun msg p ->
+		"error", vfun3 (fun msg p depth ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
-			(ccom()).error msg p;
+			let depth = decode_int depth in
+			(ccom()).error ~depth msg p;
 			raise Abort
 		);
-		"fatal_error", vfun2 (fun msg p ->
+		"fatal_error", vfun3 (fun msg p depth ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
-			raise (Error.Fatal_error (msg,p))
+			let depth = decode_int depth in
+			raise (Error.Fatal_error ((Globals.located msg p),depth))
 		);
-		"report_error", vfun2 (fun msg p ->
+		"report_error", vfun3 (fun msg p depth ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
-			(get_api()).display_error msg p;
+			let depth = decode_int depth in
+			(get_api()).display_error ~depth msg p;
 			vnull
 		);
-		"warning", vfun2 (fun msg p ->
+		"warning", vfun3 (fun msg p depth ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
-			(get_api()).warning WUser msg p;
+			let depth = decode_int depth in
+			(get_api()).warning ~depth WUser msg p;
 			vnull
 		);
-		"info", vfun2 (fun msg p ->
+		"info", vfun3 (fun msg p depth ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
-			(ccom()).info msg p;
+			let depth = decode_int depth in
+			(ccom()).info ~depth msg p;
 			vnull
 		);
 		"get_messages", vfun0 (fun() ->

+ 1 - 1
src/optimization/analyzerTexpr.ml

@@ -1208,7 +1208,7 @@ module Purity = struct
 					apply_to_class com c
 				with Purity_conflict(impure,p) ->
 					com.error "Impure field overrides/implements field which was explicitly marked as @:pure" impure.pn_field.cf_pos;
-					Error.typing_error (Error.compl_msg "Pure field is here") p;
+					Error.typing_error ~depth:1 (Error.compl_msg "Pure field is here") p;
 				end
 			| _ -> ()
 		) com.types;

+ 1 - 1
src/optimization/inlineConstructors.ml

@@ -123,7 +123,7 @@ let inline_constructors ctx original_e =
 				List.iter (fun v -> if v.v_id < 0 then cancel_v v p) io.io_dependent_vars;
 				if ioc.ioc_forced then begin
 					display_error ctx.com "Forced inline constructor could not be inlined" io.io_pos;
-					display_error ctx.com (compl_msg "Cancellation happened here") p;
+					display_error ~depth:1 ctx.com (compl_msg "Cancellation happened here") p;
 				end
 			| _ -> ()
 		end

+ 2 - 2
src/optimization/optimizer.ml

@@ -345,7 +345,7 @@ let rec reduce_loop ctx e =
 				let cf = mk_field "" ef.etype e.epos null_pos in
 				let ethis = mk (TConst TThis) t_dynamic e.epos in
 				let rt = (match follow ef.etype with TFun (_,rt) -> rt | _ -> die "" __LOC__) in
-				let inl = (try type_inline ctx cf func ethis el rt None e.epos ~self_calling_closure:true false with Error (Custom _,_) -> None) in
+				let inl = (try type_inline ctx cf func ethis el rt None e.epos ~self_calling_closure:true false with Error (Custom _,_,_) -> None) in
 				(match inl with
 				| None -> reduce_expr ctx e
 				| Some e -> reduce_loop ctx e)
@@ -354,7 +354,7 @@ let rec reduce_loop ctx e =
 				| Some {eexpr = TFunction tf} ->
 					let config = inline_config (Some cl) cf el e.etype in
 					let rt = (match follow e1.etype with TFun (_,rt) -> rt | _ -> die "" __LOC__) in
-					let inl = (try type_inline ctx cf tf ef el rt config e.epos false with Error (Custom _,_) -> None) in
+					let inl = (try type_inline ctx cf tf ef el rt config e.epos false with Error (Custom _,_,_) -> None) in
 					(match inl with
 					| None -> reduce_expr ctx e
 					| Some e ->

+ 32 - 32
src/typing/callUnification.ml

@@ -15,10 +15,10 @@ let is_forced_inline c cf =
 
 let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 	let call_error err p =
-		raise (Error (Call_error err,p))
+		raise (Error (Call_error err,p,0))
 	in
 	let arg_error ul name opt p =
-		let err = Stack (ul,Custom ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'")) in
+		let err = Stack [(ul,p); (Custom ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'"), p)] in
 		call_error (Could_not_unify err) p
 	in
 	let mk_pos_infos t =
@@ -42,8 +42,8 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 	let handle_errors fn =
 		try
 			fn()
-		with Error(l,p) when (match l with Call_error _ | Module_not_found _ -> false | _ -> true) ->
-			raise (WithTypeError (l,p))
+		with Error(l,p,nl) when (match l with Call_error _ | Module_not_found _ -> false | _ -> true) ->
+			raise (WithTypeError (l,p,nl))
 	in
 	(* let force_inline, is_extern = match cf with Some(TInst(c,_),f) -> is_forced_inline (Some c) f, (has_class_flag c CExtern) | _ -> false, false in *)
 	let type_against name t e =
@@ -61,7 +61,7 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 			[]
 		| _,[name,false,TAbstract({ a_path = ["cpp"],"Rest" },[t])] ->
 			(try List.map (fun e -> type_against name t e) el
-			with WithTypeError(ul,p) -> arg_error ul name false p)
+			with WithTypeError(ul,p,_) -> arg_error ul name false p)
 		| _,[name,false,t] when ExtType.is_rest (follow t) ->
 			begin match follow t with
 				| TAbstract({a_path=(["haxe"],"Rest")},[arg_t]) ->
@@ -80,28 +80,28 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 						match el with
 						| [(EUnop (Spread,Prefix,e),p)] ->
 							(try [mk (TUnop (Spread, Prefix, type_against name t e)) t p]
-							with WithTypeError(ul,p) -> arg_error ul name false p)
+							with WithTypeError(ul,p,_) -> arg_error ul name false p)
 						| _ when ExtType.is_mono (follow arg_t) ->
 							(try
 								let el = type_rest mk_mono in
 								unify ctx (unify_min ctx el) arg_t (punion_el callp el);
 								el
-							with WithTypeError(ul,p) ->
+							with WithTypeError(ul,p,_) ->
 								arg_error ul name false p)
 						| _ ->
 							(try
 								type_rest (fun() -> arg_t)
-							with WithTypeError(ul,p) ->
+							with WithTypeError(ul,p,_) ->
 								arg_error ul name false p)
 					(* for other platforms make sure rest arguments are wrapped in an array *)
 					else begin
 						match el with
 						| [(EUnop (Spread,Prefix,e),p)] ->
 							(try [type_against name t e]
-							with WithTypeError(ul,p) -> arg_error ul name false p)
+							with WithTypeError(ul,p,_) -> arg_error ul name false p)
 						| [] ->
 							(try [type_against name t (EArrayDecl [],callp)]
-							with WithTypeError(ul,p) -> arg_error ul name false p)
+							with WithTypeError(ul,p,_) -> arg_error ul name false p)
 						| (_,p1) :: _ ->
 							let p =
 								List.fold_left (fun p (e1,p2) ->
@@ -121,7 +121,7 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 									do_type (ECheckType(e,(CTPath tp, p)),p) (* ([arg1, arg2...]:Array<Dynamic>) *)
 								end else
 									do_type e
-							with WithTypeError(ul,p) ->
+							with WithTypeError(ul,p,_) ->
 								arg_error ul name false p
 							)
 					end
@@ -158,7 +158,7 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 				let e = type_against name t e in
 				e :: loop el args
 			with
-				WithTypeError (ul,p)->
+				WithTypeError (ul,p,_)->
 					if opt && might_skip then
 						let e_def = skip name ul t p in
 						e_def :: loop (e :: el) args
@@ -199,15 +199,15 @@ let unify_typed_args ctx tmap args el_typed call_pos =
 		match args,el with
 		| [], _ :: _ ->
 			let call_error = Call_error(Too_many_arguments) in
-			raise(Error(call_error,call_pos))
+			raise(Error(call_error,call_pos,0))
 		| _, [] ->
 			List.rev acc_args,args
 		| ((_,opt,t0) as arg) :: args,e :: el ->
 			begin try
 				unify_raise (tmap e.etype) t0 e.epos;
-			with Error(Unify _ as msg,p) ->
+			with Error(Unify _ as msg,p,nl) ->
 				let call_error = Call_error(Could_not_unify msg) in
-				raise(Error(call_error,p))
+				raise(Error(call_error,p,nl))
 			end;
 			loop (arg :: acc_args) (fun t -> t) args el
 	in
@@ -310,13 +310,13 @@ let unify_field_call ctx fa el_typed el p inline =
 			typing_error (s_type (print_context()) t ^ " cannot be called") p
 	in
 	let maybe_raise_unknown_ident cerr p =
-		let rec loop err =
+		let rec loop err p =
 			match err with
-			| Unknown_ident _ -> typing_error (error_msg err) p
-			| Stack (e1,e2) -> (loop e1; loop e2)
+			| Unknown_ident _ -> located_typing_error (error_msg p err)
+			| Stack stack -> List.iter (fun (e,p) -> loop e p) stack
 			| _ -> ()
 		in
-		match cerr with Could_not_unify err -> loop err | _ -> ()
+		match cerr with Could_not_unify err -> loop err p | _ -> ()
 	in
 	let attempt_calls candidates =
 		let rec loop candidates = match candidates with
@@ -334,7 +334,7 @@ let unify_field_call ctx fa el_typed el p inline =
 						candidate :: candidates,failures
 					end else
 						[candidate],[]
-				with Error ((Call_error cerr as err),p) ->
+				with Error ((Call_error cerr as err),p,_) ->
 					List.iter (fun (m,t,constr) ->
 						if t != m.tm_type then m.tm_type <- t;
 						if constr != m.tm_down_constraints then m.tm_down_constraints <- constr;
@@ -380,17 +380,17 @@ let unify_field_call ctx fa el_typed el p inline =
 				Option.may (fun de ->
 					raise_augmented_display_exception cf de;
 				) delayed_display;
-				cf,error_msg err,p
+				cf,error_msg p err
 			) failures in
-			let failures = remove_duplicates (fun (_,msg1,_) (_,msg2,_) -> msg1 <> msg2) failures in
+			let failures = remove_duplicates (fun (_,msg1) (_,msg2) -> msg1 <> msg2) failures in
 			begin match failures with
-			| [_,msg,p] ->
-				typing_error msg p
+			| [_,msg] ->
+				located_typing_error msg
 			| _ ->
 				display_error ctx.com "Could not find a suitable overload, reasons follow" p;
-				List.iter (fun (cf,msg,p2) ->
-					display_error ctx.com ("Overload resolution failed for " ^ (s_type (print_context()) cf.cf_type)) p;
-					display_error ctx.com msg p2;
+				List.iter (fun (cf,msg) ->
+					display_error ~depth:1 ctx.com ("Overload resolution failed for " ^ (s_type (print_context()) cf.cf_type)) p;
+					located_display_error ~depth:2 ctx.com msg;
 				) failures;
 				typing_error "End of overload failure reasons" p
 			end
@@ -404,7 +404,7 @@ let unify_field_call ctx fa el_typed el p inline =
 				display_error ctx.com "Ambiguous overload, candidates follow" p;
 				let st = s_type (print_context()) in
 				List.iter (fun fcc ->
-					display_error ctx.com (Printf.sprintf "... %s" (st fcc.fc_type)) fcc.fc_field.cf_name_pos;
+					display_error ~depth:1 ctx.com (compl_msg (st fcc.fc_type)) fcc.fc_field.cf_name_pos;
 				) (fcc :: l);
 				commit_delayed_display fcc
 		end else begin match List.rev candidates with
@@ -471,13 +471,13 @@ object(self)
 		ctx.macro_depth <- ctx.macro_depth - 1;
 		ctx.with_type_stack <- List.tl ctx.with_type_stack;
 		let old = ctx.com.error in
-		ctx.com.error <- (fun msg ep ->
+		ctx.com.error <- (fun ?(depth=0) msg ep ->
 			(* 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
 				locate_macro_error := false;
-				old msg ep;
+				old msg (if ep = null_pos then p else ep);
 				locate_macro_error := true;
-				old (compl_msg "Called from macro here") p;
+				if ep <> null_pos then old ~depth:(depth+1) (compl_msg "Called from macro here") p;
 			end else
 				old msg ep;
 		);
@@ -619,4 +619,4 @@ let maybe_reapply_overload_call ctx e =
 				e
 			end
 		| _ ->
-			e
+			e

+ 1 - 1
src/typing/calls.ml

@@ -485,7 +485,7 @@ let array_access ctx e1 e2 mode p =
 				let t = ctx.t.tarray pt in
 				begin try
 					unify_raise et t p
-				with Error(Unify _,_) ->
+				with Error(Unify _,_,_) ->
 					if not ctx.untyped then begin
 						let msg = if !has_abstract_array_access then
 							"No @:arrayAccess function accepts an argument of " ^ (s_type (print_context()) e2.etype)

+ 3 - 3
src/typing/fields.ml

@@ -76,7 +76,7 @@ let no_abstract_constructor c p =
 	if has_class_flag c CAbstract then raise_typing_error (Abstract_class (TClassDecl c)) p
 
 let check_constructor_access ctx c f p =
-	if (Meta.has Meta.CompilerGenerated f.cf_meta) then display_error ctx.com (error_msg (No_constructor (TClassDecl c))) p;
+	if (Meta.has Meta.CompilerGenerated f.cf_meta) then located_display_error ctx.com (error_msg p (No_constructor (TClassDecl c)));
 	if not (can_access ctx c f true || extends ctx.curclass c) && not ctx.untyped then display_error ctx.com (Printf.sprintf "Cannot access private constructor of %s" (s_class_path c)) p
 
 let check_no_closure_meta ctx cf fa mode p =
@@ -438,7 +438,7 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) =
 							end
 						| _ ->
 							check()
-					with Unify_error el | Error (Unify el,_) ->
+					with Unify_error el | Error (Unify el,_,_) ->
 						check_constant_struct := !check_constant_struct || List.exists (function
 							| Has_extra_field _ -> true
 							| _ -> false
@@ -629,4 +629,4 @@ let get_struct_init_anon_fields c tl =
 			PMap.add cf.cf_name cf fields
 		| _ ->
 			fields
-	) c.cl_fields PMap.empty
+	) c.cl_fields PMap.empty

+ 5 - 5
src/typing/forLoop.ml

@@ -92,9 +92,9 @@ module IterationKind = struct
 			| TDynamic _ | TMono _ ->
 				(* try to find something better than a dynamic value to iterate on *)
 				dynamic_iterator := Some e;
-				raise (Error (Unify [Unify_custom "Avoid iterating on a dynamic value"], p))
+				raise (Error (Unify [Unify_custom "Avoid iterating on a dynamic value"], p, 0))
 			| _ -> e
-		with Error (Unify _,_) ->
+		with Error (Unify _,_,depth) ->
 			let try_last_resort after =
 				try
 					match last_resort with
@@ -108,14 +108,14 @@ module IterationKind = struct
 				try
 					unify_raise acc_expr.etype t acc_expr.epos;
 					acc_expr
-				with Error (Unify(l),p) ->
+				with Error (Unify(l),p,n) ->
 					try_last_resort (fun () ->
 						match !dynamic_iterator with
 						| Some e -> e
 						| None ->
 							if resume then raise Not_found;
-							display_error ctx.com "Field iterator has an invalid type" acc_expr.epos;
-							display_error ctx.com (error_msg (Unify l)) p;
+							display_error ~depth ctx.com "Field iterator has an invalid type" acc_expr.epos;
+							located_display_error ~depth:(depth+1) ctx.com (error_msg p (Unify l));
 							mk (TConst TNull) t_dynamic p
 					)
 			in

+ 7 - 7
src/typing/generic.ml

@@ -156,7 +156,7 @@ let static_method_container gctx c cf p =
 		match t with
 		| TInst(cg,_) -> cg
 		| _ -> typing_error ("Cannot specialize @:generic static method because the generated type name is already used: " ^ name) p
-	with Error(Module_not_found path,_) when path = (pack,name) ->
+	with Error(Module_not_found path,_,_) when path = (pack,name) ->
 		let m = (try ctx.com.module_lut#find (ctx.com.type_to_module#find c.cl_path) with Not_found -> die "" __LOC__) in
 		let mg = {
 			m_id = alloc_mid();
@@ -233,7 +233,7 @@ let rec build_generic_class ctx c p tl =
 		match t with
 		| TInst({ cl_kind = KGenericInstance (csup,_) },_) when c == csup -> t
 		| _ -> typing_error ("Cannot specialize @:generic because the generated type name is already used: " ^ name) p
-	with Error(Module_not_found path,_) when path = (pack,name) ->
+	with Error(Module_not_found path,_,_) when path = (pack,name) ->
 		let m = (try ctx.com.module_lut#find (ctx.com.type_to_module#find c.cl_path) with Not_found -> die "" __LOC__) in
 		ignore(c.cl_build()); (* make sure the super class is already setup *)
 		let mg = {
@@ -300,7 +300,7 @@ let rec build_generic_class ctx c p tl =
 					| Some e ->
 						cf_new.cf_expr <- Some (generic_substitute_expr gctx e)
 				) with Unify_error l ->
-					typing_error (error_msg (Unify l)) cf_new.cf_pos
+					located_typing_error (error_msg cf_new.cf_pos (Unify l))
 				end;
 				t
 			in
@@ -383,9 +383,9 @@ let type_generic_function ctx fa fcc with_type p =
 		let name = cf.cf_name ^ "_" ^ gctx.name in
 		let unify_existing_field tcf pcf = try
 			unify_raise tcf fcc.fc_type p
-		with Error(Unify _,_) as err ->
-			display_error ctx.com ("Cannot create field " ^ name ^ " due to type mismatch") p;
-			display_error ctx.com (compl_msg "Conflicting field was defined here") pcf;
+		with Error(Unify _,_,depth) as err ->
+			display_error ~depth ctx.com ("Cannot create field " ^ name ^ " due to type mismatch") p;
+			display_error ~depth:(depth+1) ctx.com (compl_msg "Conflicting field was defined here") pcf;
 			raise err
 		in
 		let fa = try
@@ -472,4 +472,4 @@ let type_generic_function ctx fa fcc with_type p =
 		typing_error msg p)
 
 ;;
-Typecore.type_generic_function_ref := type_generic_function
+Typecore.type_generic_function_ref := type_generic_function

+ 13 - 13
src/typing/macroContext.ml

@@ -88,7 +88,7 @@ let typing_timer ctx need_type f =
 		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 e p -> raise (Error(Custom e,p)));
+	ctx.com.error <- (fun ?(depth=0) e p -> raise (Error(Custom e,p,depth)));
 	if need_type && ctx.pass < PTypeField then begin
 		ctx.pass <- PTypeField;
 		flush_pass ctx PBuildClass "typing_timer";
@@ -103,12 +103,12 @@ let typing_timer ctx need_type f =
 		let r = f() in
 		exit();
 		r
-	with Error (ekind,p) ->
+	with Error (ekind,p,_) ->
 			exit();
-			Interp.compiler_error (error_msg ekind) p
-		| WithTypeError (l,p) ->
+			Interp.compiler_error (error_msg p ekind)
+		| WithTypeError (l,p,_) ->
 			exit();
-			Interp.compiler_error (error_msg l) p
+			Interp.compiler_error (error_msg p l)
 		| e ->
 			exit();
 			raise e
@@ -146,7 +146,7 @@ let make_macro_api ctx p =
 				try
 					let m = Some (Typeload.load_instance ctx (tp,p) true) in
 					m
-				with Error (Module_not_found _,p2) when p == p2 ->
+				with Error (Module_not_found _,p2,_) when p == p2 ->
 					None
 			)
 		);
@@ -352,7 +352,7 @@ let make_macro_api ctx p =
 				try
 					ignore(AbstractCast.cast_or_unify_raise ctx t e p);
 					true
-				with Error (Unify _,_) ->
+				with Error (Unify _,_,_) ->
 					false
 			)
 		);
@@ -422,8 +422,8 @@ let make_macro_api ctx p =
 			in
 			Std.finally restore f ()
 		);
-		MacroApi.warning = (fun w msg p ->
-			warning ctx w msg p
+		MacroApi.warning = (fun ?(depth=0) w msg p ->
+			warning ~depth ctx w msg p
 		);
 	}
 
@@ -493,7 +493,7 @@ and flush_macro_context mint ctx =
 		List.iter (fun f -> f t) type_filters
 	in
 	(try Interp.add_types mint types ready
-	with Error (e,p) -> t(); raise (Fatal_error(error_msg e,p)));
+	with Error (e,p,n) -> t(); raise (Fatal_error(error_msg p e,n)));
 	t()
 
 let create_macro_interp ctx mctx =
@@ -508,7 +508,7 @@ let create_macro_interp ctx mctx =
 			mint, (fun() -> ())
 	) in
 	let on_error = com2.error in
-	com2.error <- (fun e p ->
+	com2.error <- (fun ?depth e p ->
 		Interp.set_error (Interp.get_ctx()) true;
 		macro_interp_cache := None;
 		on_error e p
@@ -664,7 +664,7 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p =
 			unify_raise mret ttype mpos;
 			(* TODO: enable this again in the future *)
 			(* warning ctx WDeprecated "Returning Type from @:genericBuild macros is deprecated, consider returning ComplexType instead" p; *)
-		with Error (Unify _,_) ->
+		with Error (Unify _,_,_) ->
 			let cttype = mk_type_path ~sub:"ComplexType" (["haxe";"macro"],"Expr") in
 			let ttype = Typeload.load_instance mctx (cttype,p) false in
 			unify_raise mret ttype mpos;
@@ -699,7 +699,7 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p =
 		*)
 		let eargs = List.map (fun (n,o,t) ->
 			try unify_raise t expr p; (n, o, t_dynamic), MAExpr
-			with Error (Unify _,_) -> match follow t with
+			with Error (Unify _,_,_) -> match follow t with
 				| TFun _ ->
 					(n,o,t), MAFunction
 				| _ ->

+ 1 - 1
src/typing/magicTypes.ml

@@ -22,7 +22,7 @@ let extend_remoting ctx c t p async prot =
 	let t = (try
 		load_type_def ctx p (mk_type_path (fst path,new_name))
 	with
-		Error (Module_not_found _,p2) when p == p2 ->
+		Error (Module_not_found _,p2,_) when p == p2 ->
 	(* build it *)
 	Common.log ctx.com ("Building proxy for " ^ s_type_path path);
 	let file, decls = (try

+ 1 - 1
src/typing/matcher.ml

@@ -271,7 +271,7 @@ module Pattern = struct
 		let catch_errors () =
 			let old = ctx.com.error in
 			let restore_report_mode = disable_report_mode ctx.com in
-			ctx.com.error <- (fun _ _ ->
+			ctx.com.error <- (fun ?depth _ _ ->
 				raise Exit
 			);
 			(fun () ->

+ 6 - 6
src/typing/operators.ml

@@ -203,7 +203,7 @@ let make_binop ctx op e1 e2 is_assign_op with_type p =
 			| KAbstract (a,tl) ->
 				try
 					AbstractCast.cast_or_unify_raise ctx tstring e p
-				with Error (Unify _,_) ->
+				with Error (Unify _,_,_) ->
 					loop (Abstract.get_underlying_type a tl)
 		in
 		loop e.etype
@@ -330,7 +330,7 @@ let make_binop ctx op e1 e2 is_assign_op with_type p =
 			(* we only have to check one type here, because unification fails if one is Void and the other is not *)
 			(match follow e2.etype with TAbstract({a_path=[],"Void"},_) -> typing_error "Cannot compare Void" p | _ -> ());
 			AbstractCast.cast_or_unify_raise ctx e2.etype e1 p,e2
-		with Error (Unify _,_) ->
+		with Error (Unify _,_,_) ->
 			e1,AbstractCast.cast_or_unify ctx e1.etype e2 p
 		in
 		if not ctx.com.config.pf_supports_function_equality then begin match e1.eexpr, e2.eexpr with
@@ -410,13 +410,13 @@ let find_abstract_binop_overload ctx op e1 e2 a c tl left is_assign_op with_type
 				let t_expected = BinopResult.get_type result in
 				begin try
 					unify_raise tret t_expected p
-				with Error (Unify _,_) ->
+				with Error (Unify _,_,depth) ->
 					match follow tret with
 						| TAbstract(a,tl) when type_iseq (Abstract.get_underlying_type a tl) t_expected ->
 							()
 						| _ ->
 							let st = s_type (print_context()) in
-							typing_error (Printf.sprintf "The result of this operation (%s) is not compatible with declared return type %s" (st t_expected) (st tret)) p
+							typing_error ~depth (Printf.sprintf "The result of this operation (%s) is not compatible with declared return type %s" (st t_expected) (st tret)) p
 				end;
 			end;
 			(*
@@ -490,10 +490,10 @@ let find_abstract_binop_overload ctx op e1 e2 a c tl left is_assign_op with_type
 					in
 					begin try
 						check e1 e2 false
-					with Error (Unify _,_) | Unify_error _ -> try
+					with Error (Unify _,_,_) | Unify_error _ -> try
 						if not (Meta.has Meta.Commutative cf.cf_meta) then raise Not_found;
 						check e2 e1 true
-					with Not_found | Error (Unify _,_) | Unify_error _ ->
+					with Not_found | Error (Unify _,_,_) | Unify_error _ ->
 						loop find_op ol
 					end
 				| _ ->

+ 15 - 15
src/typing/typeload.ml

@@ -59,7 +59,7 @@ let check_field_access ctx cff =
 				let _,p2 = List.find (fun (access',_) -> access = access') acc in
 				if p1 <> null_pos && p2 <> null_pos then begin
 					display_error ctx.com (Printf.sprintf "Duplicate access modifier %s" (Ast.s_access access)) p1;
-					display_error ctx.com (compl_msg "Previously defined here") p2;
+					display_error ~depth:1 ctx.com (compl_msg "Previously defined here") p2;
 				end;
 				loop p1 acc l
 			with Not_found -> match access with
@@ -67,7 +67,7 @@ let check_field_access ctx cff =
 					begin try
 						let _,p2 = List.find (fun (access',_) -> match access' with APublic | APrivate -> true | _ -> false) acc in
 						display_error ctx.com (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1;
-						display_error ctx.com (compl_msg "Conflicts with this") p2;
+						display_error ~depth:1 ctx.com (compl_msg "Conflicts with this") p2;
 						loop p1 acc l
 					with Not_found ->
 						loop p1 ((access,p1) :: acc) l
@@ -109,7 +109,7 @@ let load_type_raise ctx mpath tname p =
 (* raises Not_found *)
 let load_type ctx mpath tname p = try
 	load_type_raise ctx mpath tname p
-with Error((Module_not_found _ | Type_not_found _),p2) when p = p2 ->
+with Error((Module_not_found _ | Type_not_found _),p2,_) when p = p2 ->
 	raise Not_found
 
 (** since load_type_def and load_instance are used in PASS2, they should not access the structure of a type **)
@@ -142,7 +142,7 @@ let find_in_wildcard_imports ctx mname p f =
 				let m =
 					try
 						ctx.g.do_load_module ctx path p
-					with Error (Module_not_found mpath,_) when mpath = path ->
+					with Error (Module_not_found mpath,_,_) when mpath = path ->
 						raise Not_found
 				in
 				let r = f m ~resume:true in
@@ -163,7 +163,7 @@ let find_in_modules_starting_from_current_package ~resume ctx mname p f =
 			let m =
 				try
 					ctx.g.do_load_module ctx path p
-				with Error (Module_not_found mpath,_) when resume && mpath = path ->
+				with Error (Module_not_found mpath,_,_) when resume && mpath = path ->
 					raise Not_found
 			in
 			f m ~resume:resume
@@ -172,7 +172,7 @@ let find_in_modules_starting_from_current_package ~resume ctx mname p f =
 				let m =
 					try
 						ctx.g.do_load_module ctx path p
-					with Error (Module_not_found mpath,_) when mpath = path ->
+					with Error (Module_not_found mpath,_,_) when mpath = path ->
 						raise Not_found
 					in
 				f m ~resume:true;
@@ -200,7 +200,7 @@ let load_unqualified_type_def ctx mname tname p =
 let load_module ctx path p =
 	try
 		ctx.g.do_load_module ctx path p
-	with Error (Module_not_found mpath,_) as exc when mpath = path ->
+	with Error (Module_not_found mpath,_,_) as exc when mpath = path ->
 		match path with
 		| ("std" :: pack, name) ->
 			ctx.g.do_load_module ctx (pack,name) p
@@ -286,15 +286,15 @@ let check_param_constraints ctx t map c p =
 			let ti = map ti in
 			try
 				unify_raise t ti p
-			with Error(Unify l,p) ->
+			with Error(Unify l,p,n) ->
 				let fail() =
-					if not ctx.untyped then display_error ctx.com (error_msg (Unify (Constraint_failure (s_type_path c.cl_path) :: l))) p;
+					if not ctx.untyped then located_display_error ctx.com (error_msg p (Unify (Constraint_failure (s_type_path c.cl_path) :: l)));
 				in
 				match follow t with
 				| TInst({cl_kind = KExpr e},_) ->
 					let e = type_expr {ctx with locals = PMap.empty} e (WithType.with_type ti) in
 					begin try unify_raise e.etype ti p
-					with Error (Unify _,_) -> fail() end
+					with Error (Unify _,_,_) -> fail() end
 				| _ ->
 					fail()
 
@@ -437,7 +437,7 @@ and load_instance ctx ?(allow_display=false) ((_,pn) as tp) allow_no_params =
 		let t = load_instance' ctx tp allow_no_params in
 		if allow_display then DisplayEmitter.check_display_type ctx t tp;
 		t
-	with Error (Module_not_found path,_) when ctx.macro_depth <= 0 && (ctx.com.display.dms_kind = DMDefault) && DisplayPosition.display_position#enclosed_in pn ->
+	with Error (Module_not_found path,_,_) when ctx.macro_depth <= 0 && (ctx.com.display.dms_kind = DMDefault) && DisplayPosition.display_position#enclosed_in pn ->
 		let s = s_type_path path in
 		DisplayToplevel.collect_and_raise ctx TKType NoValue CRTypeHint (s,pn) (patch_string_pos pn s)
 
@@ -627,7 +627,7 @@ and load_complex_type' ctx allow_display (t,p) =
 and load_complex_type ctx allow_display (t,pn) =
 	try
 		load_complex_type' ctx allow_display (t,pn)
-	with Error(Module_not_found(([],name)),p) as exc ->
+	with Error(Module_not_found(([],name)),p,_) as exc ->
 		if Diagnostics.error_in_diagnostics_run ctx.com p then begin
 			delay ctx PForce (fun () -> DisplayToplevel.handle_unresolved_identifier ctx name p true);
 			t_dynamic
@@ -862,7 +862,7 @@ let init_core_api ctx c =
 						typing_error "Type parameters must have the same number of constraints as core type" c.cl_pos
 					| Unify_error l ->
 						display_error ctx.com ("Type parameter " ^ tp2.ttp_name ^ " has different constraint than in core type") c.cl_pos;
-						display_error ctx.com (error_msg (Unify l)) c.cl_pos
+						located_display_error ctx.com (error_msg c.cl_pos (Unify l));
 				end
 			| t1,t2 ->
 				Printf.printf "%s %s" (s_type (print_context()) t1) (s_type (print_context()) t2);
@@ -880,7 +880,7 @@ let init_core_api ctx c =
 			type_eq EqCoreType (apply_params ccore.cl_params (extract_param_types c.cl_params) f.cf_type) f2.cf_type
 		with Unify_error l ->
 			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);
+			located_display_error ctx.com (error_msg p (Unify l)));
 		if (has_class_field_flag f2 CfPublic) <> (has_class_field_flag f CfPublic) then typing_error ("Field " ^ f.cf_name ^ " has different visibility than core type") p;
 		(match f2.cf_doc with
 		| None -> f2.cf_doc <- f.cf_doc
@@ -922,4 +922,4 @@ let init_core_api ctx c =
 
 let string_list_of_expr_path (e,p) =
 	try string_list_of_expr_path_raise (e,p)
-	with Exit -> typing_error "Invalid path" p
+	with Exit -> typing_error "Invalid path" p

+ 8 - 8
src/typing/typeloadCheck.ml

@@ -153,7 +153,7 @@ let get_native_name meta =
 let check_native_name_override ctx child base =
 	let error base_pos child_pos =
 		display_error ctx.com ("Field " ^ child.cf_name ^ " has different @:native value than in superclass") child_pos;
-		display_error ctx.com (compl_msg "Base field is defined here") base_pos
+		display_error ~depth:1 ctx.com (compl_msg "Base field is defined here") base_pos
 	in
 	try
 		let child_name, child_pos = get_native_name child.cf_meta in
@@ -212,8 +212,8 @@ let check_overriding ctx c f =
 			with
 				Unify_error l ->
 					display_error ctx.com ("Field " ^ i ^ " overrides parent class with different or incomplete type") p;
-					display_error ctx.com (compl_msg "Base field is defined here") f2.cf_name_pos;
-					display_error ctx.com (compl_msg (error_msg (Unify l))) p;
+					display_error ~depth:1 ctx.com (compl_msg "Base field is defined here") f2.cf_name_pos;
+					located_display_error ~depth:1 ctx.com (compl_located_msg (error_msg p (Unify l)));
 		with
 			Not_found ->
 				if has_class_field_flag f CfOverride then
@@ -397,8 +397,8 @@ module Inheritance = struct
 						Unify_error l ->
 							if not (Meta.has Meta.CsNative c.cl_meta && (has_class_flag c CExtern)) then begin
 								display_error ctx.com ("Field " ^ f.cf_name ^ " has different type than in " ^ s_type_path intf.cl_path) p;
-								display_error ctx.com (compl_msg "Interface field is defined here") f.cf_pos;
-								display_error ctx.com (compl_msg (error_msg (Unify l))) p;
+								display_error ~depth:1 ctx.com (compl_msg "Interface field is defined here") f.cf_pos;
+								located_display_error ~depth:1 ctx.com (compl_located_msg (error_msg p (Unify l)));
 							end
 				)
 			with Not_found ->
@@ -498,7 +498,7 @@ module Inheritance = struct
 					| t ->
 						s_type pctx t
 				in
-				display_error ctx.com (Printf.sprintf "... %s(%s)" cf.cf_name s) cf.cf_name_pos
+				display_error ~depth:1 ctx.com (compl_msg (Printf.sprintf "%s(%s)" cf.cf_name s)) cf.cf_name_pos
 			) (List.rev !missing)
 
 	let set_heritance ctx c herits p =
@@ -627,7 +627,7 @@ module Inheritance = struct
 					raise_fields l (if is_extends then CRExtends else CRImplements) r.fsubject
 				in
 				Some (check_herit t is_extends p)
-			with Error(Module_not_found(([],name)),p) when ctx.com.display.dms_kind <> DMNone ->
+			with Error(Module_not_found(([],name)),p,_) when ctx.com.display.dms_kind <> DMNone ->
 				if Diagnostics.error_in_diagnostics_run ctx.com p then DisplayToplevel.handle_unresolved_identifier ctx name p true;
 				None
 		) herits in
@@ -654,4 +654,4 @@ let check_final_vars ctx e =
 		Hashtbl.iter (fun _ cf ->
 			display_error ctx.com ("final field " ^ cf.cf_name ^ " must be initialized immediately or in the constructor") cf.cf_pos;
 		) final_vars
-	end
+	end

+ 12 - 12
src/typing/typeloadFields.ml

@@ -418,7 +418,7 @@ let build_enum_abstract ctx c a fields p =
 					()
 				| VPublic(access,p2) | VPrivate(access,p2) ->
 					display_error ctx.com (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1;
-					display_error ctx.com "Conflicts with this" p2;
+					display_error ~depth:1 ctx.com "Conflicts with this" p2;
 			in
 			let rec loop visibility acc = match acc with
 				| (AExtern,p) :: acc ->
@@ -486,7 +486,7 @@ let build_module_def ctx mt meta fvars context_init fbuild =
 					| [ECall (epath,el),p] -> epath, el
 					| _ -> typing_error "Invalid build parameters" p
 				) in
-				let s = try String.concat "." (List.rev (string_list_of_expr_path epath)) with Error (_,p) -> typing_error "Build call parameter must be a class path" p in
+				let s = try String.concat "." (List.rev (string_list_of_expr_path epath)) with Error (_,p,depth) -> typing_error ~depth "Build call parameter must be a class path" p in
 				if ctx.com.is_macro_context then typing_error "You cannot use @:build inside a macro : make sure that your type is not used in macro" p;
 				let old = ctx.get_build_infos in
 				ctx.get_build_infos <- (fun() -> Some (mt, extract_param_types (t_infos mt).mt_params, fvars()));
@@ -1066,7 +1066,7 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p =
 					let r = exc_protect ctx (fun r ->
 						r := lazy_processing (fun () -> t);
 						(* the return type of a from-function must be the abstract, not the underlying type *)
-						if not fctx.is_macro then (try type_eq EqStrict ret ta with Unify_error l -> typing_error (error_msg (Unify l)) p);
+						if not fctx.is_macro then (try type_eq EqStrict ret ta with Unify_error l -> located_typing_error (error_msg p (Unify l)));
 						match t with
 							| TFun([_,_,t],_) -> t
 							| TFun([(_,_,t1);(_,true,t2)],_) when is_pos_infos t2 -> t1
@@ -1095,7 +1095,7 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p =
 					(* TODO: this doesn't seem quite right... *)
 					if not (has_class_field_flag cf CfImpl) then add_class_field_flag cf CfImpl;
 					let resolve_m args =
-						(try unify_raise t (tfun (tthis :: args) m) cf.cf_pos with Error (Unify l,p) -> typing_error (error_msg (Unify l)) p);
+						(try unify_raise t (tfun (tthis :: args) m) cf.cf_pos with Error (Unify l,p,depth) -> located_typing_error ~depth (error_msg p (Unify l)));
 						match follow m with
 							| TMono _ when (match t with TFun(_,r) -> r == t_dynamic | _ -> false) -> t_dynamic
 							| m -> m
@@ -1159,7 +1159,7 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p =
 				| (Meta.Op,[EUnop(op,flag,_),_],_) :: _ ->
 					if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "operator function" p;
 					let targ = if fctx.is_abstract_member then tthis else ta in
-					(try type_eq EqStrict t (tfun [targ] (mk_mono())) with Unify_error l -> raise (Error ((Unify l),cf.cf_pos)));
+					(try type_eq EqStrict t (tfun [targ] (mk_mono())) with Unify_error l -> raise (Error ((Unify l),cf.cf_pos,0)));
 					a.a_unops <- (op,flag,cf) :: a.a_unops;
 					allow_no_expr();
 				| (Meta.Op,[ECall _,_],_) :: _ ->
@@ -1501,13 +1501,13 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p =
 					(match f2.cf_kind with
 						| Method MethMacro ->
 							display_error ctx.com (f2.cf_name ^ ": Macro methods cannot be used as property accessor") p;
-							display_error ctx.com (compl_msg (f2.cf_name ^ ": Accessor method is here")) f2.cf_pos;
+							display_error ~depth:1 ctx.com (compl_msg (f2.cf_name ^ ": Accessor method is here")) 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
 						display_error ctx.com "Mixing abstract implementation and static properties/accessors is not allowed" f2.cf_pos;
-				with Error (Unify l,p) ->
-					raise (Error (Stack (Custom ("In method " ^ m ^ " required by property " ^ name),Unify l),p))
+				with Error (Unify l,p,depth) ->
+					raise (Error (Stack [(Custom ("In method " ^ m ^ " required by property " ^ name),p);(Unify l,p)],p,depth+1))
 			)
 		with
 			| Not_found ->
@@ -1659,7 +1659,7 @@ let check_overload ctx f fs =
 			) fs
 		in
 		display_error ctx.com ("Another overloaded field of same signature was already declared : " ^ f.cf_name) f.cf_pos;
-		display_error ctx.com (compl_msg "The second field is declared here") f2.cf_pos;
+		display_error ~depth:1 ctx.com (compl_msg "The second field is declared here") f2.cf_pos;
 		false
 	with Not_found -> try
 		(* OVERLOADTODO: generalize this and respect whether or not we actually generate the functions *)
@@ -1676,7 +1676,7 @@ let check_overload ctx f fs =
 			f.cf_name ^
 			"\nThe signatures are different in Haxe, but not in the target language"
 		) f.cf_pos;
-		display_error ctx.com (compl_msg "The second field is declared here") f2.cf_pos;
+		display_error ~depth:1 ctx.com (compl_msg "The second field is declared here") f2.cf_pos;
 		false
 	with Not_found ->
 		true
@@ -1825,8 +1825,8 @@ let init_class ctx c p context_init herits fields =
 				else
 				if fctx.do_add then TClass.add_field c cf
 			end
-		with Error (Custom str,p2) when p = p2 ->
-			display_error ctx.com str p
+		with Error (Custom str,p2,depth) when p = p2 ->
+			display_error ~depth ctx.com str p
 	) fields;
 	(match cctx.abstract with
 	| Some a ->

+ 4 - 4
src/typing/typeloadModule.ml

@@ -70,7 +70,7 @@ module ModuleLevel = struct
 			DeprecationCheck.check_is com name meta p;
 			let error prev_pos =
 				display_error ctx.com ("Name " ^ name ^ " is already defined in this module") p;
-				typing_error (compl_msg "Previous declaration here") prev_pos;
+				typing_error ~depth:1 (compl_msg "Previous declaration here") prev_pos;
 			in
 			List.iter (fun (t2,(_,p2)) ->
 				if snd (t_path t2) = name then error (t_infos t2).mt_name_pos
@@ -677,8 +677,8 @@ module TypeLevel = struct
 				check_path_display path p;
 				ImportHandling.init_import ctx context_init path mode p;
 				ImportHandling.commit_import ctx path mode p;
-			with Error(err,p) ->
-				display_error ctx.com (Error.error_msg err) p
+			with Error(err,p,depth) ->
+				located_display_error ~depth ctx.com (Error.error_msg p err)
 			end
 		| EUsing path ->
 			check_path_display path p;
@@ -797,7 +797,7 @@ let load_module' ctx g m p =
 			m
 		| None ->
 			let raise_not_found () =
-				raise (Error (Module_not_found m,p))
+				raise (Error (Module_not_found m,p,0))
 			in
 			if ctx.com.module_nonexistent_lut#mem m then raise_not_found();
 			if ctx.g.load_only_cached_modules then raise_not_found();

+ 1 - 1
src/typing/typeloadParse.ml

@@ -270,7 +270,7 @@ let handle_parser_result com p result =
 		let msg = Parser.error_msg msg in
 		match com.display.dms_error_policy with
 			| EPShow ->
-				if is_diagnostics com then add_diagnostics_message com msg p DKParserError Error
+				if is_diagnostics com then add_diagnostics_message com (located msg p) DKParserError Error
 				else typing_error msg p
 			| EPIgnore ->
 				com.has_error <- true

+ 24 - 24
src/typing/typer.ml

@@ -97,7 +97,7 @@ let maybe_type_against_enum ctx f with_type iscall p =
 			let e = try
 				f()
 			with
-			| Error (Unknown_ident n,_) ->
+			| Error (Unknown_ident n,_,_) ->
 				restore();
 				raise_or_display_message ctx (StringError.string_error n fields ("Identifier '" ^ n ^ "' is not part of " ^ s_type_path path)) p;
 				AKExpr (mk (TConst TNull) (mk_mono()) p)
@@ -128,7 +128,7 @@ let check_error ctx err p = match err with
 	| Module_not_found ([],name) when Diagnostics.error_in_diagnostics_run ctx.com p ->
 		DisplayToplevel.handle_unresolved_identifier ctx name p true
 	| _ ->
-		display_error ctx.com (error_msg err) p
+		located_display_error ctx.com (error_msg p err)
 
 (* ---------------------------------------------------------------------- *)
 (* PASS 3 : type expression & check structure *)
@@ -254,8 +254,8 @@ let rec unify_min_raise ctx (el:texpr list) : t =
 
 let unify_min ctx el =
 	try unify_min_raise ctx el
-	with Error (Unify l,p) ->
-		if not ctx.untyped then display_error ctx.com (error_msg (Unify l)) p;
+	with Error (Unify l,p,depth) ->
+		if not ctx.untyped then located_display_error ~depth ctx.com (error_msg p (Unify l));
 		(List.hd el).etype
 
 let unify_min_for_type_source ctx el src =
@@ -471,7 +471,7 @@ and type_ident ctx i p mode with_type =
 	with Not_found -> try
 		(* lookup type *)
 		if is_lower_ident i p then raise Not_found;
-		let e = (try type_type ctx ([],i) p with Error (Module_not_found ([],name),_) when name = i -> raise Not_found) in
+		let e = (try type_type ctx ([],i) p with Error (Module_not_found ([],name),_,_) when name = i -> raise Not_found) in
 		AKExpr e
 	with Not_found ->
 		let resolved_to_type_parameter = ref false in
@@ -499,7 +499,7 @@ and type_ident ctx i p mode with_type =
 				end else begin
 					let err = Unknown_ident i in
 					if ctx.in_display then begin
-						raise (Error (err,p))
+						raise (Error (err,p,0))
 					end;
 					if Diagnostics.error_in_diagnostics_run ctx.com p then begin
 						DisplayToplevel.handle_unresolved_identifier ctx i p false;
@@ -508,9 +508,9 @@ and type_ident ctx i p mode with_type =
 						AKExpr (mk (TIdent i) t p)
 					end else match ctx.com.display.dms_kind with
 						| DMNone ->
-							raise (Error(err,p))
+							raise (Error(err,p,0))
 						| _ ->
-							display_error ctx.com (error_msg err) p;
+							located_display_error ctx.com (error_msg p err);
 							let t = mk_mono() in
 							(* Add a fake local for #8751. *)
 							if !ServerConfig.legacy_completion then
@@ -539,7 +539,7 @@ and handle_efield ctx e p0 mode with_type =
 				try
 					(* TODO: we don't really want to do full type_ident again, just the second part of it *)
 					field_chain ctx pnext (type_ident ctx name p MGet WithType.value)
-				with Error (Unknown_ident _,p2) as e when p = p2 ->
+				with Error (Unknown_ident _,p2,_) as e when p = p2 ->
 					try
 						(* try raising a more sensible error if there was an uppercase-first (module name) part *)
 						begin
@@ -563,9 +563,9 @@ and handle_efield ctx e p0 mode with_type =
 							let mpath = (pack,name) in
 							if ctx.com.module_lut#mem mpath then
 								let tname = Option.default name sub in
-								raise (Error (Type_not_found (mpath,tname,Not_defined),p))
+								raise (Error (Type_not_found (mpath,tname,Not_defined),p,0))
 							else
-								raise (Error (Module_not_found mpath,p))
+								raise (Error (Module_not_found mpath,p,0))
 						end
 					with Not_found ->
 						(* if there was no module name part, last guess is that we're trying to get package completion *)
@@ -713,7 +713,7 @@ and type_vars ctx vl p =
 				DisplayEmitter.display_variable ctx v pv;
 			v,e
 		with
-			Error (e,p) ->
+			Error (e,p,_) ->
 				check_error ctx e p;
 				add_local ctx VGenerated n t_dynamic pv, None (* TODO: What to do with this... *)
 	) vl in
@@ -840,7 +840,7 @@ and type_block ctx el with_type p =
 	let rec loop acc = function
 		| [] -> List.rev acc
 		| e :: l ->
-			let acc = try merge acc (type_expr ctx e (if l = [] then with_type else WithType.no_value)) with Error (e,p) -> check_error ctx e p; acc in
+			let acc = try merge acc (type_expr ctx e (if l = [] then with_type else WithType.no_value)) with Error (e,p,_) -> check_error ctx e p; acc in
 			loop acc l
 	in
 	let l = loop [] el in
@@ -926,7 +926,7 @@ and type_object_decl ctx fl with_type p =
 			(match PMap.foldi (fun n cf acc -> if not (Meta.has Meta.Optional cf.cf_meta) && not (PMap.mem n !fields) then n :: acc else acc) field_map [] with
 				| [] -> ()
 				| [n] -> raise_or_display ctx [Unify_custom ("Object requires field " ^ n)] p
-				| nl -> raise_or_display ctx [Unify_custom ("Object requires fields: " ^ (String.concat ", " nl))] p);
+				| depth -> raise_or_display ctx [Unify_custom ("Object requires fields: " ^ (String.concat ", " depth))] p);
 			(match !extra_fields with
 			| [] -> ()
 			| _ -> raise_or_display ctx (List.map (fun n -> has_extra_field t n) !extra_fields) p);
@@ -1030,8 +1030,8 @@ and type_new ctx path el with_type force_inline p =
 			let fcc = unify_field_call ctx fa [] el p fa.fa_inline in
 			check_constructor_access ctx c fcc.fc_field p;
 			fcc
-		with Error (e,p) ->
-			typing_error (error_msg e) p;
+		with Error (e,p,depth) ->
+			located_typing_error ~depth (error_msg p e);
 	in
 	let display_position_in_el () =
 		List.exists (fun e -> DisplayPosition.display_position#enclosed_in (pos e)) el
@@ -1115,8 +1115,8 @@ and type_new ctx path el with_type force_inline p =
 		mk (TNew (c,params,el)) t p
 	| _ ->
 		typing_error (s_type (print_context()) t ^ " cannot be constructed") p
-	end with Error(No_constructor _ as err,p) when ctx.com.display.dms_kind <> DMNone ->
-		display_error ctx.com (error_msg err) p;
+	end with Error(No_constructor _ as err,p,depth) when ctx.com.display.dms_kind <> DMNone ->
+		located_display_error ~depth ctx.com (error_msg p err);
 		Diagnostics.secure_generated_code ctx (mk (TConst TNull) t p)
 
 and type_try ctx e1 catches with_type p =
@@ -1215,7 +1215,7 @@ and type_map_declaration ctx e1 el with_type p =
 		try
 			let p = Hashtbl.find keys e_key.eexpr in
 			display_error ctx.com "Duplicate key" e_key.epos;
-			typing_error (compl_msg "Previously defined here") p
+			typing_error ~depth:1 (compl_msg "Previously defined here") p
 		with Not_found ->
 			begin match e_key.eexpr with
 			| TConst _ -> Hashtbl.add keys e_key.eexpr e_key.epos;
@@ -1464,12 +1464,12 @@ and type_array_decl ctx el with_type p =
 		let el = List.map (fun e -> type_expr ctx e WithType.value) el in
 		let t = try
 			unify_min_raise ctx el
-		with Error (Unify l,p) ->
+		with Error (Unify l,p,n) ->
 			if !allow_array_dynamic || ctx.untyped || ignore_error ctx.com then
 				t_dynamic
 			else begin
 				display_error ctx.com "Arrays of mixed types are only allowed if the type is forced to Array<Dynamic>" p;
-				raise (Error (Unify l, p))
+				raise (Error (Unify l, p,n))
 			end
 		in
 		mk (TArrayDecl el) (ctx.t.tarray t) p
@@ -1563,7 +1563,7 @@ and type_return ?(implicit=false) ctx e with_type p =
 					]) t e.epos;
 				| _ ->
 					mk (TReturn (Some e)) (mono_or_dynamic ctx with_type p) p
-		with Error(err,p) ->
+		with Error(err,p,_) ->
 			check_error ctx err p;
 			(* If we have a bad return, let's generate a return null expression at least. This surpresses various
 				follow-up errors that come from the fact that the function no longer has a return expression (issue #6445). *)
@@ -1800,7 +1800,7 @@ and type_call_builtin ctx e el mode with_type p =
 			let cf = fa.fa_field in
 			let t = TInst (c,params) in
 			let e = mk (TConst TSuper) t sp in
-			if (Meta.has Meta.CompilerGenerated cf.cf_meta) then display_error ctx.com (error_msg (No_constructor (TClassDecl c))) p;
+			if (Meta.has Meta.CompilerGenerated cf.cf_meta) then located_display_error ctx.com (error_msg p (No_constructor (TClassDecl c)));
 			let fa = FieldAccess.create e cf (FHInstance(c,params)) false p in
 			let fcc = unify_field_call ctx fa [] el p false in
 			let el = fcc.fc_args in
@@ -2113,7 +2113,7 @@ let rec create com =
 	ctx.g.std <- (try
 		TypeloadModule.load_module ctx ([],"StdTypes") null_pos
 	with
-		Error (Module_not_found ([],"StdTypes"),_) ->
+		Error (Module_not_found ([],"StdTypes"),_,_) ->
 			try
 				let std_path = Sys.getenv "HAXE_STD_PATH" in
 				typing_error ("Standard library not found. Please check your `HAXE_STD_PATH` environment variable (current value: \"" ^ std_path ^ "\")") null_pos

+ 7 - 7
src/typing/typerDisplay.ml

@@ -212,7 +212,7 @@ let rec handle_signature_display ctx e_ast with_type =
 						let _ = unify_call_args ctx el args r p false false false in
 						true
 					with
-					| Error(Call_error (Not_enough_arguments _),_) -> true
+					| Error(Call_error (Not_enough_arguments _),_,_) -> true
 					| _ -> false
 					end
 				in
@@ -263,10 +263,10 @@ let rec handle_signature_display ctx e_ast with_type =
 				try
 					acc_get ctx (!type_call_target_ref ctx e1 el with_type None)
 				with
-				| Error (Unknown_ident "trace",_) ->
+				| Error (Unknown_ident "trace",_,_) ->
 					let e = expr_of_type_path (["haxe";"Log"],"trace") p in
 					type_expr ctx e WithType.value
-				| Error (Unknown_ident "$type",p) ->
+				| Error (Unknown_ident "$type",p,_) ->
 					display_dollar_type ctx p (fun t -> t,(CompletionType.from_type (get_import_status ctx) t))
 			in
 			let e1 = match e1 with
@@ -536,10 +536,10 @@ let handle_display ctx e_ast dk mode with_type =
 		mk (TConst TNull) t p (* This is "probably" a bind skip, let's just use the expected type *)
 	| (_,p),_ -> try
 		type_expr ~mode ctx e_ast with_type
-	with Error (Unknown_ident n,_) when ctx.com.display.dms_kind = DMDefault ->
+	with Error (Unknown_ident n,_,_) when ctx.com.display.dms_kind = DMDefault ->
         if dk = DKDot && is_legacy_completion ctx.com then raise (Parser.TypePath ([n],None,false,p))
 		else raise_toplevel ctx dk with_type (n,p)
-	| Error ((Type_not_found (path,_,_) | Module_not_found path),_) as err when ctx.com.display.dms_kind = DMDefault ->
+	| Error ((Type_not_found (path,_,_) | Module_not_found path),_,_) as err when ctx.com.display.dms_kind = DMDefault ->
 		if is_legacy_completion ctx.com then begin try
 			raise_fields (DisplayFields.get_submodule_fields ctx path) (CRField((make_ci_module path),p,None,None)) (make_subject None (pos e_ast))
 		with Not_found ->
@@ -615,7 +615,7 @@ let handle_display ctx e_ast dk mode with_type =
 		| WithType.WithType(t,_) ->
 			(* We don't want to actually use the transformed expression which may have inserted implicit cast calls.
 			   It only matters that unification takes place. *)
-			(try ignore(AbstractCast.cast_or_unify_raise ctx t e e.epos) with Error (Unify l,p) -> ());
+			(try ignore(AbstractCast.cast_or_unify_raise ctx t e e.epos) with Error (Unify l,p,_) -> ());
 		| _ ->
 			()
 	end;
@@ -721,4 +721,4 @@ let handle_edisplay ctx e dk mode with_type =
 		with DisplayException(DisplayFields ({fkind = CRToplevel _} as r)) ->
 			raise_fields r.fitems (CRPattern ((get_expected_type ctx with_type),outermost)) r.fsubject
 		end
-	| _ -> handle_display ctx e dk with_type
+	| _ -> handle_display ctx e dk with_type

+ 1 - 1
src/typing/typerDotPath.ml

@@ -74,7 +74,7 @@ let resolve_qualified ctx pack name next_path p mode with_type =
 	try
 		let m = Typeload.load_module ctx (pack,name) p in
 		resolve_in_module ctx m next_path p mode with_type
-	with Error (Module_not_found mpath,_) when mpath = (pack,name) ->
+	with Error (Module_not_found mpath,_,_) when mpath = (pack,name) ->
 		(* might be an instance of https://github.com/HaxeFoundation/haxe/issues/9150
 		   so let's also check (pack,name) of a TYPE in the current module context ¯\_(ツ)_/¯ *)
 		let t = Typeload.find_type_in_current_module_context ctx pack name in (* raises Not_found *)

+ 12 - 12
std/haxe/macro/Context.hx

@@ -47,38 +47,38 @@ class Context {
 		Displays a compilation error `msg` at the given `Position` `pos`
 		and aborts the current macro call.
 	**/
-	public static function error(msg:String, pos:Position):Dynamic {
-		return load("error", 2)(msg, pos);
+	public static function error(msg:String, pos:Position, ?depth:Int = 0):Dynamic {
+		return load("error", 2)(msg, pos, depth);
 	}
 
 	/**
 		Displays a compilation error `msg` at the given `Position` `pos`
 		and aborts the compilation.
 	**/
-	public static function fatalError(msg:String, pos:Position):Dynamic {
-		return load("fatal_error", 2)(msg, pos);
+	public static function fatalError(msg:String, pos:Position, ?depth:Int = 0):Dynamic {
+		return load("fatal_error", 2)(msg, pos, depth);
 	}
 
 	/**
 		Displays a compilation error `msg` at the given `Position` `pos`
 		without aborting the current macro call.
 	**/
-	public static function reportError(msg:String, pos:Position):Void {
-		load("report_error", 2)(msg, pos);
+	public static function reportError(msg:String, pos:Position, ?depth:Int = 0):Void {
+		load("report_error", 2)(msg, pos, depth);
 	}
 
 	/**
 		Displays a compilation warning `msg` at the given `Position` `pos`.
 	**/
-	public static function warning(msg:String, pos:Position) {
-		load("warning", 2)(msg, pos);
+	public static function warning(msg:String, pos:Position, ?depth:Int = 0) {
+		load("warning", 2)(msg, pos, depth);
 	}
 
 	/**
 		Displays a compilation info `msg` at the given `Position` `pos`.
 	**/
-	public static function info(msg:String, pos:Position) {
-		load("info", 2)(msg, pos);
+	public static function info(msg:String, pos:Position, ?depth:Int = 0) {
+		load("info", 2)(msg, pos, depth);
 	}
 
 	/**
@@ -311,7 +311,7 @@ class Context {
 
 	/**
 		Returns the typed expression of the call to the main function.
-		
+
 		This function will only work in the generation phase. Any calls
 		made outside a function passed to `haxe.macro.Context.onGenerate`
 		or `haxe.macro.Context.onAfterGenerate` will return `null`.
@@ -322,7 +322,7 @@ class Context {
 
 	/**
 		Returns an array of module types to be generated in the output.
-		
+
 		This list may change depending on the phase of compilation and
 		should not be treated as conclusive until the generation phase.
 

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

@@ -0,0 +1,2 @@
+compile-fail.hxml
+-D message-reporting=indent

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

@@ -0,0 +1,2 @@
+Uncaught exception Invalid string
+  Main.hx:5: characters 3-18 : Called from here

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

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

+ 10 - 0
tests/misc/projects/Issue10623/pretty-fail.hxml.stderr

@@ -0,0 +1,10 @@
+[ERROR] (unknown position)
+
+   | Uncaught exception Invalid string
+
+    ->  Main.hx:5: characters 3-18
+
+    5 |   data.toString().substr(0);
+      |   ^^^^^^^^^^^^^^^
+      | Called from here
+

+ 3 - 0
tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml.stderr

@@ -0,0 +1,3 @@
+(unknown) : Uncaught exception Could not read file define.jsno
+$$normPath(::std::)/haxe/macro/Compiler.hx:482: characters 11-39 : Called from here
+(unknown) : Called from here

+ 2 - 0
tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml

@@ -0,0 +1,2 @@
+user-defined-meta-fail.hxml
+-D message-reporting=indent

+ 2 - 0
tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml.stderr

@@ -0,0 +1,2 @@
+(unknown) : Object requires field metadata
+  (unknown) : For function argument 'meta'

+ 3 - 0
tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml.stderr

@@ -0,0 +1,3 @@
+(unknown) : Uncaught exception Could not read file meta.jsno
+$$normPath(::std::)/haxe/macro/Compiler.hx:472: characters 11-39 : Called from here
+(unknown) : Called from here

+ 2 - 0
tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml

@@ -0,0 +1,2 @@
+user-defined-meta-json-fail.hxml
+-D message-reporting=indent

+ 3 - 0
tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml.stderr

@@ -0,0 +1,3 @@
+(unknown) : Uncaught exception Could not read file meta.jsno
+  $$normPath(::std::)/haxe/macro/Compiler.hx:472: characters 11-39 : Called from here
+  (unknown) : Called from here

+ 4 - 0
tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml

@@ -0,0 +1,4 @@
+user-defined-meta-json-fail.hxml
+-D message-reporting=pretty
+-D no-color
+

+ 12 - 0
tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml.stderr

@@ -0,0 +1,12 @@
+[ERROR] --macro haxe.macro.Compiler.registerMetadataDescriptionFile('meta.jsno', 'myapp')
+
+   | Uncaught exception Could not read file meta.jsno
+
+    ->  $$normPath(::std::)/haxe/macro/Compiler.hx:472: characters 11-39
+
+    472 |   var f = sys.io.File.getContent(path);
+        |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        | Called from here
+
+        | Called from here
+

+ 4 - 0
tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml

@@ -0,0 +1,4 @@
+user-defined-meta-fail.hxml
+-D message-reporting=pretty
+-D no-color
+

+ 5 - 0
tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml.stderr

@@ -0,0 +1,5 @@
+[ERROR] --macro haxe.macro.Compiler.registerCustomMetadata({metdata: ':zzz', doc: 'something'}, 'myapp')
+
+   | Object requires field metadata
+   | For function argument 'meta'
+

+ 3 - 0
tests/misc/projects/Issue10863/logmode-fail.hxml

@@ -0,0 +1,3 @@
+Any
+-D messages-log-file=error.log
+-D messages-log-format=awesome

+ 1 - 0
tests/misc/projects/Issue10863/logmode-fail.hxml.stderr

@@ -0,0 +1 @@
+Invalid message reporting mode: "awesome", expected classic | pretty | indent (for -D messages_log_format).

+ 2 - 0
tests/misc/projects/Issue10863/reporting-fail.hxml

@@ -0,0 +1,2 @@
+Any
+-D message-reporting=awesome

+ 1 - 0
tests/misc/projects/Issue10863/reporting-fail.hxml.stderr

@@ -0,0 +1 @@
+Invalid message reporting mode: "awesome", expected classic | pretty | indent (for -D message_reporting).

+ 8 - 0
tests/misc/projects/Issue4803/Main2.hx

@@ -0,0 +1,8 @@
+extern inline overload function test(a:String) {}
+extern inline overload function test(a:Int) {}
+
+function main() {
+	test(function() {
+		var x:String = 1;
+	});
+}

+ 3 - 0
tests/misc/projects/Issue4803/compile2-fail.hxml

@@ -0,0 +1,3 @@
+--main Main2
+-js js.js
+--no-output

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

@@ -0,0 +1,2 @@
+Main2.hx:6: characters 3-20 : Int should be String
+Main2.hx:6: characters 3-20 : ... For function argument 'a'

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

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

+ 6 - 0
tests/misc/projects/Issue5644/pretty-fail.hxml.stderr

@@ -0,0 +1,6 @@
+[ERROR] User.hx:1: characters 20-47
+
+ 1 | class User extends ThisObviouslyDoesntExist<T> {}
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | Type not found : ThisObviouslyDoesntExist
+

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

@@ -0,0 +1,2 @@
+compile-fail.hxml
+-D message-reporting=indent

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

@@ -0,0 +1,2 @@
+Main.hx:11: characters 11-14 : Field a has different @:native value than in superclass
+  Main.hx:6: characters 18-19 : Base field is defined here

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

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

+ 10 - 0
tests/misc/projects/Issue5949/pretty-fail.hxml.stderr

@@ -0,0 +1,10 @@
+[ERROR] Main.hx:11: characters 11-14
+
+ 11 |  @:native("z") override public function a() {}
+    |           ^^^
+    | Field a has different @:native value than in superclass
+
+     6 |  public function a() {}
+       |                  ^
+       | Base field is defined here
+

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

@@ -0,0 +1,2 @@
+compile-fail.hxml
+-D message-reporting=indent

+ 14 - 0
tests/misc/projects/Issue6065/indent-fail.hxml.stderr

@@ -0,0 +1,14 @@
+Main.hx:8: characters 9-18 : Could not find a suitable overload, reasons follow
+  Main.hx:8: characters 9-18 : Overload resolution failed for () -> Void
+    Main.hx:8: characters 13-17 : Too many arguments
+  Main.hx:8: characters 9-18 : Overload resolution failed for (t : f.T) -> Void
+    Main.hx:8: characters 13-17 : Constraint check failure for f.T
+      Main.hx:8: characters 13-17 : String should be Int
+      Main.hx:8: characters 13-17 : For function argument 't'
+Main.hx:9: characters 9-16 : Could not find a suitable overload, reasons follow
+  Main.hx:9: characters 9-16 : Overload resolution failed for () -> Void
+    Main.hx:9: characters 13-15 : Too many arguments
+  Main.hx:9: characters 9-16 : Overload resolution failed for (t : f.T) -> Void
+    Main.hx:9: characters 13-15 : Constraint check failure for f.T
+      Main.hx:9: characters 13-15 : { } should be Int
+      Main.hx:9: characters 13-15 : For function argument 't'

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

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

+ 40 - 0
tests/misc/projects/Issue6065/pretty-fail.hxml.stderr

@@ -0,0 +1,40 @@
+[ERROR] Main.hx:8: characters 9-18
+
+ 8 |         C.f("hi");
+   |         ^^^^^^^^^
+   | Could not find a suitable overload, reasons follow
+
+      | Overload resolution failed for () -> Void
+
+       8 |         C.f("hi");
+         |             ^^^^
+         | Too many arguments
+
+      | Overload resolution failed for (t : f.T) -> Void
+
+       8 |         C.f("hi");
+         |             ^^^^
+         | Constraint check failure for f.T
+         | String should be Int
+         | For function argument 't'
+
+[ERROR] Main.hx:9: characters 9-16
+
+ 9 |         C.f({});
+   |         ^^^^^^^
+   | Could not find a suitable overload, reasons follow
+
+      | Overload resolution failed for () -> Void
+
+       9 |         C.f({});
+         |             ^^
+         | Too many arguments
+
+      | Overload resolution failed for (t : f.T) -> Void
+
+       9 |         C.f({});
+         |             ^^
+         | Constraint check failure for f.T
+         | { } should be Int
+         | For function argument 't'
+

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

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

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

@@ -0,0 +1,14 @@
+[ERROR] Main5.hx:1: lines 1-6
+
+ 1 | class Main {
+ - |   [...]
+ 6 | }
+   |
+   | 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
+

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

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

+ 7 - 0
tests/misc/projects/Issue6790/pretty-fail.hxml.stderr

@@ -0,0 +1,7 @@
+[ERROR] Mismatch.hx:6: characters 19-26
+
+ 6 |   p.then(x -> 10, e -> "");
+   |                   ^^^^^^^
+   | (e : Unknown<0>) -> String should be Null<js.lib.PromiseHandler<Dynamic, Int>>
+   | For optional function argument 'onRejected'
+

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

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

+ 7 - 0
tests/misc/projects/Issue6796/pretty-fail.hxml.stderr

@@ -0,0 +1,7 @@
+[ERROR] Main.hx:3: characters 21-25
+
+ 3 |         Sys.println(main["foo"]);
+   |                     ^^^^
+   | Array access is not allowed on () -> Unknown<0>
+   | For function argument 'v'
+

+ 3 - 0
tests/misc/projects/Issue6810/.gitignore

@@ -0,0 +1,3 @@
+logfile-02-fail.hxml.stderr
+logfile-03-fail.hxml.stderr
+logfile-04-fail.hxml.stderr

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

@@ -0,0 +1,2 @@
+compile-fail.hxml
+-D message-reporting=indent

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

@@ -0,0 +1,8 @@
+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 : 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 : have: (...) -> FakeVoid
+  Fail.hx:8: characters 8-16 : want: (...) -> haxe.NotVoid
+  Fail.hx:8: characters 8-16 : For function argument 'f'

+ 2 - 0
tests/misc/projects/Issue6810/logfile-01-fail.hxml

@@ -0,0 +1,2 @@
+-D messages-log-file=logfile-02-fail.hxml.stderr
+compile-fail.hxml

+ 4 - 0
tests/misc/projects/Issue6810/logfile-02-fail.hxml

@@ -0,0 +1,4 @@
+-D messages-log-file=logfile-03-fail.hxml.stderr
+-D messages-log-format=pretty
+-D message-reporting=indent
+compile-fail.hxml

+ 4 - 0
tests/misc/projects/Issue6810/logfile-03-fail.hxml

@@ -0,0 +1,4 @@
+-D messages-log-file=logfile-04-fail.hxml.stderr
+-D messages-log-format=classic
+-D message-reporting=pretty
+compile-fail.hxml

+ 1 - 0
tests/misc/projects/Issue6810/logfile-04-fail.hxml

@@ -0,0 +1 @@
+compile-fail.hxml

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

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

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

@@ -0,0 +1,18 @@
+[ERROR] Fail.hx:7: characters 8-12
+
+ 7 |   test(void);
+   |        ^^^^
+   | error: Void should be haxe.NotVoid
+   | have: (...) -> Void
+   | want: (...) -> haxe.NotVoid
+   | For function argument 'f'
+
+[ERROR] Fail.hx:8: characters 8-16
+
+ 8 |   test(fakeVoid);
+   |        ^^^^^^^^
+   | error: FakeVoid should be haxe.NotVoid
+   | have: (...) -> FakeVoid
+   | want: (...) -> haxe.NotVoid
+   | For function argument 'f'
+

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

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

+ 10 - 0
tests/misc/projects/Issue7968/pretty-fail.hxml.stderr

@@ -0,0 +1,10 @@
+[ERROR] Foo.hx:2: characters 1-18
+
+ 2 | typedef A = Float;
+   | ^^^^^^^^^^^^^^^^^
+   | Name A is already defined in this module
+
+    1 | typedef A = Int;
+      |         ^
+      | Previous declaration here
+

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

@@ -0,0 +1,2 @@
+compile-fail.hxml
+-D message-reporting=indent

+ 21 - 0
tests/misc/projects/Issue8303/indent-fail.hxml.stderr

@@ -0,0 +1,21 @@
+Uncaught exception Stack overflow
+  Main.hx:1: character 1 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:8: characters 4-9 : Called from here
+  Main.hx:10: characters 3-8 : Called from here
+  Main.hx:3: characters 3-9 : Called from here

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

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

+ 54 - 0
tests/misc/projects/Issue8303/pretty-fail.hxml.stderr

@@ -0,0 +1,54 @@
+[ERROR] (unknown position)
+
+   | Uncaught exception Stack overflow
+
+    ->  Main.hx:1: character 1
+
+     1 | class Main {
+       | ^
+       | Called from here
+
+     8 |    log();
+       |    ^^^^^
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+       | Called from here
+
+    10 |   log();
+       |   ^^^^^
+       | Called from here
+
+     3 |   test();
+       |   ^^^^^^
+       | Called from here
+

+ 3 - 0
tests/misc/projects/Issue8471/compile2-pretty.hxml

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

+ 22 - 0
tests/misc/projects/Issue8471/compile2-pretty.hxml.stderr

@@ -0,0 +1,22 @@
+[WARNING] Macro2.hx:12: characters 25-39
+
+ 12 |   Context.warning(("1" :DeprecatedType), Context.currentPos());
+    |                         ^^^^^^^^^^^^^^
+    | (WDeprecated) This typedef is deprecated in favor of String
+
+[WARNING] (unknown position)
+
+    | 1
+
+[WARNING] (unknown position)
+
+    | 2
+
+[WARNING] (unknown position)
+
+    | 3
+
+[WARNING] (unknown position)
+
+    | i123|i123
+

+ 7 - 14
tests/misc/projects/issue5002/compile2-fail.hxml.stderr

@@ -1,17 +1,10 @@
-"0_variable" is not a valid variable name.
-Main2.hx:6: characters 3-32 : ... Called from macro here
-"var" is not a valid variable name.
-Main2.hx:7: characters 3-25 : ... Called from macro here
-"new" is not a valid variable name.
-Main2.hx:8: characters 3-25 : ... Called from macro here
-"foo \"\t\n" is not a valid variable name.
-Main2.hx:9: characters 3-32 : ... Called from macro here
-"0_catchVariable" is not a valid catch variable name.
-Main2.hx:10: characters 3-25 : ... Called from macro here
-"0_function" is not a valid function name.
-Main2.hx:11: characters 3-24 : ... Called from macro here
-"0_argument" is not a valid function argument name.
-Main2.hx:12: characters 3-32 : ... Called from macro here
+Main2.hx:6: characters 3-32 : "0_variable" is not a valid variable name.
+Main2.hx:7: characters 3-25 : "var" is not a valid variable name.
+Main2.hx:8: characters 3-25 : "new" is not a valid variable name.
+Main2.hx:9: characters 3-32 : "foo \"\t\n" is not a valid variable name.
+Main2.hx:10: characters 3-25 : "0_catchVariable" is not a valid catch variable name.
+Main2.hx:11: characters 3-24 : "0_function" is not a valid function name.
+Main2.hx:12: characters 3-32 : "0_argument" is not a valid function argument name.
 Main2.hx:13: characters 3-27 : Unknown identifier : 0_patternVariable, pattern variables must be lower-case or with `var ` prefix
 Main2.hx:14: characters 3-23 : "0_forVariable" is not a valid for variable name.
 Main2.hx:15: characters 3-31 : "0_forVariableKey" is not a valid for variable name.

+ 7 - 1
tests/misc/src/Main.hx

@@ -69,8 +69,9 @@ class Main {
 		s = s.replace("\r\n", "\n"); // get rid of windows newlines
 
 		var cwd = Path.removeTrailingSlashes(FileSystem.fullPath(Sys.getCwd()));
+		var std = Path.removeTrailingSlashes(getStd());
 
-		var context = {cwd: cwd};
+		var context = {cwd: cwd, std: std};
 		var macros = {normPath: normPath};
 
 		return new haxe.Template(s).execute(context, macros);
@@ -165,4 +166,9 @@ class Main {
 
 		return true;
 	}
+
+	static macro function getStd() {
+		var std = Compiler.getConfiguration().stdPath;
+		return macro $v{std.shift()};
+	}
 }