Prechádzať zdrojové kódy

More strict macro API for init vs non init macros (#11043)

* More strict macro API for init vs non init macros

* Macro => Macros

* Simplify wording

* [tests] adjust tests positions

* Documentation

* Let addNativeArg and nullSafety be  called from outside init macros

* [tests] adjust tests positions

* Mention Context functions being flagged as deprecated from init macros in their doc

* Add more init macro checks

* [tests] adjust tests positions

* Doc adjustments

* Warn when calling Context.onAfterInitMacros after init macros

* Remove commented out code

* Add test
Rudy Ges 2 rokov pred
rodič
commit
4c1c12121e

+ 5 - 0
src-json/define.json

@@ -261,6 +261,11 @@
 		"doc": "The current Haxe version value in SemVer format.",
 		"reserved": true
 	},
+	{
+		"name": "HaxeNext",
+		"define": "haxe-next",
+		"doc": "Enable experimental features that are meant to be released on next Haxe version."
+	},
 	{
 		"name": "HlVer",
 		"define": "hl-ver",

+ 15 - 8
src/macro/macroApi.ml

@@ -21,8 +21,11 @@ type compiler_options = {
 type 'value compiler_api = {
 	pos : Globals.pos;
 	get_com : unit -> Common.context;
+	get_macro_stack : unit -> pos list;
+	init_macros_done : unit -> bool;
 	get_type : string -> Type.t option;
 	get_module : string -> Type.t list;
+	after_init_macros : (unit -> unit) -> unit;
 	after_typing : (module_type list -> unit) -> unit;
 	on_generate : (Type.t list -> unit) -> bool -> unit;
 	after_generate : (unit -> unit) -> unit;
@@ -1690,6 +1693,13 @@ let macro_api ccom get_api =
 		"current_pos", vfun0 (fun() ->
 			encode_pos (get_api()).pos
 		);
+		"get_macro_stack", vfun0 (fun () ->
+			let stack = ((get_api()).get_macro_stack ()) in
+			encode_array (List.map encode_pos stack)
+		);
+		"init_macros_done", vfun0 (fun () ->
+			vbool ((get_api()).init_macros_done ())
+		);
 		"error", vfun3 (fun msg p depth ->
 			let msg = decode_string msg in
 			let p = decode_pos p in
@@ -1744,10 +1754,6 @@ let macro_api ccom get_api =
 		"define", vfun2 (fun s v ->
 			let s = decode_string s in
 			let com = ccom() in
-			if com.stage <> CInitMacrosStart then begin
-				let v = if v = vnull then "" else ", " ^ (decode_string v) in
-				(get_api()).warning WMacro ("Should be used in initialization macros only: haxe.macro.Compiler.define(" ^ s ^ v ^ ")") Globals.null_pos;
-			end;
 			(* TODO: use external_define and external_define_value for #8690 *)
 			if v = vnull then
 				Common.external_define_no_check com s
@@ -1773,6 +1779,11 @@ let macro_api ccom get_api =
 		"get_module", vfun1 (fun s ->
 			encode_array (List.map encode_type ((get_api()).get_module (decode_string s)))
 		);
+		"on_after_init_macros", vfun1 (fun f ->
+			let f = prepare_callback f 1 in
+			(get_api()).after_init_macros (fun tl -> ignore(f []));
+			vnull
+		);
 		"on_after_typing", vfun1 (fun f ->
 			let f = prepare_callback f 1 in
 			(get_api()).after_typing (fun tl -> ignore(f [encode_array (List.map encode_module_type tl)]));
@@ -2052,8 +2063,6 @@ let macro_api ccom get_api =
 		"add_class_path", vfun1 (fun cp ->
 			let com = ccom() in
 			let cp = decode_string cp in
-			if com.stage <> CInitMacrosStart then
-				(get_api()).warning WMacro ("Should be used in initialization macros only: haxe.macro.Compiler.addClassPath(" ^ cp ^ ")") Globals.null_pos;
 			let cp = Path.add_trailing_slash cp in
 			com.class_path <- cp :: com.class_path;
 			(match com.get_macros() with
@@ -2068,8 +2077,6 @@ let macro_api ccom get_api =
 		"add_native_lib", vfun1 (fun file ->
 			let file = decode_string file in
 			let com = ccom() in
-			if com.stage <> CInitMacrosStart then
-				(get_api()).warning WMacro ("Should be used in initialization macros only: haxe.macro.Compiler.addNativeLib(" ^ file ^ ")") Globals.null_pos;
 			NativeLibraryHandler.add_native_lib com file false ();
 			vnull
 		);

+ 16 - 0
src/typing/macroContext.ml

@@ -134,6 +134,15 @@ let make_macro_api ctx p =
 	{
 		MacroApi.pos = p;
 		MacroApi.get_com = (fun() -> ctx.com);
+		MacroApi.get_macro_stack = (fun () ->
+			let envs = Interp.call_stack (Interp.get_eval (Interp.get_ctx ())) in
+			let envs = match envs with
+				| _ :: envs -> envs (* Skip call to getMacroStack() *)
+				| _ -> envs
+			in
+			List.map (fun (env:Interp.env) -> {pfile = EvalHash.rev_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax}) envs
+		);
+		MacroApi.init_macros_done = (fun () -> ctx.com.stage >= CInitMacrosDone);
 		MacroApi.get_type = (fun s ->
 			typing_timer ctx false (fun() ->
 				let path = parse_path s in
@@ -160,6 +169,13 @@ let make_macro_api ctx p =
 				m
 			)
 		);
+		MacroApi.after_init_macros = (fun f ->
+			ctx.com.callbacks#add_after_init_macros (fun () ->
+				let t = macro_timer ctx ["afterInitMacros"] in
+				f ();
+				t()
+			)
+		);
 		MacroApi.after_typing = (fun f ->
 			ctx.com.callbacks#add_after_typing (fun tl ->
 				let t = macro_timer ctx ["afterTyping"] in

+ 67 - 56
std/haxe/macro/Compiler.hx

@@ -70,6 +70,7 @@ class Compiler {
 	**/
 	public static function define(flag:String, ?value:String) {
 		#if (neko || eval)
+		Context.assertInitMacro();
 		load("define", 2)(flag, value);
 		#end
 	}
@@ -137,6 +138,7 @@ class Compiler {
 	**/
 	public static function addClassPath(path:String) {
 		#if (neko || eval)
+		Context.assertInitMacro();
 		load("add_class_path", 1)(path);
 		#end
 	}
@@ -183,6 +185,7 @@ class Compiler {
 	**/
 	public static function addNativeLib(name:String) {
 		#if (neko || eval)
+		Context.assertInitMacro();
 		load("add_native_lib", 1)(name);
 		#end
 	}
@@ -215,69 +218,77 @@ class Compiler {
 		@param strict If true and given package wasn't found in any of class paths, fail with an error.
 	**/
 	public static function include(pack:String, ?rec = true, ?ignore:Array<String>, ?classPaths:Array<String>, strict = false) {
-		var ignoreWildcard:Array<String> = [];
-		var ignoreString:Array<String> = [];
-		if (ignore != null) {
-			for (ignoreRule in ignore) {
-				if (StringTools.endsWith(ignoreRule, "*")) {
-					ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1));
-				} else {
-					ignoreString.push(ignoreRule);
+		function include(pack:String, ?rec = true, ?ignore:Array<String>, ?classPaths:Array<String>, strict = false) {
+			var ignoreWildcard:Array<String> = [];
+			var ignoreString:Array<String> = [];
+			if (ignore != null) {
+				for (ignoreRule in ignore) {
+					if (StringTools.endsWith(ignoreRule, "*")) {
+						ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1));
+					} else {
+						ignoreString.push(ignoreRule);
+					}
 				}
 			}
-		}
-		var skip = if (ignore == null) {
-			function(c) return false;
-		} else {
-			function(c:String) {
-				if (Lambda.has(ignoreString, c))
-					return true;
-				for (ignoreRule in ignoreWildcard)
-					if (StringTools.startsWith(c, ignoreRule))
+			var skip = if (ignore == null) {
+				function(c) return false;
+			} else {
+				function(c:String) {
+					if (Lambda.has(ignoreString, c))
 						return true;
-				return false;
+					for (ignoreRule in ignoreWildcard)
+						if (StringTools.startsWith(c, ignoreRule))
+							return true;
+					return false;
+				}
 			}
-		}
-		var displayValue = Context.definedValue("display");
-		if (classPaths == null) {
-			classPaths = Context.getClassPath();
-			// do not force inclusion when using completion
-			switch (displayValue) {
-				case null:
-				case "usage":
-				case _:
-					return;
+			var displayValue = Context.definedValue("display");
+			if (classPaths == null) {
+				classPaths = Context.getClassPath();
+				// do not force inclusion when using completion
+				switch (displayValue) {
+					case null:
+					case "usage":
+					case _:
+						return;
+				}
+				// normalize class path
+				for (i in 0...classPaths.length) {
+					var cp = StringTools.replace(classPaths[i], "\\", "/");
+					if (StringTools.endsWith(cp, "/"))
+						cp = cp.substr(0, -1);
+					if (cp == "")
+						cp = ".";
+					classPaths[i] = cp;
+				}
 			}
-			// normalize class path
-			for (i in 0...classPaths.length) {
-				var cp = StringTools.replace(classPaths[i], "\\", "/");
-				if (StringTools.endsWith(cp, "/"))
-					cp = cp.substr(0, -1);
-				if (cp == "")
-					cp = ".";
-				classPaths[i] = cp;
+			var prefix = pack == '' ? '' : pack + '.';
+			var found = false;
+			for (cp in classPaths) {
+				var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/");
+				if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path))
+					continue;
+				found = true;
+				for (file in sys.FileSystem.readDirectory(path)) {
+					if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) {
+						if( file == "import.hx" ) continue;
+						var cl = prefix + file.substr(0, file.length - 3);
+						if (skip(cl))
+							continue;
+						Context.getModule(cl);
+					} else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file))
+						include(prefix + file, true, ignore, classPaths);
+				}
 			}
+			if (strict && !found)
+				Context.error('Package "$pack" was not found in any of class paths', Context.currentPos());
 		}
-		var prefix = pack == '' ? '' : pack + '.';
-		var found = false;
-		for (cp in classPaths) {
-			var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/");
-			if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path))
-				continue;
-			found = true;
-			for (file in sys.FileSystem.readDirectory(path)) {
-				if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) {
-					if( file == "import.hx" ) continue;
-					var cl = prefix + file.substr(0, file.length - 3);
-					if (skip(cl))
-						continue;
-					Context.getModule(cl);
-				} else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file))
-					include(prefix + file, true, ignore, classPaths);
-			}
+
+		if (!Context.initMacrosDone()) {
+			Context.onAfterInitMacros(() -> include(pack, rec, ignore, classPaths, strict));
+		} else {
+			include(pack, rec, ignore, classPaths, strict);
 		}
-		if (strict && !found)
-			Context.error('Package "$pack" was not found in any of class paths', Context.currentPos());
 	}
 
 	/**
@@ -707,7 +718,7 @@ typedef CompilerConfiguration = {
 	final platform:haxe.display.Display.Platform;
 
 	/**
-		The compilation configuration for the target platform. 
+		The compilation configuration for the target platform.
 	**/
 	final platformConfig:PlatformConfig;
 

+ 154 - 0
std/haxe/macro/Context.hx

@@ -96,6 +96,14 @@ class Context {
 		load("filter_messages", 1)(predicate);
 	}
 
+	/**
+		Check if compiler is past initializations macros or not.
+		When it is, configuration phase is over and parsing/typing can start.
+	**/
+	public static function initMacrosDone():Bool {
+		return load("init_macros_done", 0)();
+	}
+
 	/**
 		Resolves a file name `file` based on the current class paths.
 
@@ -138,6 +146,14 @@ class Context {
 		return load("current_pos", 0)();
 	}
 
+	/**
+		Get the call stack (excluding the call to `Context.getMacroStack()`
+		that led to current macro.
+	**/
+	public static function getMacroStack():Array<Position> {
+		return load("get_macro_stack", 0)();
+	}
+
 	/**
 		Returns the type which is expected at the place the macro is called.
 
@@ -148,6 +164,7 @@ class Context {
 		macro is not an expression-macro.
 	**/
 	public static function getExpectedType():Null<Type> {
+		assertInitMacrosDone(false);
 		return load("get_expected_type", 0)();
 	}
 
@@ -291,8 +308,13 @@ class Context {
 		declared class path has priority.
 
 		If no type can be found, an exception of type `String` is thrown.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function getType(name:String):Type {
+		assertInitMacrosDone();
 		return load("get_type", 1)(name);
 	}
 
@@ -304,8 +326,13 @@ class Context {
 		declared class path has priority.
 
 		If no module can be found, an exception of type `String` is thrown.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function getModule(name:String):Array<Type> {
+		assertInitMacrosDone();
 		return load("get_module", 1)(name);
 	}
 
@@ -327,8 +354,13 @@ class Context {
 		should not be treated as conclusive until the generation phase.
 
 		Modifying the returned array has no effect on the compilation.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function getAllModuleTypes():Array<haxe.macro.Type.ModuleType> {
+		assertInitMacrosDone();
 		return load("get_module_types", 0)();
 	}
 
@@ -369,6 +401,7 @@ class Context {
 		Returns a hashed MD5 signature of value `v`.
 	**/
 	public static function signature(v:Dynamic):String {
+		assertInitMacrosDone(false);
 		return load("signature", 1)(v);
 	}
 
@@ -417,6 +450,19 @@ class Context {
 		load("on_after_typing", 1)(callback);
 	}
 
+	/**
+		Adds a callback function `callback` which is invoked after the compiler
+		is done running initialization macros, when typing begins.
+
+		`onAfterInitMacros` should be used to delay typer-dependant code from
+		your initalization macros, to properly separate configuration phase and
+		actual typing.
+	**/
+	public static function onAfterInitMacros(callback:Void->Void):Void {
+		assertInitMacro();
+		load("on_after_init_macros", 1)(callback);
+	}
+
 	/**
 		Adds a callback function `callback` which is invoked when a type name
 		cannot be resolved.
@@ -434,8 +480,13 @@ class Context {
 
 		Typing the expression may result in a compiler error which can be
 		caught using `try ... catch`.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function typeof(e:Expr):Type {
+		assertInitMacrosDone();
 		return load("typeof", 1)(e);
 	}
 
@@ -447,8 +498,13 @@ class Context {
 		be caught this way because the compiler might delay various checks
 		to a later stage, at which point the exception handler is no longer
 		active.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function typeExpr(e:Expr):TypedExpr {
+		assertInitMacrosDone();
 		return load("type_expr", 1)(e);
 	}
 
@@ -458,8 +514,13 @@ class Context {
 		Resolving the type may result in a compiler error which can be
 		caught using `try ... catch`.
 		Resolution is performed based on the current context in which the macro is called.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function resolveType(t:ComplexType, p:Position):Type {
+		assertInitMacrosDone();
 		return load("resolve_type", 2)(t, p);
 	}
 
@@ -474,8 +535,13 @@ class Context {
 
 	/**
 		Tries to unify `t1` and `t2` and returns `true` if successful.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function unify(t1:Type, t2:Type):Bool {
+		assertInitMacrosDone();
 		return load("unify", 2)(t1, t2);
 	}
 
@@ -483,8 +549,13 @@ class Context {
 		Follows a type.
 
 		See `haxe.macro.TypeTools.follow` for details.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function follow(t:Type, ?once:Bool):Type {
+		assertInitMacrosDone();
 		return load("follow", 2)(t, once);
 	}
 
@@ -492,8 +563,13 @@ class Context {
 		Follows a type, including abstracts' underlying implementation
 
 		See `haxe.macro.TypeTools.followWithAbstracts` for details.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function followWithAbstracts(t:Type, once:Bool = false):Type {
+		assertInitMacrosDone();
 		return load("follow_with_abstracts", 2)(t, once);
 	}
 
@@ -552,8 +628,13 @@ class Context {
 		If `moduleDependency` is given and is not `null`, it should contain
 		a module path that will be used as a dependency for the newly defined module
 		instead of the current module.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function defineType(t:TypeDefinition, ?moduleDependency:String):Void {
+		assertInitMacrosDone();
 		load("define_type", 2)(t, moduleDependency);
 	}
 
@@ -562,8 +643,13 @@ class Context {
 
 		Returned monomorph can be used with e.g. `Context.unify` to make the compiler
 		bind the monomorph to an actual type and let macro further process the resulting type.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function makeMonomorph():Type {
+		assertInitMacrosDone();
 		return load("make_monomorph", 0)();
 	}
 
@@ -574,12 +660,17 @@ class Context {
 		The individual `types` can reference each other and any identifier
 		respects the `imports` and `usings` as usual, expect that imports are
 		not allowed to have `.*` wildcards or `as s` shorthands.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function defineModule(modulePath:String, types:Array<TypeDefinition>, ?imports:Array<ImportExpr>, ?usings:Array<TypePath>):Void {
 		if (imports == null)
 			imports = [];
 		if (usings == null)
 			usings = [];
+		assertInitMacrosDone();
 		load("define_module", 4)(modulePath, types, imports, usings);
 	}
 
@@ -587,8 +678,13 @@ class Context {
 		Returns a syntax-level expression corresponding to typed expression `t`.
 
 		This process may lose some information.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function getTypedExpr(t:Type.TypedExpr):Expr {
+		assertInitMacrosDone();
 		return load("get_typed_expr", 1)(t);
 	}
 
@@ -603,8 +699,13 @@ class Context {
 		that is reset between compilations, so care should be taken when storing
 		the expression returned by this method in a static variable and using the
 		compilation server.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function storeTypedExpr(t:Type.TypedExpr):Expr {
+		assertInitMacrosDone();
 		return load("store_typed_expr", 1)(t);
 	}
 
@@ -622,16 +723,26 @@ class Context {
 		that is reset between compilations, so care should be taken when storing
 		the expression returned by this method in a static variable and using the
 		compilation server.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function storeExpr(e:Expr):Expr {
+		assertInitMacrosDone();
 		return load("store_expr", 1)(e);
 	}
 
 	/**
 		This function works like `storeExpr`, but also returns access to the expression's
 		type through the `type` field of the return value.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function typeAndStoreExpr(e:Expr):{final type:Type.Ref<Type>; final expr:Expr;} {
+		assertInitMacrosDone();
 		return load("type_and_store_expr", 1)(e);
 	}
 
@@ -643,8 +754,13 @@ class Context {
 		`externFile` has changed.
 
 		Has no effect if the compilation cache is not used.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function registerModuleDependency(modulePath:String, externFile:String) {
+		assertInitMacrosDone();
 		load("register_module_dependency", 2)(modulePath, externFile);
 	}
 
@@ -674,8 +790,13 @@ class Context {
 		is true even if `code` throws an exception.
 
 		If any argument is `null`, the result is unspecified.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function withImports<X>(imports:Array<String>, usings:Array<String>, code:() -> X):X {
+		assertInitMacrosDone();
 		return load("with_imports", 3)(imports, usings, code);
 	}
 
@@ -688,8 +809,13 @@ class Context {
 
 		`allowTransform`: when disabled, the code typed with `typeExpr` will be almost exactly the same
 		as the input code. This will disable some abstract types transformations.
+
+		Usage of this function from initialization macros is deprecated and may
+		cause compilation server issues. Use `Context.onAfterInitMacros` to
+		run your code once typer is ready to be used.
 	**/
 	public static function withOptions<X>(options:{?allowInlining:Bool,?allowTransform:Bool}, code : () -> X) : X {
+		assertInitMacrosDone();
 		return load("with_options", 2)(options, code);
 	}
 
@@ -724,5 +850,33 @@ class Context {
 	private static function sExpr(e:TypedExpr, pretty:Bool):String {
 		return haxe.macro.Context.load("s_expr", 2)(e, pretty);
 	}
+
+	@:allow(haxe.macro.Compiler)
+	private static function assertInitMacro():Void {
+		if (initMacrosDone()) {
+			var stack = getMacroStack();
+
+			warning(
+				"This API should only be used from initialization macros.",
+				if (stack.length > 2) stack[2] else currentPos()
+			);
+		}
+	}
+
+	private static function assertInitMacrosDone(includeSuggestion = true):Void {
+		#if haxe_next
+		if (!initMacrosDone()) {
+			var stack = getMacroStack();
+			var suggestion = includeSuggestion
+				? "\nUse `Context.onAfterInitMacros` to register a callback to run when context is ready."
+				: "";
+
+			warning(
+				"Cannot use this API from initialization macros." + suggestion,
+				if (stack.length > 2) stack[2] else currentPos()
+			);
+		}
+		#end
+	}
 	#end
 }

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

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

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

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

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

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

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

@@ -2,9 +2,9 @@
 
    | Uncaught exception Could not read file meta.jsno
 
-    ->  $$normPath(::std::)/haxe/macro/Compiler.hx:472: characters 11-39
+    ->  $$normPath(::std::)/haxe/macro/Compiler.hx:483: characters 11-39
 
-    472 |   var f = sys.io.File.getContent(path);
+    483 |   var f = sys.io.File.getContent(path);
         |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         | Called from here
 

+ 19 - 0
tests/misc/projects/Issue7871/Macro.hx

@@ -0,0 +1,19 @@
+import haxe.macro.Compiler;
+import haxe.macro.Context;
+
+function init() {
+	var e = macro 42;
+	Context.typeof(e);
+	if (Context.defined("haxe")) Context.warning("ok", (macro 0).pos);
+	Compiler.define("foo", "foo");
+
+	Context.onAfterInitMacros(() -> {
+		Context.warning("after init 1", (macro 0).pos);
+		Compiler.define("bar", "bar");
+		Context.typeof(e);
+	});
+	Compiler.include("hax.ds", true, true);
+	Context.onAfterInitMacros(() -> {
+		Context.warning("after init 2", (macro 0).pos);
+	});
+}

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

@@ -0,0 +1 @@
+--macro Macro.init()

+ 5 - 0
tests/misc/projects/Issue7871/compile-fail.hxml.stderr

@@ -0,0 +1,5 @@
+Macro.hx:7: characters 60-61 : Warning : ok
+Macro.hx:11: characters 42-43 : Warning : after init 1
+Macro.hx:12: characters 3-32 : Warning : This API should only be used from initialization macros.
+Package "hax.ds" was not found in any of class paths
+Macro.hx:17: characters 42-43 : Warning : after init 2

+ 2 - 0
tests/misc/projects/Issue7871/compile-next-fail.hxml

@@ -0,0 +1,2 @@
+compile-fail.hxml
+-D haxe-next

+ 7 - 0
tests/misc/projects/Issue7871/compile-next-fail.hxml.stderr

@@ -0,0 +1,7 @@
+Macro.hx:6: characters 2-19 : Warning : Cannot use this API from initialization macros.
+Macro.hx:6: characters 2-19 : ... Use `Context.onAfterInitMacros` to register a callback to run when context is ready.
+Macro.hx:7: characters 60-61 : Warning : ok
+Macro.hx:11: characters 42-43 : Warning : after init 1
+Macro.hx:12: characters 3-32 : Warning : This API should only be used from initialization macros.
+Package "hax.ds" was not found in any of class paths
+Macro.hx:17: characters 42-43 : Warning : after init 2