Flags for direct decl parsing without using disk cache
[hiphop-php.git] / hphp / hack / src / providers / direct_decl_utils.ml
blobdb6d6a7c56da222c73f7c56d9dfd91f1fd80d4cc
1 (*
2 * Copyright (c) Facebook, Inc. and its affiliates.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the "hack" directory of this source tree.
7 *)
9 open Hh_prelude
11 type parsed_file_with_hashes = Direct_decl_parser.parsed_file_with_hashes = {
12 pfh_mode: FileInfo.mode option;
13 pfh_hash: Int64.t;
14 pfh_decls: (string * Shallow_decl_defs.decl * Int64.t) list;
17 (* If any decls in the list have the same name, retain only the first
18 declaration of each symbol in the sequence. *)
19 let dedup_decls decls =
20 let open Shallow_decl_defs in
21 let seen_types = String.Table.create () in
22 let seen_funs = String.Table.create () in
23 let seen_consts = String.Table.create () in
24 let seen_modules = String.Table.create () in
25 Sequence.filter decls ~f:(fun decl ->
26 match decl with
27 | (name, Class _)
28 | (name, Typedef _) ->
29 if String.Table.mem seen_types name then
30 false
31 else
32 let () = String.Table.add_exn seen_types ~key:name ~data:() in
33 true
34 | (name, Fun _) ->
35 if String.Table.mem seen_funs name then
36 false
37 else
38 let () = String.Table.add_exn seen_funs ~key:name ~data:() in
39 true
40 | (name, Const _) ->
41 if String.Table.mem seen_consts name then
42 false
43 else
44 let () = String.Table.add_exn seen_consts ~key:name ~data:() in
45 true
46 | (name, Module _) ->
47 if String.Table.mem seen_modules name then
48 false
49 else
50 let () = String.Table.add_exn seen_modules ~key:name ~data:() in
51 true)
53 (* If a symbol was also declared in another file, and that file was determined
54 to be the winner in the naming table, remove its decl from the list.
56 Do not remove decls if there is no entry for them in the naming table. This
57 ensures that this path can populate the decl heap during full inits. This may
58 result in the decl heap containing an incorrect decl in the event of a naming
59 conflict, which might be confusing. But the user will be obligated to fix the
60 naming conflict, and this behavior is limited to full inits, so maybe we can
61 live with it. *)
62 let remove_naming_conflict_losers ctx file decls =
63 let open Shallow_decl_defs in
64 Sequence.filter decls ~f:(fun decl ->
65 match decl with
66 | (name, Class _)
67 | (name, Typedef _) ->
68 (match Naming_provider.get_type_path ctx name with
69 | Some nfile -> Relative_path.equal nfile file
70 | None -> true)
71 | (name, Fun _) ->
72 (match Naming_provider.get_fun_path ctx name with
73 | Some nfile -> Relative_path.equal nfile file
74 | None -> true)
75 | (name, Const _) ->
76 (match Naming_provider.get_const_path ctx name with
77 | Some nfile -> Relative_path.equal nfile file
78 | None -> true)
79 | (name, Module _) ->
80 (match Naming_provider.get_module_path ctx name with
81 | Some nfile -> Relative_path.equal nfile file
82 | None -> true))
84 let cache_decls ctx file decls =
85 let open Shallow_decl_defs in
86 let open Typing_defs in
87 let decls =
88 decls
89 |> List.rev_map (* direct decl parser produces reverse of syntactic order *)
90 ~f:(fun (name, decl, _hash) -> (name, decl))
91 |> Sequence.of_list
92 |> dedup_decls
93 |> remove_naming_conflict_losers ctx file
94 |> Sequence.to_list
96 match Provider_context.get_backend ctx with
97 | Provider_backend.Pessimised_shared_memory _ ->
98 (* We must never perform caching here. Otherwise, we may overwrite earlier
99 pessimisation results with unpessimised types *)
100 failwith "invalid"
101 | Provider_backend.Analysis
102 | Provider_backend.Shared_memory ->
103 List.iter decls ~f:(function
104 | (name, Class decl) ->
105 Shallow_classes_heap.Classes.add name decl;
107 TypecheckerOptions.shallow_class_decl
108 (Provider_context.get_tcopt ctx)
109 then
110 Shallow_classes_heap.MemberFilters.add decl
111 | (name, Fun decl) -> Decl_store.((get ()).add_fun name decl)
112 | (name, Typedef decl) -> Decl_store.((get ()).add_typedef name decl)
113 | (name, Const decl) -> Decl_store.((get ()).add_gconst name decl)
114 | (name, Module decl) -> Decl_store.((get ()).add_module name decl))
115 | Provider_backend.Rust_provider_backend backend ->
116 Rust_provider_backend.Decl.add_shallow_decls backend decls
117 | Provider_backend.(Local_memory { decl_cache; shallow_decl_cache; _ }) ->
118 List.iter decls ~f:(function
119 | (name, Class decl) ->
120 let (_ : shallow_class option) =
121 Provider_backend.Shallow_decl_cache.find_or_add
122 shallow_decl_cache
123 ~key:
124 (Provider_backend.Shallow_decl_cache_entry.Shallow_class_decl
125 name)
126 ~default:(fun () -> Some decl)
129 | (name, Fun decl) ->
130 let (_ : fun_elt option) =
131 Provider_backend.Decl_cache.find_or_add
132 decl_cache
133 ~key:(Provider_backend.Decl_cache_entry.Fun_decl name)
134 ~default:(fun () -> Some decl)
137 | (name, Typedef decl) ->
138 let (_ : typedef_type option) =
139 Provider_backend.Decl_cache.find_or_add
140 decl_cache
141 ~key:(Provider_backend.Decl_cache_entry.Typedef_decl name)
142 ~default:(fun () -> Some decl)
145 | (name, Const decl) ->
146 let (_ : const_decl option) =
147 Provider_backend.Decl_cache.find_or_add
148 decl_cache
149 ~key:(Provider_backend.Decl_cache_entry.Gconst_decl name)
150 ~default:(fun () -> Some decl)
153 | (name, Module decl) ->
154 let (_ : module_decl option) =
155 Provider_backend.Decl_cache.find_or_add
156 decl_cache
157 ~key:(Provider_backend.Decl_cache_entry.Module_decl name)
158 ~default:(fun () -> Some decl)
161 | Provider_backend.Decl_service _ ->
162 failwith
163 "Direct_decl_utils.cache_file_decls not implemented for Decl_service"
165 let get_file_contents ~ignore_file_content_caches ctx filename =
166 let from_entries =
167 if ignore_file_content_caches then
168 None
169 else
170 Relative_path.Map.find_opt (Provider_context.get_entries ctx) filename
172 match from_entries with
173 | Some entry ->
174 let source_text = Ast_provider.compute_source_text ~entry in
175 Some (Full_fidelity_source_text.text source_text)
176 | None ->
177 let write_to_cache =
178 (not ignore_file_content_caches)
179 && TypecheckerOptions.enable_disk_heap (Provider_context.get_tcopt ctx)
181 File_provider.get_contents
182 ~force_read_disk:ignore_file_content_caches
183 ~writeback_disk_contents_in_shmem_provider:write_to_cache
184 filename
186 let direct_decl_parse ?(ignore_file_content_caches = false) ctx file =
187 Counters.count Counters.Category.Get_decl @@ fun () ->
188 match get_file_contents ~ignore_file_content_caches ctx file with
189 | None -> None
190 | Some contents ->
191 let popt = Provider_context.get_popt ctx in
192 let opts = DeclParserOptions.from_parser_options popt in
193 let parsed_file =
194 Direct_decl_parser.parse_and_hash_decls opts file contents
196 let deregister_php_stdlib =
197 Relative_path.is_hhi (Relative_path.prefix file)
198 && ParserOptions.deregister_php_stdlib popt
200 let is_stdlib_fun fun_decl = fun_decl.Typing_defs.fe_php_std_lib in
201 let is_stdlib_class c =
202 List.exists c.Shallow_decl_defs.sc_user_attributes ~f:(fun a ->
203 String.equal
204 Naming_special_names.UserAttributes.uaPHPStdLib
205 (snd a.Typing_defs_core.ua_name))
207 let parsed_file =
208 if not deregister_php_stdlib then
209 parsed_file
210 else
211 let open Shallow_decl_defs in
212 let decls =
213 List.filter_map parsed_file.pfh_decls ~f:(function
214 | (_, Fun f, _) when is_stdlib_fun f -> None
215 | (_, Class c, _) when is_stdlib_class c -> None
216 | (name, Class c, hash) ->
217 let keep_prop sp = not (sp_php_std_lib sp) in
218 let keep_meth sm = not (sm_php_std_lib sm) in
219 let c =
221 c with
222 sc_props = List.filter c.sc_props ~f:keep_prop;
223 sc_sprops = List.filter c.sc_sprops ~f:keep_prop;
224 sc_methods = List.filter c.sc_methods ~f:keep_meth;
225 sc_static_methods =
226 List.filter c.sc_static_methods ~f:keep_meth;
229 Some (name, Class c, hash)
230 | name_decl_and_hash -> Some name_decl_and_hash)
232 { parsed_file with pfh_decls = decls }
234 Some parsed_file
236 let direct_decl_parse_and_cache ctx file =
237 match Provider_context.get_backend ctx with
238 | Provider_backend.Rust_provider_backend backend ->
239 Counters.count Counters.Category.Get_decl @@ fun () ->
240 get_file_contents ~ignore_file_content_caches:false ctx file
241 |> Option.map ~f:(fun contents ->
242 Rust_provider_backend.Decl.direct_decl_parse_and_cache
243 backend
244 file
245 contents)
246 | _ ->
247 let result = direct_decl_parse ctx file in
248 (match result with
249 | Some parsed_file -> cache_decls ctx file parsed_file.pfh_decls
250 | None -> ());
251 result
253 let decls_to_fileinfo = Direct_decl_parser.decls_to_fileinfo