浏览代码

- collect unresolved identifiers and make suggestions
- print compiler errors as diagnostics

Simon Krajewski 9 年之前
父节点
当前提交
e91c29567f
共有 4 个文件被更改,包括 235 次插入152 次删除
  1. 65 15
      src/display/display.ml
  2. 17 3
      src/typing/common.ml
  3. 13 5
      src/typing/typecore.ml
  4. 140 129
      src/typing/typer.ml

+ 65 - 15
src/display/display.ml

@@ -1,6 +1,7 @@
 open Ast
 open Common
 open Type
+open Typecore
 
 (* order of these variants affects output sorting *)
 type display_field_kind =
@@ -329,35 +330,84 @@ let process_expr com e = match com.display with
 	| _ -> e
 
 let add_import_position com p =
-	com.shared.display_information.import_positions <- PMap.add p (ref false) com.shared.display_information.import_positions
+	com.shared.shared_display_information.import_positions <- PMap.add p (ref false) com.shared.shared_display_information.import_positions
 
 let mark_import_position com p =
 	try
-		let r = PMap.find p com.shared.display_information.import_positions in
+		let r = PMap.find p com.shared.shared_display_information.import_positions in
 		r := true
 	with Not_found ->
 		()
 
-module DiagnosticsKind = struct
-	type t =
-		| DKUnusedImport
-
-	let to_int = function
-		| DKUnusedImport -> 0
-end
-
 module Diagnostics = struct
-	open DiagnosticsKind
+	module DiagnosticsKind = struct
+		type t =
+			| DKUnusedImport
+			| DKUnresolvedIdentifier
+			| DKCompilerError
+
+		let to_int = function
+			| DKUnusedImport -> 0
+			| DKUnresolvedIdentifier -> 1
+			| DKCompilerError -> 2
+	end
 
 	type t = DiagnosticsKind.t * pos
 
+	module UnresolvedIdentifierSuggestion = struct
+		type t =
+			| UISImport
+			| UISTypo
+
+		let to_int = function
+			| UISImport -> 0
+			| UISTypo -> 1
+	end
+
+	open UnresolvedIdentifierSuggestion
+	open DiagnosticsKind
+
 	let print_diagnostics com =
 		let diag = DynArray.create() in
+		let add dk p args =
+			DynArray.add diag (dk,p,args)
+		in
+		begin match !(Common.global_cache) with
+			| None ->
+				()
+			| Some cache ->
+				let find_type i =
+					let types = ref [] in
+					Hashtbl.iter (fun _ m ->
+						List.iter (fun mt ->
+							let tinfos = t_infos mt in
+							if snd tinfos.mt_path = i then
+								types := JObject [
+									"kind",JInt (UnresolvedIdentifierSuggestion.to_int UISImport);
+									"name",JString (s_type_path m.m_path)
+								] :: !types
+						) m.m_types;
+					) cache.c_modules;
+					!types
+				in
+			List.iter (fun (s,p,suggestions) ->
+				let suggestions = List.map (fun (s,_) ->
+					JObject [
+						"kind",JInt (UnresolvedIdentifierSuggestion.to_int UISTypo);
+						"name",JString s
+					]
+				) suggestions in
+				add DKUnresolvedIdentifier p (suggestions @ (find_type s));
+			) com.display_information.unresolved_identifiers;
+		end;
 		PMap.iter (fun p r ->
-			if not !r then DynArray.add diag (DKUnusedImport,p)
-		) com.shared.display_information.import_positions;
-		let jl = DynArray.fold_left (fun acc (dk,p) ->
-			(JObject ["kind",JInt (to_int dk);"range",pos_to_json_range p]) :: acc
+			if not !r then add DKUnusedImport p []
+		) com.shared.shared_display_information.import_positions;
+		List.iter (fun (s,p) ->
+			add DKCompilerError p [JString s]
+		) com.shared.shared_display_information.compiler_errors;
+		let jl = DynArray.fold_left (fun acc (dk,p,args) ->
+			(JObject ["kind",JInt (to_int dk);"range",pos_to_json_range p;"args",JArray args]) :: acc
 		) [] diag in
 		let js = JArray jl in
 		let b = Buffer.create 0 in

+ 17 - 3
src/typing/common.ml

@@ -126,13 +126,18 @@ module IdentifierType = struct
 		| ITPackage s -> s
 end
 
