Explorar o código

Reference cleanup 2020 (#9079)

* [display] reorganize statistics a bit

* [display] extend reference finding to parent/child fields

see #9044

* [display] always walk up the chain to find overrides

* [display] collect interface field implementations transitively

* [display] consider subclasses transitively as well

* [display] and make implementation relations transitive too

* [display] add display/implementation

see #9043

* [display] add GotoImplementation

* [display] speed up implementation finding
Simon Krajewski %!s(int64=5) %!d(string=hai) anos
pai
achega
a210a2365d

+ 3 - 1
src/compiler/displayOutput.ml

@@ -414,10 +414,12 @@ let process_global_display_mode com tctx =
 	match com.display.dms_kind with
 	| DMUsage with_definition ->
 		FindReferences.find_references tctx com with_definition
+	| DMImplementation ->
+		FindReferences.find_implementations tctx com
 	| DMDiagnostics global ->
 		Diagnostics.run com global
 	| DMStatistics ->
-		let stats = Statistics.collect_statistics tctx (SFFile (DisplayPosition.display_position#get).pfile) in
+		let stats = Statistics.collect_statistics tctx (SFFile (DisplayPosition.display_position#get).pfile) true in
 		raise_statistics (Statistics.Printer.print_statistics stats)
 	| DMModuleSymbols (Some "") -> ()
 	| DMModuleSymbols filter ->

+ 0 - 3
src/context/common.ml

@@ -159,7 +159,6 @@ type shared_display_information = {
 
 type display_information = {
 	mutable unresolved_identifiers : (string * pos * (string * CompletionItem.t * int) list) list;
-	mutable interface_field_implementations : (tclass * tclass_field * tclass * tclass_field option) list;
 	mutable display_module_has_macro_defines : bool;
 }
 
@@ -440,7 +439,6 @@ let create version s_version args =
 		};
 		display_information = {
 			unresolved_identifiers = [];
-			interface_field_implementations = [];
 			display_module_has_macro_defines = false;
 		};
 		sys_args = args;
@@ -519,7 +517,6 @@ let clone com =
 		callbacks = new compiler_callbacks;
 		display_information = {
 			unresolved_identifiers = [];
-			interface_field_implementations = [];
 			display_module_has_macro_defines = false;
 		};
 		defines = {

+ 1 - 1
src/context/display/display.ml

@@ -22,7 +22,7 @@ let parse_module ctx m p =
 	display_position#run_outside (fun () -> TypeloadParse.parse_module ctx m p)
 
 module ReferencePosition = struct
-	let reference_position = ref ("",null_pos,KVar)
+	let reference_position = ref ("",null_pos,SKOther)
 	let set (s,p,k) = reference_position := (s,{p with pfile = Path.unique_full_path p.pfile},k)
 	let get () = !reference_position
 end

+ 13 - 7
src/context/display/displayEmitter.ml

@@ -13,6 +13,12 @@ open Common
 open Display
 open DisplayPosition
 
+let symbol_of_module_type = function
+	| TClassDecl c -> SKClass c
+	| TEnumDecl en -> SKEnum en
+	| TTypeDecl td -> SKTypedef td
+	| TAbstractDecl a -> SKAbstract a
+
 let display_module_type ctx mt p = match ctx.com.display.dms_kind with
 	| DMDefinition | DMTypeDefinition ->
 		begin match mt with
@@ -22,9 +28,9 @@ let display_module_type ctx mt p = match ctx.com.display.dms_kind with
 		| _ ->
 			raise_positions [(t_infos mt).mt_name_pos];
 		end
-	| DMUsage _ ->
+	| DMUsage _ | DMImplementation ->
 		let infos = t_infos mt in
-		ReferencePosition.set (snd infos.mt_path,infos.mt_name_pos,KModuleType)
+		ReferencePosition.set (snd infos.mt_path,infos.mt_name_pos,symbol_of_module_type mt)
 	| DMHover ->
 		let t = type_of_module_type mt in
 		let ct = CompletionType.from_type (get_import_status ctx) t in
@@ -81,7 +87,7 @@ let raise_position_of_type t =
 let display_variable ctx v p = match ctx.com.display.dms_kind with
 	| DMDefinition -> raise_positions [v.v_pos]
 	| DMTypeDefinition -> raise_position_of_type v.v_type
-	| DMUsage _ -> ReferencePosition.set (v.v_name,v.v_pos,KVar)
+	| DMUsage _ -> ReferencePosition.set (v.v_name,v.v_pos,SKVariable v)
 	| DMHover ->
 		let ct = CompletionType.from_type (get_import_status ctx) ~values:(get_value_meta v.v_meta) v.v_type in
 		raise_hover (make_ci_local v (v.v_type,ct)) None p
@@ -90,13 +96,13 @@ let display_variable ctx v p = match ctx.com.display.dms_kind with
 let display_field ctx origin scope cf p = match ctx.com.display.dms_kind with
 	| DMDefinition -> raise_positions [cf.cf_name_pos]
 	| DMTypeDefinition -> raise_position_of_type cf.cf_type
-	| DMUsage _ ->
+	| DMUsage _ | DMImplementation ->
 		let name,kind = match cf.cf_name,origin with
 			| "new",(Self (TClassDecl c) | Parent(TClassDecl c)) ->
 				(* For constructors, we care about the class name so we don't end up looking for "new". *)
-				snd c.cl_path,KConstructor
+				snd c.cl_path,SKConstructor cf
 			| _ ->
-				cf.cf_name,KClassField
+				cf.cf_name,SKField cf
 		in
 		ReferencePosition.set (name,cf.cf_name_pos,kind)
 	| DMHover ->
@@ -119,7 +125,7 @@ let maybe_display_field ctx origin scope cf p =
 let display_enum_field ctx en ef p = match ctx.com.display.dms_kind with
 	| DMDefinition -> raise_positions [ef.ef_name_pos]
 	| DMTypeDefinition -> raise_position_of_type ef.ef_type
-	| DMUsage _ -> ReferencePosition.set (ef.ef_name,ef.ef_name_pos,KEnumField)
+	| DMUsage _ -> ReferencePosition.set (ef.ef_name,ef.ef_name_pos,SKEnumField ef)
 	| DMHover ->
 		let ct = CompletionType.from_type (get_import_status ctx) ef.ef_type in
 		raise_hover (make_ci_enum_field (CompletionEnumField.make ef (Self (TEnumDecl en)) true) (ef.ef_type,ct)) None p

+ 6 - 1
src/context/display/displayJson.ml

@@ -102,7 +102,7 @@ let handler =
 				];
 				"protocolVersion",jobject [
 					"major",jint 0;
-					"minor",jint 3;
+					"minor",jint 4;
 					"patch",jint 0;
 				]
 			])
@@ -126,6 +126,11 @@ let handler =
 			hctx.display#set_display_file false true;
 			hctx.display#enable_display DMDefinition;
 		);
+		"display/implementation", (fun hctx ->
+			Common.define hctx.com Define.NoCOpt;
+			hctx.display#set_display_file false true;
+			hctx.display#enable_display (DMImplementation);
+		);
 		"display/typeDefinition", (fun hctx ->
 			Common.define hctx.com Define.NoCOpt;
 			hctx.display#set_display_file false true;

+ 39 - 7
src/context/display/findReferences.ml

@@ -6,17 +6,26 @@ open Typecore
 open CompilationServer
 open ImportHandling
 
+type reference_kind =
+	| KVar
+	| KIdent
+	| KAnyField
+	| KClassField
+	| KEnumField
+	| KModuleType
+	| KConstructor
+
 let find_possible_references kind name (pack,decls) =
 	(* Employ some heuristics: We know what kind of symbol we are looking for, so let's
 	   filter where we can. *)
 	let check kind' name' =
 		if name = name' then match kind',kind with
 			| KIdent,_
-			| KAnyField,(KAnyField | KClassField | KEnumField)
-			| KClassField,KClassField
-			| KEnumField,KEnumField
-			| KModuleType,KModuleType
-			| KConstructor,(KConstructor | KModuleType) ->
+			| KAnyField,(SKField _ | SKConstructor _ | SKEnumField _)
+			| KClassField,SKField _
+			| KEnumField,SKEnumField _
+			| KModuleType,(SKClass _ | SKEnum _ | SKTypedef _ | SKAbstract _)
+			| KConstructor,(SKConstructor _ | SKClass _) ->
 				raise Exit
 			| _ ->
 				()
@@ -172,7 +181,7 @@ let find_possible_references tctx cs =
 let find_references tctx com with_definition =
 	let name,pos,kind = Display.ReferencePosition.get () in
 	let t = Timer.timer ["display";"references";"collect"] in
-	let symbols,relations = Statistics.collect_statistics tctx (SFPos pos) in
+	let symbols,relations = Statistics.collect_statistics tctx (SFPos pos) true in
 	t();
 	let rec loop acc relations = match relations with
 		| (Statistics.Referenced,p) :: relations -> loop (p :: acc) relations
@@ -190,5 +199,28 @@ let find_references tctx com with_definition =
 		if c <> 0 then c else compare p1.pmin p2.pmin
 	) usages in
 	t();
-	Display.ReferencePosition.set ("",null_pos,KVar);
+	Display.ReferencePosition.set ("",null_pos,SKOther);
+	DisplayException.raise_positions usages
+
+let find_implementations tctx com =
+	let name,pos,kind = Display.ReferencePosition.get () in
+	let t = Timer.timer ["display";"implementations";"collect"] in
+	let symbols,relations = Statistics.collect_statistics tctx (SFPos pos) false in
+	t();
+	let rec loop acc relations = match relations with
+		| ((Statistics.Implemented | Statistics.Overridden | Statistics.Extended),p) :: relations -> loop (p :: acc) relations
+		| _ :: relations -> loop acc relations
+		| [] -> acc
+	in
+	let t = Timer.timer ["display";"implementations";"filter"] in
+	let usages = Hashtbl.fold (fun p sym acc ->
+		(try loop acc (Hashtbl.find relations p)
+		with Not_found -> acc)
+	) symbols [] in
+	let usages = List.sort (fun p1 p2 ->
+		let c = compare p1.pfile p2.pfile in
+		if c <> 0 then c else compare p1.pmin p2.pmin
+	) usages in
+	t();
+	Display.ReferencePosition.set ("",null_pos,SKOther);
 	DisplayException.raise_positions usages

+ 93 - 27
src/context/display/statistics.ml

@@ -3,6 +3,7 @@ open Ast
 open Type
 open Common
 open Typecore
+open DisplayTypes
 
 open ImportHandling
 
@@ -12,22 +13,12 @@ type relation =
 	| Overridden
 	| Referenced
 
-type symbol =
-	| SKClass of tclass
-	| SKInterface of tclass
-	| SKEnum of tenum
-	| SKTypedef of tdef
-	| SKAbstract of tabstract
-	| SKField of tclass_field
-	| SKEnumField of tenum_field
-	| SKVariable of tvar
-
 type statistics_filter =
 	| SFNone
 	| SFPos of pos
 	| SFFile of string
 
-let collect_statistics ctx pfilter =
+let collect_statistics ctx pfilter with_expressions =
 	let relations = Hashtbl.create 0 in
 	let symbols = Hashtbl.create 0 in
 	let handled_modules = Hashtbl.create 0 in
@@ -69,14 +60,34 @@ let collect_statistics ctx pfilter =
 						let cf' = PMap.find cf.cf_name c.cl_fields in
 						add_relation cf'.cf_name_pos (Overridden,cf.cf_pos)
 					with Not_found ->
-						loop c
-					end
+						()
+					end;
+					loop c
 				| _ ->
 					()
 			in
 			loop c
 		) c.cl_overrides
 	in
+	let collect_implementations c =
+		List.iter (fun cf ->
+			let rec loop c =
+				begin try
+					let cf' = PMap.find cf.cf_name c.cl_fields in
+					add_relation cf.cf_name_pos (Implemented,cf'.cf_name_pos)
+				with Not_found ->
+					()
+				end;
+				List.iter loop c.cl_descendants
+			in
+			List.iter loop c.cl_descendants
+		) c.cl_ordered_fields;
+		let rec loop c' =
+			add_relation c.cl_name_pos ((if c'.cl_interface then Extended else Implemented),c'.cl_name_pos);
+			List.iter loop c'.cl_descendants
+		in
+		List.iter loop c.cl_descendants
+	in
 	let rec find_real_constructor c = match c.cl_constructor,c.cl_super with
 		(* The pos comparison might be a bit weak, not sure... *)
 		| Some cf,_ when not (Meta.has Meta.CompilerGenerated cf.cf_meta) && c.cl_pos <> cf.cf_pos -> cf
@@ -85,8 +96,39 @@ let collect_statistics ctx pfilter =
 	in
 	let var_decl v = declare (SKVariable v) v.v_pos in
 	let patch_string_pos p s = { p with pmin = p.pmax - String.length s } in
-	let field_reference cf p =
-		add_relation cf.cf_name_pos (Referenced,patch_string_pos p cf.cf_name)
+	let field_reference co cf p =
+		let p' = patch_string_pos p cf.cf_name in
+		add_relation cf.cf_name_pos (Referenced,p');
+		(* extend to related classes for instance fields *)
+		match co with
+		| Some c ->
+			let check c =
+				try
+					let cf = PMap.find cf.cf_name c.cl_fields in
+					add_relation cf.cf_name_pos (Referenced,p')
+				with Not_found ->
+					()
+			in
+			(* to children *)
+			let rec loop c =
+				List.iter (fun c ->
+					check c;
+					loop c;
+				) c.cl_descendants
+			in
+			loop c;
+			(* to parents *)
+			let rec loop c =
+				let f (c,_) =
+					check c;
+					loop c;
+				in
+				List.iter f c.cl_implements;
+				Option.may f c.cl_super
+			in
+			loop c;
+		| None ->
+			()
 	in
 	let collect_references c e =
 		let rec loop e = match e.eexpr with
@@ -96,11 +138,13 @@ let collect_statistics ctx pfilter =
 				if e1.epos.pmin = e.epos.pmin && e1.epos.pmax <> e.epos.pmax then
 					loop e1;
 				begin match fa with
-					| FStatic(_,cf) | FInstance(_,_,cf) | FClosure(_,cf) ->
-						field_reference cf e.epos
+					| FStatic(_,cf) | FClosure(None,cf) ->
+						field_reference None cf e.epos
+					| FInstance(c,_,cf) | FClosure(Some(c,_),cf) ->
+						field_reference (Some c) cf e.epos
 					| FAnon cf ->
 						declare  (SKField cf) cf.cf_name_pos;
-						field_reference cf e.epos
+						field_reference None cf e.epos
 					| FEnum(_,ef) ->
 						add_relation ef.ef_name_pos (Referenced,patch_string_pos e.epos ef.ef_name)
 					| FDynamic _ ->
@@ -144,20 +188,45 @@ let collect_statistics ctx pfilter =
 			List.iter (fun (p,pn) -> add_relation pn (Referenced,p)) m.m_extra.m_display.m_type_hints
 		end
 	in
+	(* set up descendants *)
+	let f = function
+		| TClassDecl c ->
+				List.iter (fun (iface,_) -> add_descendant iface c) c.cl_implements;
+				begin match c.cl_super with
+					| Some (csup,_) -> add_descendant csup c
+					| None -> ()
+				end;
+		| _ ->
+			()
+	in
+	let rec loop com =
+		List.iter f com.types;
+		Option.may loop (com.get_macros())
+	in
+	loop ctx.com;
+	(* find things *)
 	let f = function
 		| TClassDecl c ->
 			check_module c.cl_module;
 			declare (if c.cl_interface then (SKInterface c) else (SKClass c)) c.cl_name_pos;
-			List.iter (fun (c',_) -> add_relation c'.cl_name_pos ((if c.cl_interface then Extended else Implemented),c.cl_name_pos)) c.cl_implements;
 			begin match c.cl_super with
 				| None -> ()
-				| Some (c',_) -> add_relation c'.cl_name_pos (Extended,c.cl_name_pos);
+				| Some (c',_) ->
+					let rec loop c' =
+						add_relation c'.cl_name_pos (Extended,c.cl_name_pos);
+						Option.may (fun (c',_) -> loop c') c'.cl_super
+					in
+					loop c'
 			end;
 			collect_overrides c;
+			if c.cl_interface then
+				collect_implementations c;
 			let field cf =
 				if cf.cf_pos.pmin > c.cl_name_pos.pmin then declare (SKField cf) cf.cf_name_pos;
-				let _ = follow cf.cf_type in
-				match cf.cf_expr with None -> () | Some e -> collect_references c e
+				if with_expressions then begin
+					let _ = follow cf.cf_type in
+					match cf.cf_expr with None -> () | Some e -> collect_references c e
+				end
 			in
 			Option.may field c.cl_constructor;
 			List.iter field c.cl_ordered_fields;
@@ -178,11 +247,6 @@ let collect_statistics ctx pfilter =
 		Option.may loop (com.get_macros())
 	in
 	loop ctx.com;
-	let l = List.fold_left (fun acc (_,cfi,_,cfo) -> match cfo with
-		| Some cf -> if List.mem_assoc cf.cf_name_pos acc then acc else (cf.cf_name_pos,cfi.cf_name_pos) :: acc
-		| None -> acc
-	) [] ctx.com.display_information.interface_field_implementations in
-	List.iter (fun (p,p') -> add_relation p' (Implemented,p)) l;
 	(* let deal_with_imports paths =
 		let check_subtype m s p =
 			try
@@ -242,8 +306,10 @@ module Printer = struct
 		| SKTypedef _ -> "typedef"
 		| SKAbstract _ -> "abstract"
 		| SKField _ -> "class field"
+		| SKConstructor _ -> "constructor"
 		| SKEnumField _ -> "enum field"
 		| SKVariable _ -> "variable"
+		| SKOther -> "other"
 
 	let print_statistics (kinds,relations) =
 		let files = Hashtbl.create 0 in

+ 16 - 8
src/core/displayTypes.ml

@@ -183,6 +183,7 @@ module DisplayMode = struct
 		| DMUsage of bool (* true = also report definition *)
 		| DMDefinition
 		| DMTypeDefinition
+		| DMImplementation
 		| DMResolve of string
 		| DMPackage
 		| DMHover
@@ -245,6 +246,9 @@ module DisplayMode = struct
 		match dm with
 		| DMNone -> default_compilation_settings
 		| DMDefault | DMDefinition | DMTypeDefinition | DMResolve _ | DMPackage | DMHover | DMSignature -> settings
+		| DMImplementation -> { settings with
+				dms_exit_during_typing = false;
+			}
 		| DMUsage _ -> { settings with
 				dms_full_typing = true;
 				dms_force_macro_typing = true;
@@ -277,6 +281,7 @@ module DisplayMode = struct
 		| DMDefault -> "field"
 		| DMDefinition -> "position"
 		| DMTypeDefinition -> "type-definition"
+		| DMImplementation -> "implementation"
 		| DMResolve s -> "resolve " ^ s
 		| DMPackage -> "package"
 		| DMHover -> "type"
@@ -289,14 +294,17 @@ module DisplayMode = struct
 		| DMSignature -> "signature"
 end
 
-type reference_kind =
-	| KVar
-	| KIdent
-	| KAnyField
-	| KClassField
-	| KEnumField
-	| KModuleType
-	| KConstructor
+type symbol =
+	| SKClass of tclass
+	| SKInterface of tclass
+	| SKEnum of tenum
+	| SKTypedef of tdef
+	| SKAbstract of tabstract
+	| SKField of tclass_field
+	| SKConstructor of tclass_field
+	| SKEnumField of tenum_field
+	| SKVariable of tvar
+	| SKOther
 
 type completion_subject = {
 	s_name : string option;

+ 0 - 4
src/typing/typeloadCheck.ml

@@ -355,10 +355,6 @@ module Inheritance = struct
 					else
 						t2, f2
 				in
-				if ctx.com.display.dms_collect_data then begin
-						let h = ctx.com.display_information in
-						h.interface_field_implementations <- (intf,f,c,Some f2) :: h.interface_field_implementations;
-				end;
 				ignore(follow f2.cf_type); (* force evaluation *)
 				let p = f2.cf_name_pos in
 				let mkind = function

+ 8 - 8
src/typing/typerDisplay.ml

@@ -303,32 +303,32 @@ and display_expr ctx e_ast e dk with_type p =
 	| DMUsage _ ->
 		let rec loop e = match e.eexpr with
 		| TField(_,FEnum(_,ef)) ->
-			Display.ReferencePosition.set (ef.ef_name,ef.ef_name_pos,KEnumField);
+			Display.ReferencePosition.set (ef.ef_name,ef.ef_name_pos,SKEnumField ef);
 		| TField(_,(FAnon cf | FInstance (_,_,cf) | FStatic (_,cf) | FClosure (_,cf))) ->
-			Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,KClassField);
+			Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,SKField cf);
 		| TLocal v | TVar(v,_) ->
-			Display.ReferencePosition.set (v.v_name,v.v_pos,KVar);
+			Display.ReferencePosition.set (v.v_name,v.v_pos,SKVariable v);
 		| TTypeExpr mt ->
 			let ti = t_infos mt in
-			Display.ReferencePosition.set (snd ti.mt_path,ti.mt_name_pos,KModuleType);
+			Display.ReferencePosition.set (snd ti.mt_path,ti.mt_name_pos,symbol_of_module_type mt);
 		| TNew(c,tl,_) ->
 			begin try
 				let _,cf = get_constructor ctx c tl p in
-				Display.ReferencePosition.set (snd c.cl_path,cf.cf_name_pos,KConstructor);
+				Display.ReferencePosition.set (snd c.cl_path,cf.cf_name_pos,SKConstructor cf);
 			with Not_found ->
 				()
 			end
 		| TCall({eexpr = TConst TSuper},_) ->
 			begin try
 				let cf = get_super_constructor() in
-				Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,KClassField);
+				Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,SKField cf);
 			with Not_found ->
 				()
 			end
 		| TConst TSuper ->
 			begin match ctx.curclass.cl_super with
 				| None -> ()
-				| Some (c,_) -> Display.ReferencePosition.set (snd c.cl_path,c.cl_name_pos,KModuleType);
+				| Some (c,_) -> Display.ReferencePosition.set (snd c.cl_path,c.cl_name_pos,SKClass c);
 			end
 		| TCall(e1,_) ->
 			loop e1
@@ -416,7 +416,7 @@ and display_expr ctx e_ast e dk with_type p =
 					raise_toplevel ctx dk with_type (name,p)
 				end
 		end
-	| DMDefault | DMNone | DMModuleSymbols _ | DMDiagnostics _ | DMStatistics ->
+	| DMDefault | DMNone | DMModuleSymbols _ | DMDiagnostics _ | DMStatistics | DMImplementation ->
 		let fields = DisplayFields.collect ctx e_ast e dk with_type p in
 		let item = completion_item_of_expr ctx e in
 		let iterator = try

+ 6 - 1
std/haxe/display/Display.hx

@@ -23,8 +23,8 @@
 package haxe.display;
 
 import haxe.display.JsonModuleTypes;
-import haxe.display.Protocol;
 import haxe.display.Position;
+import haxe.display.Protocol;
 
 /**
 	Methods of the JSON-RPC-based `--display` protocol in Haxe 4.
@@ -53,6 +53,11 @@ class DisplayMethods {
 	**/
 	static inline var GotoDefinition = new HaxeRequestMethod<PositionParams, GotoDefinitionResult>("display/definition");
 
+	/**
+		The goto implementation request is sent from the client to Haxe to resolve the implementation location(s) of a symbol at a given text document position.
+	**/
+	static inline var GotoImplementation = new HaxeRequestMethod<PositionParams, GotoDefinitionResult>("display/implementation");
+
 	/**
 		The goto type definition request is sent from the client to Haxe to resolve the type definition location(s) of a symbol at a given text document position.
 	**/