@@ -491,6 +491,49 @@ pub fn analyzeDefmacro(
491491 const meta_map : Form = .{ .data = .{ .map = try arena .dupe (Form , meta_items .items ) }, .location = form .location };
492492 placeholder_var .meta = try analyzer_mod .formToValue (rt , env , try unquoteMetaValues (arena , meta_map ));
493493 }
494+ // D-563(b): the wire-riding meta EXPRESSION for macro Vars — the same
495+ // merged map plus the compiler-minted :line/:column/:file, wholesale-
496+ // quoted to one composite constant (mirrors analyzeDef's def_meta_expr;
497+ // macro docstrings now survive AOT artifacts too).
498+ const macro_meta_expr : ? * const Node = blk : {
499+ const src_loc = items [1 ].location ;
500+ if (src_loc .line == 0 ) break :blk null ;
501+ var meta_items : std .ArrayList (Form ) = .empty ;
502+ if (items [1 ].meta ) | mf | try meta_items .appendSlice (arena , mf .data .map );
503+ if (attr_form ) | a | try meta_items .appendSlice (arena , a .data .map );
504+ if (doc_form ) | d | {
505+ try meta_items .append (arena , .{ .data = .{ .keyword = .{ .name = "doc" } }, .location = form .location });
506+ try meta_items .append (arena , d );
507+ }
508+ const arglists_inner2 = if (multi_arity ) blk2 : {
509+ const clauses = items [head .. ];
510+ const av = try arena .alloc (Form , clauses .len );
511+ for (clauses , 0.. ) | c , j | av [j ] = c .data .list [0 ];
512+ break :blk2 av ;
513+ } else blk2 : {
514+ const av = try arena .alloc (Form , 1 );
515+ av [0 ] = items [head ];
516+ break :blk2 av ;
517+ };
518+ try meta_items .append (arena , .{ .data = .{ .keyword = .{ .name = "arglists" } }, .location = form .location });
519+ try meta_items .append (arena , .{ .data = .{ .list = arglists_inner2 }, .location = form .location });
520+ const kws = [_ ]struct { name : []const u8 , v : i64 }{
521+ .{ .name = "line" , .v = @intCast (src_loc .line ) },
522+ .{ .name = "column" , .v = @intCast (src_loc .column ) },
523+ };
524+ for (kws ) | kv | {
525+ try meta_items .append (arena , .{ .data = .{ .keyword = .{ .name = kv .name } }, .location = form .location });
526+ try meta_items .append (arena , .{ .data = .{ .integer = kv .v }, .location = form .location });
527+ }
528+ try meta_items .append (arena , .{ .data = .{ .keyword = .{ .name = "file" } }, .location = form .location });
529+ try meta_items .append (arena , .{ .data = .{ .string = src_loc .file }, .location = form .location });
530+ const meta_map2 : Form = .{ .data = .{ .map = try arena .dupe (Form , meta_items .items ) }, .location = form .location };
531+ const quote_items = try arena .alloc (Form , 2 );
532+ quote_items [0 ] = macro_dispatch .makeSymbol ("quote" , form .location );
533+ quote_items [1 ] = try unquoteMetaValues (arena , meta_map2 );
534+ const quoted : Form = .{ .data = .{ .list = quote_items }, .location = form .location };
535+ break :blk try analyzer_mod .analyze (arena , rt , env , scope , quoted , macro_table );
536+ };
494537
495538 // Build the synthetic `fn` Form, then analyse it through the regular path
496539 // so multi-arity / closure / arg checks all ride existing FnNode
@@ -538,6 +581,7 @@ pub fn analyzeDefmacro(
538581 .value_expr = value_node ,
539582 .is_macro = true ,
540583 .is_private = is_private ,
584+ .meta_expr = macro_meta_expr ,
541585 .loc = form .location ,
542586 } };
543587 return n ;
0 commit comments