-type display_information = {
+type shared_display_information = {
 	mutable import_positions : (pos,bool ref) PMap.t;
+	mutable compiler_errors : (string * pos) list;
+}
+
+type display_information = {
+	mutable unresolved_identifiers : (string * pos * (string * IdentifierType.t) list) list;
 }
 
 (* This information is shared between normal and macro context. *)
 type shared_context = {
-	display_information : display_information;
+	shared_display_information : shared_display_information;
 }
 
 type context = {
@@ -140,6 +145,7 @@ type context = {
 	version : int;
 	args : string list;
 	shared : shared_context;
+	display_information : display_information;
 	mutable sys_args : string list;
 	mutable display : display_mode;
 	mutable debug : bool;
@@ -707,10 +713,14 @@ let create version s_version args =
 		version = version;
 		args = args;
 		shared = {
-			display_information = {
+			shared_display_information = {
 				import_positions = PMap.empty;
+				compiler_errors = [];
 			}
 		};
+		display_information = {
+			unresolved_identifiers = [];
+		};
 		sys_args = args;
 		debug = false;
 		display = !display_default;
@@ -1114,3 +1124,7 @@ let float_repres f =
 			if f = float_of_string s2 then s2 else
 			Printf.sprintf "%.18g" f
 		in valid_float_lexeme float_val
+
+let add_diagnostics_error com s p =
+	let di = com.shared.shared_display_information in
+	di.compiler_errors <- (s,p) :: di.compiler_errors

+ 13 - 5
src/typing/typecore.ml

@@ -231,7 +231,9 @@ let pass_name = function
 	| PForce -> "force"
 	| PFinal -> "final"
 
-let display_error ctx msg p = ctx.on_error ctx msg p
+let display_error ctx msg p = match ctx.com.display with
+	| DMDiagnostics -> add_diagnostics_error ctx.com msg p
+	| _ -> ctx.on_error ctx msg p
 
 let error msg p = raise (Error (Custom msg,p))
 
@@ -257,10 +259,16 @@ let make_static_call ctx c cf map args t p =
 	let ef = make_static_field_access c cf (map cf.cf_type) p in
 	make_call ctx ef args (map t) p
 
-let raise_or_display ctx l p =
-	if ctx.untyped || ctx.com.display <> DMNone then ()
-	else if ctx.in_call_args then raise (WithTypeError(l,p))
-	else display_error ctx (error_msg (Unify l)) p
+let raise_or_display ctx l p = match ctx.com.display with
+	| DMDiagnostics when ctx.is_display_file ->
+		let di = ctx.com.shared.shared_display_information in
+		di.compiler_errors <- (error_msg (Unify l),p) :: di.compiler_errors
+	| DMNone when ctx.untyped -> ()
+	| DMNone ->
+		if ctx.in_call_args then raise (WithTypeError(l,p))
+		else display_error ctx (error_msg (Unify l)) p
+	| _ ->
+		()
 
 let raise_or_display_message ctx msg p =
 	if ctx.in_call_args then raise (WithTypeError ([Unify_custom msg],p))

+ 140 - 129
src/typing/typer.ml

@@ -371,140 +371,140 @@ module ToplevelCollecter = struct
 	open IdentifierType
 
 	let run ctx =
-	let acc = DynArray.create () in
+		let acc = DynArray.create () in
 
-	(* locals *)
-	PMap.iter (fun _ v ->
-		if not (is_gen_local v) then
+		(* locals *)
+		PMap.iter (fun _ v ->
+			if not (is_gen_local v) then
 				DynArray.add acc (ITLocal v)
-	) ctx.locals;
+		) ctx.locals;
 
-	(* member vars *)
-	if ctx.curfun <> FunStatic then begin
-		let rec loop c =
-			List.iter (fun cf ->
+		(* member vars *)
+		if ctx.curfun <> FunStatic then begin
+			let rec loop c =
+				List.iter (fun cf ->
 					DynArray.add acc (ITMember(ctx.curclass,cf))
-			) c.cl_ordered_fields;
-			match c.cl_super with
-				| None ->
-					()
-				| Some (csup,tl) ->
-					loop csup; (* TODO: type parameters *)
-		in
-		loop ctx.curclass;
-		(* TODO: local using? *)
-	end;
+				) c.cl_ordered_fields;
+				match c.cl_super with
+					| None ->
+						()
+					| Some (csup,tl) ->
+						loop csup; (* TODO: type parameters *)
+			in
+			loop ctx.curclass;
+			(* TODO: local using? *)
+		end;
 
-	(* statics *)
-	List.iter (fun cf ->
+		(* statics *)
+		List.iter (fun cf ->
 			DynArray.add acc (ITStatic(ctx.curclass,cf))
-	) ctx.curclass.cl_ordered_statics;
+		) ctx.curclass.cl_ordered_statics;
 
-	(* enum constructors *)
-	let rec enum_ctors t =
-		match t with
-		| TClassDecl _ | TAbstractDecl _ ->
-			()
-		| TTypeDecl t ->
-			begin match follow t.t_type with
-				| TEnum (e,_) -> enum_ctors (TEnumDecl e)
-				| _ -> ()
-			end
-		| TEnumDecl e ->
-			PMap.iter (fun _ ef ->
+		(* enum constructors *)
+		let rec enum_ctors t =
+			match t with
+			| TClassDecl _ | TAbstractDecl _ ->
+				()
+			| TTypeDecl t ->
+				begin match follow t.t_type with
+					| TEnum (e,_) -> enum_ctors (TEnumDecl e)
+					| _ -> ()
+				end
+			| TEnumDecl e ->
+				PMap.iter (fun _ ef ->
 					DynArray.add acc (ITEnum(e,ef))
-			) e.e_constrs;
-	in
-	List.iter enum_ctors ctx.m.curmod.m_types;
-	List.iter enum_ctors (List.map fst ctx.m.module_types);
+				) e.e_constrs;
+		in
+		List.iter enum_ctors ctx.m.curmod.m_types;
+		List.iter enum_ctors (List.map fst ctx.m.module_types);
 
-	(* imported globals *)
-	PMap.iter (fun _ (mt,s,_) ->
-		try
-			let t = match Typeload.resolve_typedef mt with
-				| TClassDecl c -> (PMap.find s c.cl_statics).cf_type
-				| TEnumDecl en -> (PMap.find s en.e_constrs).ef_type
-				| TAbstractDecl {a_impl = Some c} -> (PMap.find s c.cl_statics).cf_type
-				| _ -> raise Not_found
-			in
+		(* imported globals *)
+		PMap.iter (fun _ (mt,s,_) ->
+			try
+				let t = match Typeload.resolve_typedef mt with
+					| TClassDecl c -> (PMap.find s c.cl_statics).cf_type
+					| TEnumDecl en -> (PMap.find s en.e_constrs).ef_type
+					| TAbstractDecl {a_impl = Some c} -> (PMap.find s c.cl_statics).cf_type
+					| _ -> raise Not_found
+				in
 				DynArray.add acc (ITGlobal(mt,s,t))
-		with Not_found ->
-			()
-	) ctx.m.module_globals;
-
-	let module_types = ref [] in
+			with Not_found ->
+				()
+		) ctx.m.module_globals;
 
-	let add_type mt =
-		match mt with
-		| TClassDecl {cl_kind = KAbstractImpl _} -> ()
-		| _ ->
-			let path = (t_infos mt).mt_path in
-			if not (List.exists (fun mt2 -> (t_infos mt2).mt_path = path) !module_types) then module_types := mt :: !module_types
-	in
+		let module_types = ref [] in
 
-	(* module types *)
-	List.iter add_type ctx.m.curmod.m_types;
+		let add_type mt =
+			match mt with
+			| TClassDecl {cl_kind = KAbstractImpl _} -> ()
+			| _ ->
+				let path = (t_infos mt).mt_path in
+				if not (List.exists (fun mt2 -> (t_infos mt2).mt_path = path) !module_types) then module_types := mt :: !module_types
+		in
 
-	(* module imports *)
-	List.iter add_type (List.map fst ctx.m.module_types);
+		(* module types *)
+		List.iter add_type ctx.m.curmod.m_types;
 
-	(* module using *)
-	List.iter (fun (c,_) ->
-		add_type (TClassDecl c)
-	) ctx.m.module_using;
+		(* module imports *)
+		List.iter add_type (List.map fst ctx.m.module_types);
 
-	(* TODO: wildcard packages. How? *)
+		(* module using *)
+		List.iter (fun (c,_) ->
+			add_type (TClassDecl c)
+		) ctx.m.module_using;
 
-	(* packages and toplevel types *)
-	let class_paths = ctx.com.class_path in
-	let class_paths = List.filter (fun s -> s <> "") class_paths in
+		(* TODO: wildcard packages. How? *)
 
-	let packages = ref [] in
-	let add_package pack =
-		try
-			begin match PMap.find pack ctx.com.package_rules with
-				| Forbidden ->
-					()
-				| _ ->
-					raise Not_found
-			end
-		with Not_found ->
-			if not (List.mem pack !packages) then packages := pack :: !packages
-	in
+		(* packages and toplevel types *)
+		let class_paths = ctx.com.class_path in
+		let class_paths = List.filter (fun s -> s <> "") class_paths in
 
-	List.iter (fun dir ->
-		try
-			let entries = Sys.readdir dir in
-			Array.iter (fun file ->
-				match file with
-					| "." | ".." ->
+		let packages = ref [] in
+		let add_package pack =
+			try
+				begin match PMap.find pack ctx.com.package_rules with
+					| Forbidden ->
 						()
-					| _ when Sys.is_directory (dir ^ file) && file.[0] >= 'a' && file.[0] <= 'z' ->
-						add_package file
 					| _ ->
-						let l = String.length file in
-						if l > 3 && String.sub file (l - 3) 3 = ".hx" then begin
-							try
-								let name = String.sub file 0 (l - 3) in
-								let md = Typeload.load_module ctx ([],name) Ast.null_pos in
-								List.iter (fun mt ->
-									if (t_infos mt).mt_path = md.m_path then add_type mt
-								) md.m_types
-							with _ ->
-								()
-						end
-			) entries;
-		with Sys_error _ ->
-			()
-	) class_paths;
+						raise Not_found
+				end
+			with Not_found ->
+				if not (List.mem pack !packages) then packages := pack :: !packages
+		in
+
+		List.iter (fun dir ->
+			try
+				let entries = Sys.readdir dir in
+				Array.iter (fun file ->
+					match file with
+						| "." | ".." ->
+							()
+						| _ when Sys.is_directory (dir ^ file) && file.[0] >= 'a' && file.[0] <= 'z' ->
+							add_package file
+						| _ ->
+							let l = String.length file in
+							if l > 3 && String.sub file (l - 3) 3 = ".hx" then begin
+								try
+									let name = String.sub file 0 (l - 3) in
+									let md = Typeload.load_module ctx ([],name) Ast.null_pos in
+									List.iter (fun mt ->
+										if (t_infos mt).mt_path = md.m_path then add_type mt
+									) md.m_types
+								with _ ->
+									()
+							end
+				) entries;
+			with Sys_error _ ->
+				()
+		) class_paths;
 
-	List.iter (fun pack ->
+		List.iter (fun pack ->
 			DynArray.add acc (ITPackage pack)
-	) !packages;
+		) !packages;
 
-	List.iter (fun mt ->
+		List.iter (fun mt ->
 			DynArray.add acc (ITType mt)
-	) !module_types;
+		) !module_types;
 		DynArray.to_list acc
 end
 
@@ -2602,25 +2602,36 @@ and type_ident ctx i p mode =
 			if ctx.curfun = FunStatic && PMap.mem i ctx.curclass.cl_fields then error ("Cannot access " ^ i ^ " in static function") p;
 			let err = Unknown_ident i in
 			if ctx.in_display then raise (Error (err,p));
-			if ctx.com.display <> DMNone then begin
-				display_error ctx (error_msg err) p;
-				let t = mk_mono() in
-				AKExpr (mk (TLocal (add_local ctx i t p)) t p)
-			end else begin
-				let e = try
-					let t = List.find (fun (i2,_) -> i2 = i) ctx.type_params in
-					let c = match follow (snd t) with TInst(c,_) -> c | _ -> assert false in
-					if Typeload.is_generic_parameter ctx c && Meta.has Meta.Const c.cl_meta then
-						AKExpr (type_module_type ctx (TClassDecl c) None p)
-					else begin
-						display_error ctx ("Type parameter " ^ i ^ " is only available at compilation and is not a runtime value") p;
-						AKExpr (mk (TConst TNull) t_dynamic p)
-					end
-				with Not_found ->
-					raise (Error(err,p))
-				in
-				e
-			end
+			match ctx.com.display with
+				| DMNone ->
+					let e = try
+						let t = List.find (fun (i2,_) -> i2 = i) ctx.type_params in
+						let c = match follow (snd t) with TInst(c,_) -> c | _ -> assert false in
+						if Typeload.is_generic_parameter ctx c && Meta.has Meta.Const c.cl_meta then
+							AKExpr (type_module_type ctx (TClassDecl c) None p)
+						else begin
+							display_error ctx ("Type parameter " ^ i ^ " is only available at compilation and is not a runtime value") p;
+							AKExpr (mk (TConst TNull) t_dynamic p)
+						end
+					with Not_found ->
+						raise (Error(err,p))
+					in
+					e
+				| DMDiagnostics when ctx.is_display_file ->
+					let l = ToplevelCollecter.run ctx in
+					let cl = List.map (fun it ->
+						let s = IdentifierType.get_name it in
+						(s,it),StringError.levenshtein i s
+					) l in
+					let cl = List.sort (fun (_,c1) (_,c2) -> compare c1 c2) cl in
+					let cl = StringError.filter_similar (fun (s,_) r -> r <= (min (String.length s) (String.length i)) / 3) cl in
+					ctx.com.display_information.unresolved_identifiers <- (i,p,cl) :: ctx.com.display_information.unresolved_identifiers;
+					let t = mk_mono() in
+					AKExpr (mk (TLocal (add_local ctx i t p)) t p)
+				| _ ->
+					display_error ctx (error_msg err) p;
+					let t = mk_mono() in
+					AKExpr (mk (TLocal (add_local ctx i t p)) t p)
 		end
 
 (* MORDOR *)