Fix special case of alternate if statements
[hiphop-php.git] / hphp / hack / src / server / fileOutline.ml
blobfefe577e04553e974be18245745ded38e1150974
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Hh_core
11 open Reordered_argument_collections
12 open SymbolDefinition
13 module Parser = Full_fidelity_ast
15 let modifiers_of_ast_kinds l =
16 List.map l begin function
17 | Ast.Final -> Final
18 | Ast.Static -> Static
19 | Ast.Abstract -> Abstract
20 | Ast.Private -> Private
21 | Ast.Public -> Public
22 | Ast.Protected -> Protected
23 end
25 let get_full_name class_name name =
26 match class_name with
27 | None -> name
28 | Some class_name -> class_name ^ "::" ^ name
30 let summarize_property class_name kinds var =
31 let modifiers = modifiers_of_ast_kinds kinds in
32 let span, (pos, name), _expr_opt = var in
33 let kind = Property in
34 let id = get_symbol_id kind (Some class_name) name in
35 let full_name = get_full_name (Some class_name) name in
37 kind;
38 name;
39 full_name;
40 id;
41 pos;
42 span;
43 modifiers;
44 children = None;
45 params = None;
46 docblock = None;
49 let maybe_summarize_property class_name ~skip kinds var =
50 let _, (_, name), _ = var in
51 if SSet.mem skip name then [] else [summarize_property class_name kinds var]
53 let summarize_const class_name ((pos, name), (expr_pos, _)) =
54 let span = (Pos.btw pos expr_pos) in
55 let kind = Const in
56 let id = get_symbol_id kind (Some class_name) name in
57 let full_name = get_full_name (Some class_name) name in
59 kind;
60 name;
61 full_name;
62 id;
63 pos;
64 span;
65 modifiers = [];
66 children = None;
67 params = None;
68 docblock = None;
71 let summarize_abs_const class_name (pos, name) =
72 let kind = Const in
73 let id = get_symbol_id kind (Some class_name) name in
74 let full_name = get_full_name (Some class_name) name in
76 kind;
77 name;
78 full_name;
79 id;
80 pos = pos;
81 span = pos;
82 modifiers = [Abstract];
83 children = None;
84 params = None;
85 docblock = None;
88 let modifier_of_fun_kind acc = function
89 | Ast.FAsync | Ast.FAsyncGenerator -> Async :: acc
90 | _ -> acc
92 let modifier_of_param_kind acc = function
93 | Some Ast.Pinout -> Inout :: acc
94 | _ -> acc
96 let summarize_typeconst class_name t =
97 let pos, name = t.Ast.tconst_name in
98 let kind = Typeconst in
99 let id = get_symbol_id kind (Some class_name) name in
100 let full_name = get_full_name (Some class_name) name in
102 kind;
103 name;
104 full_name;
106 pos;
107 span = t.Ast.tconst_span;
108 modifiers = if t.Ast.tconst_abstract then [Abstract] else [];
109 children = None;
110 params = None;
111 docblock = None;
114 let summarize_param param =
115 let pos, name = param.Ast.param_id in
116 let param_start = Option.value_map param.Ast.param_hint ~f:fst ~default:pos in
117 let param_end = Option.value_map param.Ast.param_expr ~f:fst ~default:pos in
118 let modifiers = modifier_of_param_kind [] param.Ast.param_callconv in
119 let param_vis = Option.to_list param.Ast.param_modifier in
120 let modifiers = (modifiers_of_ast_kinds param_vis) @ modifiers in
121 let kind = Param in
122 let id = get_symbol_id kind None name in
123 let full_name = get_full_name None name in
125 kind;
126 name;
127 full_name;
129 pos;
130 span = Pos.btw param_start param_end;
131 children = None;
132 modifiers;
133 params = None;
134 docblock = None;
137 let summarize_method class_name m =
138 let modifiers = modifier_of_fun_kind [] m.Ast.m_fun_kind in
139 let modifiers = (modifiers_of_ast_kinds m.Ast.m_kind) @ modifiers in
140 let params = Some (List.map m.Ast.m_params summarize_param) in
141 let name = snd m.Ast.m_name in
142 let kind = Method in
143 let id = get_symbol_id kind (Some class_name) name in
144 let full_name = get_full_name (Some class_name) name in
146 kind;
147 name;
148 full_name;
150 pos = (fst m.Ast.m_name);
151 span = m.Ast.m_span;
152 modifiers;
153 children = None;
154 params;
155 docblock = None;
158 (* Parser synthesizes AST nodes for implicit properties (defined in constructor
159 * parameter lists. We don't want them to show up in outline view *)
160 let params_implicit_fields params =
161 List.filter_map params ~f:begin function
162 | { Ast.param_modifier = Some _vis; param_id; _ } ->
163 Some (String_utils.lstrip (snd param_id) "$" )
164 | _ -> None
167 let class_implicit_fields class_ =
168 List.concat_map class_.Ast.c_body ~f:begin function
169 | Ast.Method { Ast.m_name = _, "__construct"; m_params; _ } ->
170 params_implicit_fields m_params
171 | _ -> []
174 let summarize_class class_ ~no_children =
175 let class_name = Utils.strip_ns (snd class_.Ast.c_name) in
176 let class_name_pos = fst class_.Ast.c_name in
177 let c_span = class_.Ast.c_span in
178 let modifiers =
179 if class_.Ast.c_final then [Final] else []
181 let modifiers = match class_.Ast.c_kind with
182 | Ast.Cabstract -> Abstract :: modifiers
183 | _ -> modifiers
185 let children = if no_children then None else begin
186 let implicit_props = List.fold (class_implicit_fields class_)
187 ~f:SSet.add ~init:SSet.empty
189 Some (List.concat_map class_.Ast.c_body ~f:begin function
190 | Ast.Method m -> [summarize_method class_name m]
191 | Ast.ClassVars { Ast.cv_kinds = kinds; Ast.cv_names = vars; _ } ->
192 List.concat_map vars
193 ~f:(maybe_summarize_property class_name ~skip:implicit_props kinds)
194 | Ast.XhpAttr (_, var, _, _) ->
195 maybe_summarize_property class_name ~skip:implicit_props [] var
196 | Ast.Const (_, cl) -> List.map cl ~f:(summarize_const class_name)
197 | Ast.AbsConst (_, id) -> [summarize_abs_const class_name id]
198 | Ast.TypeConst t -> [summarize_typeconst class_name t]
199 | _ -> []
200 end)
201 end in
202 let kind = match class_.Ast.c_kind with
203 | Ast.Cinterface -> Interface
204 | Ast.Ctrait -> Trait
205 | Ast.Cenum -> Enum
206 | _ -> Class
208 let name = class_name in
209 let id = get_symbol_id kind None name in
210 let full_name = get_full_name None name in
212 kind;
213 name;
214 full_name;
216 pos = class_name_pos;
217 span = c_span;
218 modifiers;
219 children;
220 params = None;
221 docblock = None;
224 let typedef_kind_pos tk =
225 begin match tk with
226 | Ast.Alias (pos,_) -> pos
227 | Ast.NewType (pos,_) -> pos
230 let summarize_typedef tdef =
231 let kind = Typedef in
232 let name = Utils.strip_ns (snd tdef.Ast.t_id) in
233 let id = get_symbol_id kind None name in
234 let full_name = get_full_name None name in
235 let pos = fst tdef.Ast.t_id in
236 let kind_pos = typedef_kind_pos tdef.Ast.t_kind in
237 let span = (Pos.btw pos kind_pos) in
239 kind;
240 name;
241 full_name;
243 pos;
244 span;
245 modifiers = [];
246 children = None;
247 params = None;
248 docblock = None;
251 let summarize_fun f =
252 let modifiers = modifier_of_fun_kind [] f.Ast.f_fun_kind in
253 let params = Some (List.map f.Ast.f_params summarize_param) in
254 let kind = Function in
255 let name = Utils.strip_ns (snd f.Ast.f_name) in
256 let id = get_symbol_id kind None name in
257 let full_name = get_full_name None name in
259 kind;
260 name;
261 full_name;
263 pos = fst f.Ast.f_name;
264 span = f.Ast.f_span;
265 modifiers;
266 children = None;
267 params;
268 docblock = None;
271 let summarize_gconst cst =
272 let pos = fst cst.Ast.cst_name in
273 let gconst_start = Option.value_map cst.Ast.cst_type ~f:fst ~default:pos in
274 let gconst_end = fst cst.Ast.cst_value in
275 let kind = Const in
276 let name = Utils.strip_ns (snd cst.Ast.cst_name) in
277 let id = get_symbol_id kind None name in
278 let full_name = get_full_name None name in
280 kind;
281 name;
282 full_name;
284 pos;
285 span = Pos.btw gconst_start gconst_end;
286 modifiers = [];
287 children = None;
288 params = None;
289 docblock = None;
292 let summarize_local name span =
293 let kind = LocalVar in
294 let id = get_symbol_id kind None name in
295 let full_name = get_full_name None name in
297 kind;
298 name;
299 full_name;
301 pos = span;
302 span;
303 modifiers = [];
304 children = None;
305 params = None;
306 docblock = None;
309 let outline_ast ast =
310 let outline = List.filter_map ast ~f:begin function
311 | Ast.Fun f -> Some (summarize_fun f)
312 | Ast.Class c -> Some (summarize_class c ~no_children:false)
313 | _ -> None
314 end in
315 List.map outline SymbolDefinition.to_absolute
317 let should_add_docblock = function
318 | Function| Class | Method | Property | Const | Enum
319 | Interface | Trait | Typeconst | Typedef -> true
320 | LocalVar | Param -> false
322 let add_def_docblock finder previous_def_line def =
323 let line = Pos.line def.pos in
324 let docblock = if should_add_docblock def.kind
325 then Docblock_finder.find_docblock finder previous_def_line line
326 else None
328 line, { def with docblock }
330 let add_docblocks defs comments =
331 let finder = Docblock_finder.make_docblock_finder comments in
333 let rec map_def f (acc : int) (def : string SymbolDefinition.t) =
334 let acc, def = f acc def in
335 let acc, children = Option.value_map def.children
336 ~f:(fun defs ->
337 let acc, defs = map_def_list f acc defs in
338 acc, Some defs)
339 ~default:(acc, None)
341 acc, { def with children }
343 and map_def_list f (acc : int) (defs : string SymbolDefinition.t list) =
344 let acc, defs = List.fold_left defs
345 ~f:(fun (acc, defs) def ->
346 let acc, def = map_def f acc def in
347 acc, def :: defs)
348 ~init:(acc, []) in
349 acc, List.rev defs
352 snd (map_def_list (add_def_docblock finder) 0 defs)
354 let outline popt content =
355 let env = Parser.make_env
356 ~parser_options:popt
357 ~include_line_comments:true
358 ~keep_errors:false
359 Relative_path.default
361 let {Parser_hack.ast; comments; _} = Parser.from_text_with_legacy env content in
362 let result = outline_ast ast in
363 add_docblocks result comments
365 let rec print_def ~short_pos indent def =
367 {name; kind; id; pos; span; modifiers; children; params; docblock;
368 full_name=_} = def
370 let print_pos, print_span = if short_pos
371 then Pos.string_no_file, Pos.multiline_string_no_file
372 else Pos.string, Pos.multiline_string
374 Printf.printf "%s%s\n" indent name;
375 Printf.printf "%s kind: %s\n" indent (string_of_kind kind);
376 Option.iter id (fun id -> Printf.printf "%s id: %s\n" indent id);
377 Printf.printf "%s position: %s\n" indent (print_pos pos);
378 Printf.printf "%s span: %s\n" indent (print_span span);
379 Printf.printf "%s modifiers: " indent;
380 List.iter modifiers
381 (fun x -> Printf.printf "%s " (string_of_modifier x));
382 Printf.printf "\n";
383 Option.iter params (fun x ->
384 Printf.printf "%s params:\n" indent;
385 print ~short_pos (indent ^ " ") x;
387 Option.iter docblock (fun x ->
388 Printf.printf "%s docblock:\n" indent;
389 Printf.printf "%s\n" x;
391 Printf.printf "\n";
392 Option.iter children (fun x ->
393 print ~short_pos (indent ^ " ") x
396 and print ~short_pos indent defs =
397 List.iter defs ~f:(print_def ~short_pos indent)
399 let print_def ?short_pos:(short_pos = false) = print_def ~short_pos
400 let print ?short_pos:(short_pos = false) = print ~short_pos ""