diff --git a/src/context/display/display.ml b/src/context/display/display.ml index 405f2a30ebb..8e408fe257b 100644 --- a/src/context/display/display.ml +++ b/src/context/display/display.ml @@ -72,7 +72,7 @@ module ExprPreprocessing = struct match fst e with | EVars vl when is_annotated (pos e) && is_completion -> let rec loop2 acc mark vl = match vl with - | ((s,pn),final,tho,eo) as v :: vl -> + | ((s,pn),final,tho,eo,ml) as v :: vl -> if mark then loop2 (v :: acc) mark vl else if is_annotated pn then @@ -96,7 +96,7 @@ module ExprPreprocessing = struct in let p = {p0 with pmax = (pos e).pmin} in let e = if is_annotated p then annotate_marked e else e in - loop2 (((s,pn),final,tho,(Some e)) :: acc) mark vl + loop2 (((s,pn),final,tho,(Some e),ml) :: acc) mark vl end | [] -> List.rev acc,mark @@ -196,7 +196,7 @@ module ExprPreprocessing = struct raise Exit | EVars vl when is_annotated (pos e) -> (* We only want to mark EVars if we're on a var name. *) - if List.exists (fun ((_,pn),_,_,_) -> is_annotated pn) vl then + if List.exists (fun ((_,pn),_,_,_,_) -> is_annotated pn) vl then annotate_marked e else raise Exit diff --git a/src/context/display/documentSymbols.ml b/src/context/display/documentSymbols.ml index ca70907cf0a..bae8e63be30 100644 --- a/src/context/display/documentSymbols.ml +++ b/src/context/display/documentSymbols.ml @@ -12,7 +12,7 @@ let collect_module_symbols mname with_locals (pack,decls) = let add name kind location = add name kind location parent in begin match e with | EVars vl -> - List.iter (fun ((s,p),_,_,eo) -> + List.iter (fun ((s,p),_,_,eo,_) -> add s Variable p false; expr_opt parent eo ) vl diff --git a/src/context/display/syntaxExplorer.ml b/src/context/display/syntaxExplorer.ml index 26b492b6c67..23b7ac08a3b 100644 --- a/src/context/display/syntaxExplorer.ml +++ b/src/context/display/syntaxExplorer.ml @@ -60,7 +60,7 @@ let find_in_syntax symbols (pack,decls) = expr e1; check KAnyField s; | EVars vl -> - List.iter (fun (_,_,tho,eo) -> + List.iter (fun (_,_,tho,eo,_) -> Option.may type_hint tho; expr_opt eo ) vl; diff --git a/src/core/ast.ml b/src/core/ast.ml index d7a78c01341..a7cff7520ee 100644 --- a/src/core/ast.ml +++ b/src/core/ast.ml @@ -205,7 +205,7 @@ and expr_def = | ECall of expr * expr list | ENew of placed_type_path * expr list | EUnop of unop * unop_flag * expr - | EVars of (placed_name * bool * type_hint option * expr option) list + | EVars of (placed_name * bool * type_hint option * expr option * metadata) list | EFunction of function_kind * func | EBlock of expr list | EFor of expr * expr @@ -666,10 +666,10 @@ let map_expr loop (e,p) = ENew (t,el) | EUnop (op,f,e) -> EUnop (op,f,loop e) | EVars vl -> - EVars (List.map (fun (n,b,t,eo) -> + EVars (List.map (fun (n,b,t,eo,ml) -> let t = opt type_hint t in let eo = opt loop eo in - n,b,t,eo + n,b,t,eo,ml ) vl) | EFunction (kind,f) -> EFunction (kind,func f) | EBlock el -> EBlock (List.map loop el) @@ -750,7 +750,7 @@ let iter_expr loop (e,p) = | EFunction(_,f) -> List.iter (fun (_,_,_,_,eo) -> opt eo) f.f_args; opt f.f_expr - | EVars vl -> List.iter (fun (_,_,_,eo) -> opt eo) vl + | EVars vl -> List.iter (fun (_,_,_,eo,_) -> opt eo) vl let s_object_key_name name = function | DoubleQuotes -> "\"" ^ StringHelper.s_escape name ^ "\"" @@ -885,8 +885,10 @@ module Printer = struct if List.length tl > 0 then "<" ^ String.concat ", " (List.map (s_type_param tabs) tl) ^ ">" else "" and s_func_arg tabs ((n,_),o,_,t,e) = if o then "?" else "" ^ n ^ s_opt_type_hint tabs t ":" ^ s_opt_expr tabs e " = " - and s_var tabs ((n,_),_,t,e) = - n ^ (s_opt_type_hint tabs t ":") ^ s_opt_expr tabs e " = " + and s_var tabs ((n,_),_,t,e,ml) = + let s = n ^ (s_opt_type_hint tabs t ":") ^ s_opt_expr tabs e " = " in + if ml = [] then s + else (String.concat " " (List.map (s_metadata tabs) ml)) ^ " " ^ s and s_case tabs (el,e1,e2,_) = "case " ^ s_expr_list tabs el ", " ^ (match e1 with None -> ":" | Some e -> " if (" ^ s_expr_inner tabs e ^ "):") ^ @@ -1032,7 +1034,7 @@ module Expr = struct loop e1 | EVars vl -> add "EVars"; - List.iter (fun ((n,p),_,cto,eo) -> + List.iter (fun ((n,p),_,cto,eo,_) -> add (Printf.sprintf "%s %s%s" tabs n (match cto with None -> "" | Some (ct,_) -> ":" ^ Printer.s_complex_type "" ct)); match eo with | None -> () diff --git a/src/core/tOther.ml b/src/core/tOther.ml index 8f00387c529..c515a694290 100644 --- a/src/core/tOther.ml +++ b/src/core/tOther.ml @@ -103,7 +103,7 @@ module TExprToExpr = struct let arg (v,c) = (v.v_name,v.v_pos), false, v.v_meta, mk_type_hint v.v_type null_pos, (match c with None -> None | Some c -> Some (convert_expr c)) in EFunction (FKAnonymous,{ f_params = []; f_args = List.map arg f.tf_args; f_type = mk_type_hint f.tf_type null_pos; f_expr = Some (convert_expr f.tf_expr) }) | TVar (v,eo) -> - EVars ([(v.v_name,v.v_pos), has_var_flag v VFinal, mk_type_hint v.v_type v.v_pos, eopt eo]) + EVars ([(v.v_name,v.v_pos), has_var_flag v VFinal, mk_type_hint v.v_type v.v_pos, eopt eo, v.v_meta]) | TBlock el -> EBlock (List.map convert_expr el) | TFor (v,it,e) -> let ein = (EBinop (OpIn,(EConst (Ident v.v_name),it.epos),convert_expr it),it.epos) in diff --git a/src/macro/eval/evalDebugMisc.ml b/src/macro/eval/evalDebugMisc.ml index 7182cd6b5b7..84506a18094 100644 --- a/src/macro/eval/evalDebugMisc.ml +++ b/src/macro/eval/evalDebugMisc.ml @@ -329,7 +329,7 @@ let rec expr_to_value ctx env e = let v1 = loop e1 in throw v1 (pos e) | EVars vl -> - List.iter (fun ((n,_),_,_,eo) -> + List.iter (fun ((n,_),_,_,eo,_) -> match eo with | Some e -> env.env_extra_locals <- IntMap.add (hash n) (loop e) env.env_extra_locals diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index 21ab3f5d899..9be97d621dd 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -439,13 +439,14 @@ and encode_expr e = | EUnop (op,flag,e) -> 9, [encode_unop op; vbool (match flag with Prefix -> false | Postfix -> true); loop e] | EVars vl -> - 10, [encode_array (List.map (fun (v,final,t,eo) -> + 10, [encode_array (List.map (fun (v,final,t,eo,ml) -> encode_obj [ "name",encode_placed_name v; "name_pos",encode_pos (pos v); "isFinal",vbool final; "type",null encode_ctype t; "expr",null loop eo; + "meta",encode_meta_content ml; ] ) vl)] | EFunction (kind,f) -> @@ -773,7 +774,9 @@ and decode_expr v = EVars (List.map (fun v -> let vfinal = field v "isFinal" in let final = if vfinal == vnull then false else decode_bool vfinal in - ((decode_placed_name (field v "name_pos") (field v "name")),final,opt decode_ctype (field v "type"),opt loop (field v "expr")) + let vmeta = field v "meta" in + let meta = if vmeta == vnull then [] else decode_meta_content vmeta in + ((decode_placed_name (field v "name_pos") (field v "name")),final,opt decode_ctype (field v "type"),opt loop (field v "expr"),meta) ) (decode_array vl)) | 11, [kind;f] -> EFunction (decode_function_kind kind,decode_fun f) diff --git a/src/optimization/optimizer.ml b/src/optimization/optimizer.ml index cd73d52c790..af4a4694456 100644 --- a/src/optimization/optimizer.ml +++ b/src/optimization/optimizer.ml @@ -669,10 +669,10 @@ let optimize_completion_expr e args = ()); map e | EVars vl -> - let vl = List.map (fun ((v,pv),final,t,e) -> + let vl = List.map (fun ((v,pv),final,t,e,ml) -> let e = (match e with None -> None | Some e -> Some (loop e)) in decl v (Option.map fst t) e; - ((v,pv),final,t,e) + ((v,pv),final,t,e,ml) ) vl in (EVars vl,p) | EBlock el -> @@ -719,7 +719,7 @@ let optimize_completion_expr e args = (fun (name, pos) -> let etmp = (EConst (Ident "`tmp"),pos) in decl name None (Some (EBlock [ - (EVars [("`tmp",null_pos),false,None,None],p); + (EVars [("`tmp",null_pos),false,None,None,[]],p); (EFor(header,(EBinop (OpAssign,etmp,(EConst (Ident name),p)),p)), p); etmp ],p)); @@ -817,7 +817,7 @@ let optimize_completion_expr e args = with Not_found -> let e = subst_locals lc e in let name = "`tmp_" ^ string_of_int id in - tmp_locals := ((name,null_pos),false,None,Some e) :: !tmp_locals; + tmp_locals := ((name,null_pos),false,None,Some e,[]) :: !tmp_locals; tmp_hlocals := PMap.add id name !tmp_hlocals; name ) in diff --git a/src/syntax/grammar.mly b/src/syntax/grammar.mly index 5100fb7bfdc..9361afe9c09 100644 --- a/src/syntax/grammar.mly +++ b/src/syntax/grammar.mly @@ -1151,33 +1151,35 @@ and parse_array_decl p1 s = in EArrayDecl (List.rev el),punion p1 p2 -and parse_var_decl_head final = parser - | [< name, p = dollar_ident; t = popt parse_type_hint >] -> (name,final,t,p) +and parse_var_decl_head final s = + let meta = parse_meta s in + match s with parser + | [< name, p = dollar_ident; t = popt parse_type_hint >] -> (meta,name,final,t,p) and parse_var_assignment = parser | [< '(Binop OpAssign,p1); s >] -> Some (secure_expr s) | [< >] -> None -and parse_var_assignment_resume final vl name pn t s = +and parse_var_assignment_resume final vl name pn t meta s = let eo = parse_var_assignment s in - ((name,pn),final,t,eo) + ((name,pn),final,t,eo,meta) and parse_var_decls_next final vl = parser - | [< '(Comma,p1); name,final,t,pn = parse_var_decl_head final; s >] -> - let v_decl = parse_var_assignment_resume final vl name pn t s in + | [< '(Comma,p1); meta,name,final,t,pn = parse_var_decl_head final; s >] -> + let v_decl = parse_var_assignment_resume final vl name pn t meta s in parse_var_decls_next final (v_decl :: vl) s | [< >] -> vl and parse_var_decls final p1 = parser - | [< name,final,t,pn = parse_var_decl_head final; s >] -> - let v_decl = parse_var_assignment_resume final [] name pn t s in + | [< meta,name,final,t,pn = parse_var_decl_head final; s >] -> + let v_decl = parse_var_assignment_resume final [] name pn t meta s in List.rev (parse_var_decls_next final [v_decl] s) | [< s >] -> error (Custom "Missing variable identifier") p1 and parse_var_decl final = parser - | [< name,final,t,pn = parse_var_decl_head final; v_decl = parse_var_assignment_resume final [] name pn t >] -> v_decl + | [< meta,name,final,t,pn = parse_var_decl_head final; v_decl = parse_var_assignment_resume final [] name pn t meta >] -> v_decl and inline_function = parser | [< '(Kwd Inline,_); '(Kwd Function,p1) >] -> true, p1 @@ -1499,8 +1501,8 @@ and parse_guard = parser e and expr_or_var = parser - | [< '(Kwd Var,p1); name,p2 = dollar_ident; >] -> EVars [(name,p2),false,None,None],punion p1 p2 - | [< '(Kwd Final,p1); name,p2 = dollar_ident; >] -> EVars [(name,p2),true,None,None],punion p1 p2 + | [< '(Kwd Var,p1); name,p2 = dollar_ident; >] -> EVars [(name,p2),false,None,None,[]],punion p1 p2 + | [< '(Kwd Final,p1); name,p2 = dollar_ident; >] -> EVars [(name,p2),true,None,None,[]],punion p1 p2 | [< e = secure_expr >] -> e and parse_switch_cases eswitch cases = parser diff --git a/src/syntax/reification.ml b/src/syntax/reification.ml index 91bfc0bc741..e94cb4e1d41 100644 --- a/src/syntax/reification.ml +++ b/src/syntax/reification.ml @@ -278,12 +278,13 @@ let reify in_macro = ) [] p in expr "EUnop" [op;to_bool (flag = Postfix) p;loop e] | EVars vl -> - expr "EVars" [to_array (fun ((n,pn),final,th,e) p -> + expr "EVars" [to_array (fun ((n,pn),final,th,e,ml) p -> let fields = [ "name", to_string n pn; "type", to_opt to_type_hint th p; "expr", to_opt to_expr e p; "isFinal",to_bool final p; + "meta",to_meta ml p; ] in to_obj fields p ) vl p] diff --git a/src/typing/macroContext.ml b/src/typing/macroContext.ml index c053737d52f..0d86ddce902 100644 --- a/src/typing/macroContext.ml +++ b/src/typing/macroContext.ml @@ -730,7 +730,7 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = else List.map Interp.decode_field (Interp.decode_array v) in - Some (EVars [("fields",null_pos),false,Some (CTAnonymous fields,p),None],p) + Some (EVars [("fields",null_pos),false,Some (CTAnonymous fields,p),None,[]],p) ) | MMacroType -> "ComplexType",(fun () -> diff --git a/src/typing/matcher.ml b/src/typing/matcher.ml index e867dfbf3f2..4adec2cd182 100644 --- a/src/typing/matcher.ml +++ b/src/typing/matcher.ml @@ -336,7 +336,7 @@ module Pattern = struct if i = "_" then PatAny else handle_ident i (pos e) end - | EVars([(s,p),final,None,None]) -> + | EVars([(s,p),final,None,None,[]]) -> let v = add_local final s p in PatVariable v | ECall(e1,el) -> diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index 1471a161610..c4b566c78a7 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -226,7 +226,7 @@ let transform_abstract_field com this_t a_t a f = | FProp _ when not stat -> error "Member property accessors must be get/set or never" p; | FFun fu when fst f.cff_name = "new" && not stat -> - let init p = (EVars [("this",null_pos),false,Some this_t,None],p) in + let init p = (EVars [("this",null_pos),false,Some this_t,None,[]],p) in let cast e = (ECast(e,None)),pos e in let ret p = (EReturn (Some (cast (EConst (Ident "this"),p))),p) in let meta = (Meta.Impl,[],null_pos) :: (Meta.NoCompletion,[],null_pos) :: f.cff_meta in @@ -419,7 +419,7 @@ let build_enum_abstract ctx c a fields p = | _ -> () ) fields; - EVars [("",null_pos),false,Some (CTAnonymous fields,p),None],p + EVars [("",null_pos),false,Some (CTAnonymous fields,p),None,[]],p let apply_macro ctx mode path el p = let cpath, meth = (match List.rev (ExtString.String.nsplit path ".") with @@ -664,7 +664,7 @@ let build_fields (ctx,cctx) c fields = c.cl_build <- (fun() -> BuildMacro pending); build_module_def ctx (TClassDecl c) c.cl_meta get_fields cctx.context_init (fun (e,p) -> match e with - | EVars [_,_,Some (CTAnonymous f,p),None] -> + | EVars [_,_,Some (CTAnonymous f,p),None,_] -> let f = List.map (fun f -> transform_field (ctx,cctx) c f fields p) f in fields := f | _ -> error "Class build macro must return a single variable with anonymous fields" p diff --git a/src/typing/typeloadModule.ml b/src/typing/typeloadModule.ml index cd085644915..19db5fd82cd 100644 --- a/src/typing/typeloadModule.ml +++ b/src/typing/typeloadModule.ml @@ -736,7 +736,7 @@ let init_module_type ctx context_init (decl,p) = in TypeloadFields.build_module_def ctx (TEnumDecl e) e.e_meta get_constructs context_init (fun (e,p) -> match e with - | EVars [_,_,Some (CTAnonymous fields,p),None] -> + | EVars [_,_,Some (CTAnonymous fields,p),None,_] -> constructs := List.map (fun f -> let args, params, t = (match f.cff_kind with | FVar (t,None) -> [], [], t diff --git a/src/typing/typer.ml b/src/typing/typer.ml index 862d27e52e1..99a1149d3a0 100644 --- a/src/typing/typer.ml +++ b/src/typing/typer.ml @@ -1443,7 +1443,7 @@ and type_array_access ctx e1 e2 p mode = Calls.array_access ctx e1 e2 mode p and type_vars ctx vl p = - let vl = List.map (fun ((v,pv),final,t,e) -> + let vl = List.map (fun ((v,pv),final,t,e,ml) -> try let t = Typeload.load_type_hint ctx p t in let e = (match e with @@ -1454,6 +1454,7 @@ and type_vars ctx vl p = Some e ) in let v = add_local_with_origin ctx TVOLocalVariable v t pv in + v.v_meta <- ml; if final then add_var_flag v VFinal; if ctx.in_display && DisplayPosition.display_position#enclosed_in pv then DisplayEmitter.display_variable ctx v pv; diff --git a/std/haxe/macro/Expr.hx b/std/haxe/macro/Expr.hx index 1d6f338aba5..53428fb406f 100644 --- a/std/haxe/macro/Expr.hx +++ b/std/haxe/macro/Expr.hx @@ -315,6 +315,11 @@ typedef Var = { Whether or not the variable can be assigned to. **/ var ?isFinal:Bool; + + /** + Metadata associatied with the variable, if available. + **/ + var ?meta:Metadata; } /** diff --git a/std/haxe/macro/Printer.hx b/std/haxe/macro/Printer.hx index 3d9fb62dfbf..4814999c9bb 100644 --- a/std/haxe/macro/Printer.hx +++ b/std/haxe/macro/Printer.hx @@ -208,8 +208,13 @@ class Printer { + opt(func.expr, printExpr, " "); } - public function printVar(v:Var) - return v.name + opt(v.type, printComplexType, ":") + opt(v.expr, printExpr, " = "); + public function printVar(v:Var) { + var s = v.name + opt(v.type, printComplexType, ":") + opt(v.expr, printExpr, " = "); + return switch v.meta { + case null|[]: s; + case meta: meta.map(printMetadata).join(" ") + " " + s; + } + } public function printObjectFieldKey(of:ObjectField) { return switch (of.quotes) { diff --git a/tests/unit/src/unit/TestMacro.hx b/tests/unit/src/unit/TestMacro.hx index 4caf14dfdd3..a1117a1df90 100644 --- a/tests/unit/src/unit/TestMacro.hx +++ b/tests/unit/src/unit/TestMacro.hx @@ -38,6 +38,7 @@ class TestMacro extends Test { parseAndPrint("var a:A"); parseAndPrint("var a = b"); parseAndPrint("var a:A = b"); + parseAndPrint("var @:a(b) c:D = e"); parseAndPrint("function() { }"); parseAndPrint("for (a in b) { }"); parseAndPrint("if (a) b");