displayToplevel.ml 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. (*
  2. The Haxe Compiler
  3. Copyright (C) 2005-2019 Haxe Foundation
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. *)
  16. open Ast
  17. open Common
  18. open CompilationServer
  19. open Type
  20. open Typecore
  21. open CompletionItem
  22. open ClassFieldOrigin
  23. open DisplayTypes
  24. open Genjson
  25. open Globals
  26. let maybe_resolve_macro_field ctx t c cf =
  27. try
  28. if cf.cf_kind <> Method MethMacro then raise Exit;
  29. let (tl,tr,c,cf) = ctx.g.do_load_macro ctx false c.cl_path cf.cf_name null_pos in
  30. (TFun(tl,tr)),c,cf
  31. with _ ->
  32. t,c,cf
  33. let exclude : string list ref = ref []
  34. class explore_class_path_task cs com recursive f_pack f_module dir pack = object(self)
  35. inherit server_task ["explore";dir] 50
  36. val platform_str = platform_name_macro com
  37. method private execute : unit =
  38. let dot_path = (String.concat "." (List.rev pack)) in
  39. if (List.mem dot_path !exclude) then
  40. ()
  41. else try
  42. let entries = Sys.readdir dir in
  43. Array.iter (fun file ->
  44. match file with
  45. | "." | ".." ->
  46. ()
  47. | _ when Sys.is_directory (dir ^ file) && file.[0] >= 'a' && file.[0] <= 'z' ->
  48. begin try
  49. begin match PMap.find file com.package_rules with
  50. | Forbidden | Remap _ -> ()
  51. | _ -> raise Not_found
  52. end
  53. with Not_found ->
  54. f_pack (List.rev pack,file);
  55. if recursive then begin
  56. let task = new explore_class_path_task cs com recursive f_pack f_module (dir ^ file ^ "/") (file :: pack) in
  57. begin match cs with
  58. | None -> task#run
  59. | Some cs' -> cs'#add_task task
  60. end
  61. end
  62. end
  63. | _ ->
  64. let l = String.length file in
  65. if l > 3 && String.sub file (l - 3) 3 = ".hx" then begin
  66. try
  67. let name =
  68. let name = String.sub file 0 (l - 3) in
  69. try
  70. let dot_pos = String.rindex name '.' in
  71. if platform_str = String.sub file dot_pos (String.length name - dot_pos) then
  72. String.sub file 0 dot_pos
  73. else
  74. raise Exit
  75. with Not_found -> name
  76. in
  77. let path = (List.rev pack,name) in
  78. let dot_path = if dot_path = "" then name else dot_path ^ "." ^ name in
  79. if (List.mem dot_path !exclude) then () else f_module (dir ^ file) path;
  80. with _ ->
  81. ()
  82. end
  83. ) entries;
  84. with Sys_error _ ->
  85. ()
  86. end
  87. let explore_class_paths com timer class_paths recursive f_pack f_module =
  88. let cs = CompilationServer.get() in
  89. let t = Timer.timer (timer @ ["class path exploration"]) in
  90. let tasks = List.map (fun dir ->
  91. new explore_class_path_task cs com recursive f_pack f_module dir []
  92. ) class_paths in
  93. begin match cs with
  94. | None -> List.iter (fun task -> task#run) tasks
  95. | Some cs -> List.iter (fun task -> cs#add_task task) tasks
  96. end;
  97. t()
  98. let read_class_paths com timer =
  99. explore_class_paths com timer (List.filter ((<>) "") com.class_path) true (fun _ -> ()) (fun file path ->
  100. (* Don't parse the display file as that would maybe overwrite the content from stdin with the file contents. *)
  101. if not (DisplayPosition.display_position#is_in_file file) then begin
  102. let file,_,pack,_ = Display.parse_module' com path Globals.null_pos in
  103. match CompilationServer.get() with
  104. | Some cs when pack <> fst path ->
  105. let file_key = Path.UniqueKey.create file in
  106. (CommonCache.get_cache cs com)#remove_file_for_real file_key
  107. | _ ->
  108. ()
  109. end
  110. )
  111. let init_or_update_server cs com timer_name =
  112. let cc = CommonCache.get_cache cs com in
  113. if not cc#is_initialized then begin
  114. cc#set_initialized true;
  115. read_class_paths com timer_name
  116. end;
  117. (* Force executing all "explore" tasks here because we need their information. *)
  118. cs#run_tasks true (fun task -> match task#get_id with
  119. | "explore" :: _ -> true
  120. | _ -> false
  121. );
  122. (* Iterate all removed files of the current context. If they aren't part of the context again,
  123. re-parse them and remove them from c_removed_files. *)
  124. let removed_files = cc#get_removed_files in
  125. let removed_removed_files = DynArray.create () in
  126. Hashtbl.iter (fun file_key file_path ->
  127. DynArray.add removed_removed_files file_key;
  128. try
  129. ignore(cc#find_file file_key);
  130. with Not_found ->
  131. try ignore(TypeloadParse.parse_module_file com file_path null_pos) with _ -> ()
  132. ) removed_files;
  133. DynArray.iter (Hashtbl.remove removed_files) removed_removed_files
  134. module CollectionContext = struct
  135. open ImportStatus
  136. type t = {
  137. ctx : typer;
  138. items : CompletionItem.t DynArray.t;
  139. names : (string,CompletionItem.t) Hashtbl.t;
  140. paths : (Globals.path,bool) Hashtbl.t;
  141. }
  142. let create ctx = {
  143. ctx = ctx;
  144. items = DynArray.create ();
  145. names = Hashtbl.create 0;
  146. paths = Hashtbl.create 0;
  147. }
  148. let add_item ctx item name =
  149. DynArray.add ctx.items item;
  150. match name with
  151. | None -> ()
  152. | Some name -> Hashtbl.replace ctx.names name item
  153. let get_import_status ctx is_import path =
  154. try
  155. let _ = Hashtbl.find ctx.names (snd path) in
  156. (* TODO: do we have to check if we get the same thing? *)
  157. Shadowed
  158. with Not_found ->
  159. let check_wildcard () =
  160. List.exists (fun (sl,_) -> (sl,snd path) = path) ctx.ctx.m.wildcard_packages
  161. in
  162. if is_import || (fst path = []) || check_wildcard () then Imported else Unimported
  163. let is_qualified ctx name =
  164. not (Hashtbl.mem ctx.names name)
  165. let path_exists ctx path = Hashtbl.mem ctx.paths path
  166. let add_path ctx path = Hashtbl.add ctx.paths path true
  167. end
  168. open CollectionContext
  169. (* +1 for each matching package part. 0 = no common part *)
  170. let pack_similarity pack1 pack2 =
  171. let rec loop count pack1 pack2 = match pack1,pack2 with
  172. | [],[] -> count
  173. | (s1 :: pack1),(s2 :: pack2) when s1 = s2 -> loop (count + 1) pack1 pack2
  174. | _ -> count
  175. in
  176. loop 0 pack1 pack2
  177. (* Returns `true` if `pack1` contains or is `pack2` *)
  178. let pack_contains pack1 pack2 =
  179. let rec loop pack1 pack2 = match pack1,pack2 with
  180. | [],_ -> true
  181. | (s1 :: pack1),(s2 :: pack2) when s1 = s2 -> loop pack1 pack2
  182. | _ -> false
  183. in
  184. loop pack1 pack2
  185. let is_pack_visible pack =
  186. not (List.exists (fun s -> String.length s > 0 && s.[0] = '_') pack)
  187. let collect ctx tk with_type sort =
  188. let t = Timer.timer ["display";"toplevel"] in
  189. let cctx = CollectionContext.create ctx in
  190. let curpack = fst ctx.curclass.cl_path in
  191. (* Note: This checks for the explicit `ServerConfig.legacy_completion` setting instead of using
  192. `is_legacy_completion com` because the latter is always false for the old protocol, yet we have
  193. tests which assume advanced completion even in the old protocol. This means that we can only
  194. use "legacy mode" in the new protocol. *)
  195. let is_legacy_completion = !ServerConfig.legacy_completion in
  196. let packages = Hashtbl.create 0 in
  197. let add_package path = Hashtbl.replace packages path true in
  198. let add item name = add_item cctx item name in
  199. let add_type mt =
  200. match mt with
  201. | TClassDecl {cl_kind = KAbstractImpl _ | KModuleFields _} -> ()
  202. | _ ->
  203. let path = (t_infos mt).mt_path in
  204. let mname = snd (t_infos mt).mt_module.m_path in
  205. let path = if snd path = mname then path else (fst path @ [mname],snd path) in
  206. if not (path_exists cctx path) then begin
  207. Display.merge_core_doc ctx mt;
  208. let is = get_import_status cctx true path in
  209. if not (Meta.has Meta.NoCompletion (t_infos mt).mt_meta) then begin
  210. add (make_ci_type (CompletionModuleType.of_module_type mt) is None) (Some (snd path));
  211. add_path cctx path;
  212. end
  213. end
  214. in
  215. let process_decls pack name decls =
  216. let added_something = ref false in
  217. let add item name =
  218. added_something := true;
  219. add item name
  220. in
  221. let run () = List.iter (fun (d,p) ->
  222. begin try
  223. let tname,is_private,meta = match d with
  224. | EClass d -> fst d.d_name,List.mem HPrivate d.d_flags,d.d_meta
  225. | EEnum d -> fst d.d_name,List.mem EPrivate d.d_flags,d.d_meta
  226. | ETypedef d -> fst d.d_name,List.mem EPrivate d.d_flags,d.d_meta
  227. | EAbstract d -> fst d.d_name,List.mem AbPrivate d.d_flags,d.d_meta
  228. | EStatic d -> fst d.d_name,List.exists (fun (a,_) -> a = APrivate) d.d_flags,d.d_meta
  229. | EImport _ | EUsing _ -> raise Exit
  230. in
  231. let path = Path.full_dot_path pack name tname in
  232. if not (path_exists cctx path) && not is_private && not (Meta.has Meta.NoCompletion meta) then begin
  233. add_path cctx path;
  234. (* If we share a package, the module's main type shadows everything with the same name. *)
  235. let shadowing_name = if pack_contains pack curpack && tname = name then (Some name) else None in
  236. (* Also, this means we can access it as if it was imported (assuming it's not shadowed by something else. *)
  237. let is = get_import_status cctx (shadowing_name <> None) path in
  238. add (make_ci_type (CompletionModuleType.of_type_decl pack name (d,p)) is None) shadowing_name
  239. end
  240. with Exit ->
  241. ()
  242. end
  243. ) decls in
  244. if is_pack_visible pack then run();
  245. !added_something
  246. in
  247. (* Collection starts here *)
  248. let tpair ?(values=PMap.empty) t =
  249. let ct = CompletionType.from_type (Display.get_import_status ctx) ~values t in
  250. (t,ct)
  251. in
  252. begin match tk with
  253. | TKType | TKOverride -> ()
  254. | TKExpr p | TKPattern p | TKField p ->
  255. (* locals *)
  256. PMap.iter (fun _ v ->
  257. if not (is_gen_local v) then
  258. add (make_ci_local v (tpair ~values:(get_value_meta (get_var_meta v)) v.v_type)) (Some v.v_name)
  259. ) ctx.locals;
  260. let add_field scope origin cf =
  261. let origin,cf = match origin with
  262. | Self (TClassDecl c) ->
  263. let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in
  264. Self (TClassDecl c),cf
  265. | StaticImport (TClassDecl c) ->
  266. let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in
  267. StaticImport (TClassDecl c),cf
  268. | Parent (TClassDecl c) ->
  269. let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in
  270. Parent (TClassDecl c),cf
  271. | StaticExtension (TClassDecl c) ->
  272. let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in
  273. StaticExtension (TClassDecl c),cf
  274. | _ ->
  275. origin,cf
  276. in
  277. let is_qualified = is_qualified cctx cf.cf_name in
  278. add (make_ci_class_field (CompletionClassField.make cf scope origin is_qualified) (tpair ~values:(get_value_meta cf.cf_meta) cf.cf_type)) (Some cf.cf_name)
  279. in
  280. let maybe_add_field scope origin cf =
  281. if not (Meta.has Meta.NoCompletion cf.cf_meta) then add_field scope origin cf
  282. in
  283. (* member fields *)
  284. if ctx.curfun <> FunStatic then begin
  285. let all_fields = Type.TClass.get_all_fields ctx.curclass (List.map snd ctx.curclass.cl_params) in
  286. PMap.iter (fun _ (c,cf) ->
  287. let origin = if c == ctx.curclass then Self (TClassDecl c) else Parent (TClassDecl c) in
  288. maybe_add_field CFSMember origin cf
  289. ) all_fields;
  290. (* TODO: local using? *)
  291. end;
  292. (* statics *)
  293. begin match ctx.curclass.cl_kind with
  294. | KAbstractImpl ({a_impl = Some c} as a) ->
  295. let origin = Self (TAbstractDecl a) in
  296. List.iter (fun cf ->
  297. if Meta.has Meta.Impl cf.cf_meta then begin
  298. if ctx.curfun = FunStatic then ()
  299. else begin
  300. let cf = prepare_using_field cf in
  301. maybe_add_field CFSMember origin cf
  302. end
  303. end else
  304. maybe_add_field CFSStatic origin cf
  305. ) c.cl_ordered_statics
  306. | _ ->
  307. List.iter (maybe_add_field CFSStatic (Self (TClassDecl ctx.curclass))) ctx.curclass.cl_ordered_statics
  308. end;
  309. (* enum constructors *)
  310. let rec enum_ctors t =
  311. match t with
  312. | TAbstractDecl ({a_impl = Some c} as a) when Meta.has Meta.Enum a.a_meta && not (path_exists cctx a.a_path) && ctx.curclass != c ->
  313. add_path cctx a.a_path;
  314. List.iter (fun cf ->
  315. let ccf = CompletionClassField.make cf CFSMember (Self (decl_of_class c)) true in
  316. if (Meta.has Meta.Enum cf.cf_meta) && not (Meta.has Meta.NoCompletion cf.cf_meta) then
  317. add (make_ci_enum_abstract_field a ccf (tpair cf.cf_type)) (Some cf.cf_name);
  318. ) c.cl_ordered_statics
  319. | TTypeDecl t ->
  320. begin match follow t.t_type with
  321. | TEnum (e,_) -> enum_ctors (TEnumDecl e)
  322. | _ -> ()
  323. end
  324. | TEnumDecl e when not (path_exists cctx e.e_path) ->
  325. add_path cctx e.e_path;
  326. let origin = Self (TEnumDecl e) in
  327. PMap.iter (fun _ ef ->
  328. let is_qualified = is_qualified cctx ef.ef_name in
  329. add (make_ci_enum_field (CompletionEnumField.make ef origin is_qualified) (tpair ef.ef_type)) (Some ef.ef_name)
  330. ) e.e_constrs;
  331. | _ ->
  332. ()
  333. in
  334. List.iter enum_ctors ctx.m.curmod.m_types;
  335. List.iter enum_ctors (List.map fst ctx.m.module_types);
  336. (* enum constructors of expected type *)
  337. begin match with_type with
  338. | WithType.WithType(t,_) ->
  339. (try enum_ctors (module_type_of_type (follow t)) with Exit -> ())
  340. | _ -> ()
  341. end;
  342. (* imported globals *)
  343. PMap.iter (fun name (mt,s,_) ->
  344. try
  345. let is_qualified = is_qualified cctx name in
  346. let class_import c =
  347. let cf = PMap.find s c.cl_statics in
  348. let cf = if name = cf.cf_name then cf else {cf with cf_name = name} in
  349. let decl,make = match c.cl_kind with
  350. | KAbstractImpl a -> TAbstractDecl a,
  351. if Meta.has Meta.Enum cf.cf_meta then make_ci_enum_abstract_field a else make_ci_class_field
  352. | _ -> TClassDecl c,make_ci_class_field
  353. in
  354. let origin = StaticImport decl in
  355. add (make (CompletionClassField.make cf CFSStatic origin is_qualified) (tpair ~values:(get_value_meta cf.cf_meta) cf.cf_type)) (Some name)
  356. in
  357. match resolve_typedef mt with
  358. | TClassDecl c -> class_import c;
  359. | TEnumDecl en ->
  360. let ef = PMap.find s en.e_constrs in
  361. let ef = if name = ef.ef_name then ef else {ef with ef_name = name} in
  362. let origin = StaticImport (TEnumDecl en) in
  363. add (make_ci_enum_field (CompletionEnumField.make ef origin is_qualified) (tpair ef.ef_type)) (Some s)
  364. | TAbstractDecl {a_impl = Some c} -> class_import c;
  365. | _ -> raise Not_found
  366. with Not_found ->
  367. ()
  368. ) ctx.m.module_globals;
  369. (* literals *)
  370. add (make_ci_literal "null" (tpair t_dynamic)) (Some "null");
  371. add (make_ci_literal "true" (tpair ctx.com.basic.tbool)) (Some "true");
  372. add (make_ci_literal "false" (tpair ctx.com.basic.tbool)) (Some "false");
  373. begin match ctx.curfun with
  374. | FunMember | FunConstructor | FunMemberClassLocal ->
  375. let t = TInst(ctx.curclass,List.map snd ctx.curclass.cl_params) in
  376. add (make_ci_literal "this" (tpair t)) (Some "this");
  377. begin match ctx.curclass.cl_super with
  378. | Some(c,tl) -> add (make_ci_literal "super" (tpair (TInst(c,tl)))) (Some "super")
  379. | None -> ()
  380. end
  381. | _ ->
  382. ()
  383. end;
  384. if not is_legacy_completion then begin
  385. (* keywords *)
  386. let kwds = [
  387. Function; Var; Final; If; Else; While; Do; For; Break; Return; Continue; Switch;
  388. Try; New; Throw; Untyped; Cast; Inline;
  389. ] in
  390. List.iter (fun kwd -> add(make_ci_keyword kwd) (Some (s_keyword kwd))) kwds;
  391. (* builtins *)
  392. add (make_ci_literal "trace" (tpair (TFun(["value",false,t_dynamic],ctx.com.basic.tvoid)))) (Some "trace")
  393. end
  394. end;
  395. (* type params *)
  396. List.iter (fun (s,t) -> match follow t with
  397. | TInst(c,_) ->
  398. add (make_ci_type_param c (tpair t)) (Some (snd c.cl_path))
  399. | _ -> die "" __LOC__
  400. ) ctx.type_params;
  401. (* module types *)
  402. List.iter add_type ctx.m.curmod.m_types;
  403. (* module imports *)
  404. List.iter add_type (List.rev_map fst ctx.m.module_types); (* reverse! *)
  405. (* types from files *)
  406. begin match !CompilationServer.instance with
  407. | None ->
  408. (* offline: explore class paths *)
  409. let class_paths = ctx.com.class_path in
  410. let class_paths = List.filter (fun s -> s <> "") class_paths in
  411. explore_class_paths ctx.com ["display";"toplevel"] class_paths true add_package (fun file path ->
  412. if not (path_exists cctx path) then begin
  413. let _,decls = Display.parse_module ctx path Globals.null_pos in
  414. ignore(process_decls (fst path) (snd path) decls)
  415. end
  416. )
  417. | Some cs ->
  418. (* online: iter context files *)
  419. init_or_update_server cs ctx.com ["display";"toplevel"];
  420. let cc = CommonCache.get_cache cs ctx.com in
  421. let files = cc#get_files in
  422. (* Sort files by reverse distance of their package to our current package. *)
  423. let files = Hashtbl.fold (fun file cfile acc ->
  424. let i = pack_similarity curpack cfile.c_package in
  425. ((file,cfile),i) :: acc
  426. ) files [] in
  427. let files = List.sort (fun (_,i1) (_,i2) -> -compare i1 i2) files in
  428. let check_package pack = match List.rev pack with
  429. | [] -> ()
  430. | s :: sl -> add_package (List.rev sl,s)
  431. in
  432. List.iter (fun ((file_key,cfile),_) ->
  433. let module_name = CompilationServer.get_module_name_of_cfile cfile.c_file_path cfile in
  434. let dot_path = s_type_path (cfile.c_package,module_name) in
  435. (* In legacy mode we only show toplevel types. *)
  436. if is_legacy_completion && cfile.c_package <> [] then begin
  437. (* And only toplevel packages. *)
  438. match cfile.c_package with
  439. | [s] -> add_package ([],s)
  440. | _ -> ()
  441. end else if (List.exists (fun e -> ExtString.String.starts_with dot_path (e ^ ".")) !exclude) then
  442. ()
  443. else begin
  444. Hashtbl.replace ctx.com.module_to_file (cfile.c_package,module_name) cfile.c_file_path;
  445. if process_decls cfile.c_package module_name cfile.c_decls then check_package cfile.c_package;
  446. end
  447. ) files;
  448. List.iter (fun file ->
  449. match cs#get_native_lib file with
  450. | Some lib ->
  451. Hashtbl.iter (fun path (pack,decls) ->
  452. if process_decls pack (snd path) decls then check_package pack;
  453. ) lib.c_nl_files
  454. | None ->
  455. ()
  456. ) ctx.com.native_libs.all_libs
  457. end;
  458. (* packages *)
  459. Hashtbl.iter (fun path _ ->
  460. let full_pack = fst path @ [snd path] in
  461. if is_pack_visible full_pack then add (make_ci_package path []) (Some (snd path))
  462. ) packages;
  463. (* sorting *)
  464. let l = DynArray.to_list cctx.items in
  465. let l = if is_legacy_completion then
  466. List.sort (fun item1 item2 -> compare (get_name item1) (get_name item2)) l
  467. else if sort then
  468. Display.sort_fields l with_type tk
  469. else
  470. l
  471. in
  472. t();
  473. l
  474. let collect_and_raise ctx tk with_type cr (name,pname) pinsert =
  475. let fields = match !DisplayException.last_completion_pos with
  476. | Some p' when pname.pmin = p'.pmin ->
  477. Array.to_list (!DisplayException.last_completion_result)
  478. | _ ->
  479. collect ctx tk with_type (name = "")
  480. in
  481. DisplayException.raise_fields fields cr (make_subject (Some name) ~start_pos:(Some pname) pinsert)
  482. let handle_unresolved_identifier ctx i p only_types =
  483. let l = collect ctx (if only_types then TKType else TKExpr p) NoValue false in
  484. let cl = List.map (fun it ->
  485. let s = CompletionItem.get_name it in
  486. let i = StringError.levenshtein i s in
  487. (s,it,i),i
  488. ) l in
  489. let cl = List.sort (fun (_,c1) (_,c2) -> compare c1 c2) cl in
  490. let cl = StringError.filter_similar (fun (s,_,_) r -> r <= (min (String.length s) (String.length i)) / 3) cl in
  491. ctx.com.display_information.unresolved_identifiers <- (i,p,cl) :: ctx.com.display_information.unresolved_identifiers