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 years ago
parent
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";
 	CommonCache.lock_signature com "after_init_macros";
 	List.iter (fun f -> f ()) (List.rev com.callbacks#get_after_init_macros);
 	List.iter (fun f -> f ()) (List.rev com.callbacks#get_after_init_macros);
 	run_or_diagnose com (fun () ->
 	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);
 		List.iter (fun cpath -> ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath null_pos)) (List.rev classes);
 		Finalization.finalize tctx;
 		Finalization.finalize tctx;
 	) ();
 	) ();

+ 0 - 8
src/compiler/server.ml

@@ -498,14 +498,6 @@ let create sctx write params =
 		ServerMessage.defines ctx.com "";
 		ServerMessage.defines ctx.com "";
 		ServerMessage.signature ctx.com "" sign;
 		ServerMessage.signature ctx.com "" sign;
 		ServerMessage.display_position ctx.com "" (DisplayPosition.display_position#get);
 		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
 		try
 			if (Hashtbl.find sctx.class_paths sign) <> ctx.com.class_path then begin
 			if (Hashtbl.find sctx.class_paths sign) <> ctx.com.class_path then begin
 				ServerMessage.class_paths_changed ctx.com "";
 				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 () ->
 		TypeloadParse.current_stdin := jsonrpc#get_opt_param (fun () ->
 			let s = jsonrpc#get_string_param "contents" in
 			let s = jsonrpc#get_string_param "contents" in
 			Common.define com Define.DisplayStdin; (* TODO: awkward *)
 			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
 			Some s
 		) None;
 		) None;
 		Parser.was_auto_triggered := was_auto_triggered;
 		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
 	let decls = List.rev !decls in
 	decls, List.rev tdecls
 	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
 	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
 	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 is_flat = ref true in
 		let fields = ref PMap.empty in
 		let fields = ref PMap.empty in
 		List.iter (fun c ->
 		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);
 			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;
 			e.e_constrs <- PMap.add f.ef_name f e.e_constrs;
 			fields := PMap.add cf.cf_name cf !fields;
 			fields := PMap.add cf.cf_name cf !fields;
 			incr index;
 			incr index;