Browse Source

Implement texpr-based display support (#8962)

* modules, fields, type-hints

* metadata

* start on expressions

* try another approach for expression handling

* process import.hx

* cleanup

* cleanup more

* support class relations

* don't use texpr-based diagnostics for now

* support type parameters and other module types

Still problems with abstracts

* deal with abstract implementation classes
Simon Krajewski 5 năm trước cách đây
mục cha
commit
099e5ce5ae

+ 1 - 0
src/compiler/haxe.ml

@@ -501,6 +501,7 @@ let do_type tctx config_macros classes =
 	CommonCache.lock_signature com "after_init_macros";
 	List.iter (fun f -> f ()) (List.rev com.callbacks#get_after_init_macros);
 	run_or_diagnose com (fun () ->
+		if com.display.dms_kind <> DMNone then Option.may (DisplayTexpr.check_display_file tctx) (CompilationServer.get ());
 		List.iter (fun cpath -> ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath null_pos)) (List.rev classes);
 		Finalization.finalize tctx;
 	) ();

+ 0 - 8
src/compiler/server.ml

@@ -498,14 +498,6 @@ let create sctx write params =
 		ServerMessage.defines ctx.com "";
 		ServerMessage.signature ctx.com "" sign;
 		ServerMessage.display_position ctx.com "" (DisplayPosition.display_position#get);
-		(* Special case for diagnostics: It's not treated as a display mode, but we still want to invalidate the
-			current file in order to run diagnostics on it again. *)
-		if ctx.com.display.dms_display || (match ctx.com.display.dms_kind with DMDiagnostics _ -> true | _ -> false) then begin
-			let file = (DisplayPosition.display_position#get).pfile in
-			(* force parsing again : if the completion point have been changed *)
-			cs#remove_files file;
-			cs#taint_modules file;
-		end;
 		try
 			if (Hashtbl.find sctx.class_paths sign) <> ctx.com.class_path then begin
 				ServerMessage.class_paths_changed ctx.com "";

+ 0 - 2
src/context/display/displayJson.ml

@@ -63,8 +63,6 @@ class display_handler (jsonrpc : jsonrpc_handler) com (cs : CompilationServer.t)
 		TypeloadParse.current_stdin := jsonrpc#get_opt_param (fun () ->
 			let s = jsonrpc#get_string_param "contents" in
 			Common.define com Define.DisplayStdin; (* TODO: awkward *)
-			(* Remove our current display file from the cache so the server doesn't pick it up *)
-			cs#remove_files file;
 			Some s
 		) None;
 		Parser.was_auto_triggered := was_auto_triggered;

+ 159 - 0
src/context/display/displayTexpr.ml

@@ -0,0 +1,159 @@
+open Globals
+open Common
+open Ast
+open Type
+open Typecore
+open DisplayPosition
+open CompletionItem
+open CompilationServer
+open ClassFieldOrigin
+
+let find_field_by_position sc p =
+	List.find (fun cff ->
+		if pos cff.cff_name = p then true else false
+	) sc.d_data
+
+let find_enum_field_by_position sc p =
+	List.find (fun eff ->
+		if pos eff.ec_name = p then true else false
+	) sc.d_data
+
+let find_class_by_position cfile p =
+	let rec loop dl = match dl with
+		| (EClass c,_) :: dl when pos c.d_name = p -> c
+		| _ :: dl -> loop dl
+		| [] -> raise Not_found
+	in
+	loop cfile.c_decls
+
+let find_enum_by_position cfile p =
+	let rec loop dl = match dl with
+		| (EEnum en,_) :: dl when pos en.d_name = p -> en
+		| _ :: dl -> loop dl
+		| [] -> raise Not_found
+	in
+	loop cfile.c_decls
+
+let find_typedef_by_position cfile p =
+	let rec loop dl = match dl with
+		| (ETypedef td,_) :: dl when pos td.d_name = p -> td
+		| _ :: dl -> loop dl
+		| [] -> raise Not_found
+	in
+	loop cfile.c_decls
+
+let find_abstract_by_position cfile p =
+	let rec loop dl = match dl with
+		| (EAbstract a,_) :: dl when pos a.d_name = p -> a
+		| _ :: dl -> loop dl
+		| [] -> raise Not_found
+	in
+	loop cfile.c_decls
+
+let check_display_field ctx sc c cf =
+	let cff = find_field_by_position sc cf.cf_name_pos in
+	let ctx,cctx = TypeloadFields.create_class_context ctx c (fun () -> ()) cf.cf_pos in
+	let ctx,fctx = TypeloadFields.create_field_context (ctx,cctx) c cff in
+	let cf = TypeloadFields.init_field (ctx,cctx,fctx) cff in
+	ignore(follow cf.cf_type)
+
+let check_display_class ctx cc cfile c =
+	let check_field sc cf =
+		if display_position#enclosed_in cf.cf_pos then
+			check_display_field ctx sc c cf;
+		DisplayEmitter.check_display_metadata ctx cf.cf_meta
+	in
+	match c.cl_kind with
+	| KAbstractImpl a ->
+		let sa = find_abstract_by_position cfile c.cl_name_pos in
+		let check_field = check_field sa in
+		List.iter check_field c.cl_ordered_statics;
+	| _ ->
+		let sc = find_class_by_position cfile c.cl_name_pos in
+		ignore(Typeload.type_type_params ctx c.cl_path (fun() -> c.cl_params) null_pos sc.d_params);
+		List.iter (function
+			| (HExtends(ct,p) | HImplements(ct,p)) when display_position#enclosed_in p ->
+				ignore(Typeload.load_instance ~allow_display:true ctx (ct,p) false)
+			| _ ->
+				()
+		) sc.d_flags;
+		let check_field = check_field sc in
+		List.iter check_field c.cl_ordered_statics;
+		List.iter check_field c.cl_ordered_fields;
+		Option.may check_field c.cl_constructor
+
+let check_display_enum ctx cc cfile en =
+	let se = find_enum_by_position cfile en.e_name_pos in
+	ignore(Typeload.type_type_params ctx en.e_path (fun() -> en.e_params) null_pos se.d_params);
+	PMap.iter (fun _ ef ->
+		if display_position#enclosed_in ef.ef_pos then begin
+			let sef = find_enum_field_by_position se ef.ef_name_pos in
+			ignore(TypeloadModule.load_enum_field ctx en (TEnum (en,List.map snd en.e_params)) (ref false) (ref 0) sef)
+		end
+	) en.e_constrs
+
+let check_display_typedef ctx cc cfile td =
+	let st = find_typedef_by_position cfile td.t_name_pos in
+	ignore(Typeload.type_type_params ctx td.t_path (fun() -> td.t_params) null_pos st.d_params);
+	ignore(Typeload.load_complex_type ctx true st.d_data)
+
+let check_display_abstract ctx cc cfile a =
+	let sa = find_abstract_by_position cfile a.a_name_pos in
+	ignore(Typeload.type_type_params ctx a.a_path (fun() -> a.a_params) null_pos sa.d_params);
+	List.iter (function
+		| (AbOver(ct,p) | AbFrom(ct,p) | AbTo(ct,p)) when display_position#enclosed_in p ->
+			ignore(Typeload.load_complex_type ctx true (ct,p))
+		| _ ->
+			()
+	) sa.d_flags
+
+let check_display_module ctx cc cfile m =
+	let imports = List.filter (function
+		| (EImport _ | EUsing _),_ -> true
+		| _ -> false
+	) cfile.c_decls in
+	let imports = TypeloadModule.handle_import_hx ctx m imports null_pos in
+	let ctx = TypeloadModule.type_types_into_module ctx m imports null_pos in
+	List.iter (fun md ->
+		let infos = t_infos md in
+		if display_position#enclosed_in infos.mt_name_pos then
+			DisplayEmitter.display_module_type ctx md infos.mt_name_pos;
+		begin if display_position#enclosed_in infos.mt_pos then match md with
+		| TClassDecl c ->
+			check_display_class ctx cc cfile c
+		| TEnumDecl en ->
+			check_display_enum ctx cc cfile en
+		| TTypeDecl td ->
+			check_display_typedef ctx cc cfile td
+		| TAbstractDecl a ->
+			check_display_abstract ctx cc cfile a
+		end;
+		DisplayEmitter.check_display_metadata ctx infos.mt_meta
+	) m.m_types
+
+let check_display_file ctx cs =
+	match ctx.com.cache with
+	| Some cc ->
+		begin try
+			(* TODO: diagnostics currently relies on information collected during typing. *)
+			begin match ctx.com.display.dms_kind with
+				| DMDiagnostics _ -> raise Not_found
+				| _ -> ()
+			end;
+			let p = DisplayPosition.display_position#get in
+			let cfile = cc#find_file (Path.unique_full_path p.pfile) in
+			let path = (cfile.c_package,get_module_name_of_cfile p.pfile cfile) in
+			let m = cc#find_module path in
+			check_display_module ctx cc cfile m
+		with Not_found ->
+			(* Special case for diagnostics: It's not treated as a display mode, but we still want to invalidate the
+				current file in order to run diagnostics on it again. *)
+			if ctx.com.display.dms_display || (match ctx.com.display.dms_kind with DMDiagnostics _ -> true | _ -> false) then begin
+				let file = (DisplayPosition.display_position#get).pfile in
+				(* force parsing again : if the completion point have been changed *)
+				cs#remove_files file;
+				cs#taint_modules file;
+			end;
+		end
+	| None ->
+		()

+ 53 - 49
src/typing/typeloadModule.ml

@@ -344,6 +344,58 @@ let module_pass_1 ctx m tdecls loadp =
 	let decls = List.rev !decls in
 	decls, List.rev tdecls
 
+let load_enum_field ctx e et is_flat index c =
+	let p = c.ec_pos in
+	let params = ref [] in
+	params := type_type_params ~enum_constructor:true ctx ([],fst c.ec_name) (fun() -> !params) c.ec_pos c.ec_params;
+	let params = !params in
+	let ctx = { ctx with type_params = params @ ctx.type_params } in
+	let rt = (match c.ec_type with
+		| None -> et
+		| Some (t,pt) ->
+			let t = load_complex_type ctx true (t,pt) in
+			(match follow t with
+			| TEnum (te,_) when te == e ->
+				()
+			| _ ->
+				error "Explicit enum type must be of the same enum type" pt);
+			t
+	) in
+	let t = (match c.ec_args with
+		| [] -> rt
+		| l ->
+			is_flat := false;
+			let pnames = ref PMap.empty in
+			TFun (List.map (fun (s,opt,(t,tp)) ->
+				(match t with CTPath({tpackage=[];tname="Void"}) -> error "Arguments of type Void are not allowed in enum constructors" tp | _ -> ());
+				if PMap.mem s (!pnames) then error ("Duplicate argument `" ^ s ^ "` in enum constructor " ^ fst c.ec_name) p;
+				pnames := PMap.add s () (!pnames);
+				s, opt, load_type_hint ~opt ctx p (Some (t,tp))
+			) l, rt)
+	) in
+	let f = {
+		ef_name = fst c.ec_name;
+		ef_type = t;
+		ef_pos = p;
+		ef_name_pos = snd c.ec_name;
+		ef_doc = c.ec_doc;
+		ef_index = !index;
+		ef_params = params;
+		ef_meta = c.ec_meta;
+	} in
+	let cf = {
+		(mk_field f.ef_name f.ef_type p f.ef_name_pos) with
+		cf_kind = (match follow f.ef_type with
+			| TFun _ -> Method MethNormal
+			| _ -> Var { v_read = AccNormal; v_write = AccNo }
+		);
+		cf_doc = f.ef_doc;
+		cf_params = f.ef_params;
+	} in
+	if ctx.is_display_file && DisplayPosition.display_position#enclosed_in f.ef_name_pos then
+		DisplayEmitter.display_enum_field ctx e f p;
+	f,cf
+
 (*
 	In this pass, we can access load and access other modules types, but we cannot follow them or access their structure
 	since they have not been setup. We also build a context_init list that will be evaluated the first time we evaluate
@@ -617,56 +669,8 @@ let init_module_type ctx context_init do_init (decl,p) =
 		let is_flat = ref true in
 		let fields = ref PMap.empty in
 		List.iter (fun c ->
-			let p = c.ec_pos in
-			let params = ref [] in
-			params := type_type_params ~enum_constructor:true ctx ([],fst c.ec_name) (fun() -> !params) c.ec_pos c.ec_params;
-			let params = !params in
-			let ctx = { ctx with type_params = params @ ctx.type_params } in
-			let rt = (match c.ec_type with
-				| None -> et
-				| Some (t,pt) ->
-					let t = load_complex_type ctx true (t,pt) in
-					(match follow t with
-					| TEnum (te,_) when te == e ->
-						()
-					| _ ->
-						error "Explicit enum type must be of the same enum type" pt);
-					t
-			) in
-			let t = (match c.ec_args with
-				| [] -> rt
-				| l ->
-					is_flat := false;
-					let pnames = ref PMap.empty in
-					TFun (List.map (fun (s,opt,(t,tp)) ->
-						(match t with CTPath({tpackage=[];tname="Void"}) -> error "Arguments of type Void are not allowed in enum constructors" tp | _ -> ());
-						if PMap.mem s (!pnames) then error ("Duplicate argument `" ^ s ^ "` in enum constructor " ^ fst c.ec_name) p;
-						pnames := PMap.add s () (!pnames);
-						s, opt, load_type_hint ~opt ctx p (Some (t,tp))
-					) l, rt)
-			) in
 			if PMap.mem (fst c.ec_name) e.e_constrs then error ("Duplicate constructor " ^ fst c.ec_name) (pos c.ec_name);
-			let f = {
-				ef_name = fst c.ec_name;
-				ef_type = t;
-				ef_pos = p;
-				ef_name_pos = snd c.ec_name;
-				ef_doc = c.ec_doc;
-				ef_index = !index;
-				ef_params = params;
-				ef_meta = c.ec_meta;
-			} in
-			let cf = {
-				(mk_field f.ef_name f.ef_type p f.ef_name_pos) with
-				cf_kind = (match follow f.ef_type with
-					| TFun _ -> Method MethNormal
-					| _ -> Var { v_read = AccNormal; v_write = AccNo }
-				);
-				cf_doc = f.ef_doc;
-				cf_params = f.ef_params;
-			} in
- 			if ctx.is_display_file && DisplayPosition.display_position#enclosed_in f.ef_name_pos then
- 				DisplayEmitter.display_enum_field ctx e f p;
+			let f,cf = load_enum_field ctx e et is_flat index c in
 			e.e_constrs <- PMap.add f.ef_name f e.e_constrs;
 			fields := PMap.add cf.cf_name cf !fields;
 			incr index;