Remove repeated return type in hover
[hiphop-php.git] / hphp / hack / src / server / serverHover.ml
blob71cc4020108e8686cf4ea37913e9ea4c4120e9e6
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_prelude
11 open HoverService
13 (** When we get a Class occurrence and a Method occurrence, that means that the
14 user is hovering over an invocation of the constructor, and would therefore only
15 want to see information about the constructor, rather than getting both the
16 class and constructor back in the hover. *)
17 let filter_class_and_constructor results =
18 let result_is_constructor result =
19 SymbolOccurrence.is_constructor (fst result)
21 let result_is_class result = SymbolOccurrence.is_class (fst result) in
22 let has_class = List.exists results ~f:result_is_class in
23 let has_constructor = List.exists results ~f:result_is_constructor in
24 if has_class && has_constructor then
25 List.filter results ~f:result_is_constructor |> List.map ~f:snd
26 else
27 results |> List.map ~f:snd
29 let make_hover_doc_block ctx entry occurrence def_opt =
30 match def_opt with
31 | Some def ->
32 let base_class_name = SymbolOccurrence.enclosing_class occurrence in
33 ServerDocblockAt.go_comments_for_symbol_ctx
34 ~ctx
35 ~entry
36 ~def
37 ~base_class_name
38 |> Option.to_list
39 | None -> []
41 let make_hover_const_definition entry def_opt =
42 match def_opt with
43 | Some def ->
45 Pos.get_text_from_pos
46 ~content:(Provider_context.read_file_contents_exn entry)
47 def.SymbolDefinition.span;
49 | _ -> []
51 let make_hover_full_name env_and_ty occurrence def_opt =
52 SymbolOccurrence.(
53 let found_it () =
54 let name =
55 match def_opt with
56 | Some def -> def.SymbolDefinition.full_name
57 | None -> occurrence.name
59 [Printf.sprintf "Full name: `%s`" (Utils.strip_ns name)]
61 Typing_defs.(
62 match (occurrence, env_and_ty) with
63 | ({ type_ = Method _; _ }, Some (_, _ty)) -> found_it ()
64 | ( { type_ = Property _ | ClassConst _ | XhpLiteralAttr _; _ },
65 Some (_, ty) )
66 when is_fun ty ->
67 found_it ()
68 | _ -> []))
70 (* Return a markdown description of built-in Hack attributes. *)
71 let make_hover_attr_docs name =
72 match name with
73 | "__AcceptDisposable" ->
75 "Allows passing values that implement `IDisposable` or `IAsyncDisposable`."
76 ^ " Normally these values cannot be passed to functions."
77 ^ "\n\nYou cannot save references to `__AcceptDisposable` parameters, to ensure they are disposed at the end of their using block.";
79 | "__ALWAYS_INLINE" ->
81 "Instructs HHVM to always inline this function."
82 ^ " Only used for testing HHVM."
83 ^ "\n\nSee also `__NEVER_INLINE`.";
85 | "__ConsistentConstruct" ->
87 "Requires all child classes to have the same constructor signature. "
88 ^ " This allows `new static(...)` and `new $the_class_name(...)`.";
90 | "__Const" ->
92 "Marks a class or property as immutable."
93 ^ " When applied to a class, all the properties are considered `__Const`."
94 ^ " `__Const` properties can only be set in the constructor.";
96 | "__Deprecated" ->
98 "Mark a function/method as deprecated. "
99 ^ " The type checker will show an error at call sites, and a runtime warning is logged if this function/method is called."
100 ^ "\n\nThe optional second argument specifies a rate limit for warning logs."
101 ^ " If the rate limit is 100, a warning is only issued every 1/100 calls.";
103 | "__DynamicallyCallable" ->
105 "Allows this function/method to be called dynamically, based on a string of its name. "
106 ^ " HHVM will warn or error (depending on settings) on dynamic calls to functions without this attribute."
107 ^ "\n\nSee also `HH\\dynamic_fun()` and `HH\\dynamic_fun()`.";
109 | "__DynamicallyConstructible" ->
111 "Allows this class to be instantiated dynamically, based on a string of its name."
112 ^ " HHVM will warn or error (depending on settings) on dynamic instantiations without this attribute.";
114 | "__Enforceable" ->
116 "Ensures that this type is enforceable."
117 ^ " Enforceable types can be used with `is` and `as`."
118 ^ " This forbids usage of function types and erased (not reified) generics.";
120 | "__EntryPoint" ->
122 "Execution of the program will start here."
123 ^ " This only applies in the first file executed, `__EntryPoint` in required or autoloaded files has no effect.";
125 | "__Explicit" ->
127 "Requires callers to explicitly specify this type."
128 ^ "\n\nNormally Hack allows generics to be inferred at the call site.";
130 | "__HasReifiedParent" ->
132 "Marks a class as extending a class that uses reified generics."
133 ^ " This is an internal attribute used for byte compilation, and is banned in user code.";
135 | "__IsFoldable" ->
137 "Marks this function can be constant-folded if all arguments are constants."
138 ^ " Used by hhbbc.";
140 | "__LateInit" ->
142 "Marks a property as late initialized."
143 ^ " Normally properties are required to be initialized in the constructor.";
145 | "__LSB" ->
147 "Marks this property as implicitly redeclared on all subclasses."
148 ^ " This ensures each subclass has its own value for the property.";
150 | "__MockClass" ->
152 "Allows subclasses of final classes and overriding of final methods."
153 ^ " This is useful for writing mock classes."
154 ^ "\n\nYou cannot use this to subclass `vec`, `keyset`, `dict`, `Vector`, `Map` or `Set`.";
156 | "__Memoize" ->
158 "Cache the return values from this function/method."
159 ^ " Calls with the same arguments will return the cached value."
160 ^ "\n\nCaching is per-request and shared between subclasses (see also `__MemoizeLSB`).";
162 | "__MemoizeLSB" ->
164 "Cache the return values from this method."
165 ^ " Calls with the same arguments will return the cached value."
166 ^ "\n\nCaching is per-request and has Late Static Binding, so subclasses do not share the cache.";
168 | "__Native" ->
170 "Declares a native function."
171 ^ " This declares the signature, the implementation will be in an HHVM extension (usually C++).";
173 | "__NativeData" ->
175 "Associates this class with a native data type (usually a C++ class)."
176 ^ " When instantiating this class, the corresponding native object will also be allocated.";
178 | "__Newable" ->
180 "Ensures the class can be constructed."
181 ^ "\n\nThis forbids abstract classes, and ensures that the constructor has a consistent signature."
182 ^ " Classes must use `__ConsistentConstruct` or be final.";
184 | "__NoFlatten" ->
186 "Instructs hhbbc to never inline this trait into classes that use it."
187 ^ " Used for testing hhbbc optimizations.";
189 | "__NEVER_INLINE" ->
191 "Instructs HHVM to never inline this function."
192 ^ " Only used for testing HHVM."
193 ^ "\n\nSee also `__ALWAYS_INLINE`.";
195 | "__Override" -> ["Ensures there's a parent method being overridden."]
196 | "__PHPStdLib" ->
198 "Ignore this built-in function or class, so the type checker errors if code uses it."
199 ^ " This only applies to code in .hhi files by default, but can apply everywhere with `deregister_php_stdlib`.";
201 | "__ProvenanceSkipFrame" ->
203 "Don't track Hack arrays created by this function."
204 ^ " This is useful when migrating code from PHP arrays to Hack arrays.";
206 | "__Reifiable" ->
208 "Requires this type to be reifiable."
209 ^ " This bans PHP arrays (varray and darray).";
211 | "__Reified" ->
213 "Marks a function as taking reified generics."
214 ^ " This is an internal attribute used for byte compilation, and is banned in user code.";
216 | "__ReturnDisposable" ->
218 "Allows a function/method to return a value that implements `IDisposable` or `IAsyncDisposable`."
219 ^ " The function must return a fresh disposable value by either instantiating a class or "
220 ^ " returning a value from another method/function marked `__ReturnDisposable`.";
222 | "__Sealed" ->
224 "Only the named classes can extend this class or interface."
225 ^ " Child classes may still be extended unless they are marked `final`.";
227 | "__Soft" ->
229 "A runtime type mismatch on this parameter/property will not throw a TypeError/Error."
230 ^ " This is useful for migrating partial code where you're unsure about the type."
231 ^ "\n\nThe type checker will ignore this attribute, so your code will still get type checked."
232 ^ " If the type is wrong at runtime, a warning will be logged and code execution will continue.";
234 | "__Warn" ->
236 "Ensures that incorrect reified types are a warning rather than error."
237 ^ "\n\nThis is intended to help gradually migrate code to reified types.";
239 | _ -> []
241 let keyword_info (khi : SymbolOccurrence.keyword_with_hover_docs) : string =
242 let await_explanation =
243 "\n\nThis does not give you threads. Only one function is running at any point in time."
244 ^ " Instead, the runtime may switch to another function at an `await` expression, and come back to this function later."
245 ^ "\n\nThis allows data fetching (e.g. database requests) to happen in parallel."
248 match khi with
249 | SymbolOccurrence.FinalOnClass ->
250 "A `final` class cannot be extended by other classes.\n\nTo restrict which classes can extend this, use `<<__Sealed()>>`."
251 | SymbolOccurrence.FinalOnMethod ->
252 "A `final` method cannot be overridden in child classes."
253 | SymbolOccurrence.AbstractOnClass ->
254 "An `abstract` class can only contain `static` methods and `abstract` instance methods.\n\n"
255 ^ "`abstract` classes cannot be instantiated directly. You can only use `new` on child classes that aren't `abstract`."
256 | SymbolOccurrence.AbstractOnMethod ->
257 "An `abstract` method has a signature but no body. Child classes must provide an implementation."
258 | SymbolOccurrence.ExtendsOnClass ->
259 "Extending a class allows your class to inherit methods from another class."
260 ^ "\n\nInheritance allows your class to:"
261 ^ "\n * Reuse methods from the parent class"
262 ^ "\n * Call `protected` methods on the parent class"
263 ^ "\n * Be passed as a parameter whenever an instance of the parent class is expected"
264 ^ "\n\nHack does not support multiple inheritance on classes. If you need to share functionality between"
265 ^ " unrelated classes, use traits."
266 | SymbolOccurrence.ExtendsOnInterface ->
267 "Extending an interface allows your interface to include methods from other interfaces."
268 ^ "\n\nAn interface can extend multiple interfaces."
269 | SymbolOccurrence.ReadonlyOnMethod ->
270 "A `readonly` method treats `$this` as `readonly`."
271 | SymbolOccurrence.ReadonlyOnExpression
272 | SymbolOccurrence.ReadonlyOnParameter ->
273 "A `readonly` value is a reference that cannot modify the underlying value."
274 | SymbolOccurrence.ReadonlyOnReturnType ->
275 "This function/method may return a `readonly` value."
276 | SymbolOccurrence.Async ->
277 "An `async` function can use `await` to get results from other `async` functions. You may still return plain values, e.g. `return 1;` is permitted in an `Awaitable<int>` function."
278 ^ await_explanation
279 | SymbolOccurrence.AsyncBlock ->
280 "An `async` block is syntactic sugar for an `async` lambda that is immediately called."
281 ^ "\n\n```"
282 ^ "\n$f = async { return 1; };"
283 ^ "\n// Equivalent to:"
284 ^ "\n$f = (async () ==> { return 1; })();"
285 ^ "\n```"
286 ^ "\n\nThis is useful when building more complex async expressions."
287 ^ "\n\n```"
288 ^ "\nconcurrent {"
289 ^ "\n $group_name = await async {"
290 ^ "\n return $group is null ? '' : await $group->genName();"
291 ^ "\n };"
292 ^ "\n await async {"
293 ^ "\n try {"
294 ^ "\n await gen_log_request();"
295 ^ "\n } catch (LogRequestFailed $_) {}"
296 ^ "\n }"
297 ^ "\n}"
298 ^ "\n```"
299 | SymbolOccurrence.Await ->
300 "`await` waits for the result of an `Awaitable<_>` value."
301 ^ await_explanation
302 | SymbolOccurrence.Concurrent ->
303 "`concurrent` allows you to `await` multiple values at once. This is similar to `Vec\\map_async`, but `concurrent` allows awaiting unrelated values of different types."
305 let make_hover_info ctx env_and_ty entry occurrence def_opt =
306 SymbolOccurrence.(
307 Typing_defs.(
308 let snippet =
309 match (occurrence, env_and_ty) with
310 | ({ name; _ }, None) -> Utils.strip_hh_lib_ns name
311 | ({ type_ = Method (ClassName classname, name); _ }, Some (env, ty))
312 when String.equal name Naming_special_names.Members.__construct ->
313 let snippet_opt =
314 Option.Monad_infix.(
315 Decl_provider.get_class ctx classname >>= fun c ->
316 fst (Decl_provider.Class.construct c) >>| fun elt ->
317 let ty = Lazy.force_val elt.ce_type in
318 Tast_env.print_ty_with_identity env (DeclTy ty) occurrence def_opt)
320 begin
321 match snippet_opt with
322 | Some s -> s
323 | None ->
324 Tast_env.print_ty_with_identity env (LoclTy ty) occurrence def_opt
326 | (occurrence, Some (env, ty)) ->
327 Tast_env.print_ty_with_identity env (LoclTy ty) occurrence def_opt
329 let addendum =
330 match occurrence with
331 | { name; type_ = Attribute _; _ } ->
332 List.concat
334 make_hover_attr_docs name;
335 make_hover_doc_block ctx entry occurrence def_opt;
337 | { type_ = GConst; _ } ->
338 List.concat
340 make_hover_doc_block ctx entry occurrence def_opt;
341 make_hover_const_definition entry def_opt;
343 | { type_ = Keyword info; _ } -> [keyword_info info]
344 | _ ->
345 List.concat
347 make_hover_doc_block ctx entry occurrence def_opt;
348 make_hover_full_name env_and_ty occurrence def_opt;
351 HoverService.
352 { snippet; addendum; pos = Some occurrence.SymbolOccurrence.pos }))
354 let make_hover_info_with_fallback results =
355 let class_fallback =
356 List.hd
357 (List.filter results ~f:(fun (_, _, _, occurrence, _) ->
358 SymbolOccurrence.is_class occurrence))
360 List.map
361 ~f:(fun (ctx, env_and_ty, entry, occurrence, def_opt) ->
363 SymbolOccurrence.is_constructor occurrence
364 && List.is_empty (make_hover_doc_block ctx entry occurrence def_opt)
365 then
366 (* Case where constructor docblock is empty. *)
367 let hover_info =
368 make_hover_info ctx env_and_ty entry occurrence def_opt
370 match class_fallback with
371 | Some (ctx, _, entry, class_occurrence, def_opt) ->
372 let fallback_doc_block =
373 make_hover_doc_block ctx entry class_occurrence def_opt
375 ( occurrence,
376 HoverService.
378 snippet = hover_info.snippet;
379 addendum = List.concat [fallback_doc_block; hover_info.addendum];
380 pos = hover_info.pos;
382 | None -> (occurrence, hover_info)
383 else
384 (occurrence, make_hover_info ctx env_and_ty entry occurrence def_opt))
385 results
387 let go_quarantined
388 ~(ctx : Provider_context.t)
389 ~(entry : Provider_context.entry)
390 ~(line : int)
391 ~(column : int) : HoverService.result =
392 let identities =
393 ServerIdentifyFunction.go_quarantined ~ctx ~entry ~line ~column
395 let { Tast_provider.Compute_tast.tast; _ } =
396 Tast_provider.compute_tast_quarantined ~ctx ~entry
398 let env_and_ty = ServerInferType.expanded_type_at_pos ctx tast line column in
399 (* There are legitimate cases where we expect to have no identities returned,
400 so just format the type. *)
401 match identities with
402 | [] ->
403 begin
404 match env_and_ty with
405 | Some (env, ty) ->
406 [{ snippet = Tast_env.print_ty env ty; addendum = []; pos = None }]
407 | None -> []
409 | identities ->
410 identities
411 |> List.map ~f:(fun (occurrence, def_opt) ->
412 let env_and_ty =
413 match occurrence.SymbolOccurrence.type_ with
414 | SymbolOccurrence.TypeVar -> None
415 | _ -> env_and_ty
417 let path =
418 def_opt
419 |> Option.map ~f:(fun def -> def.SymbolDefinition.pos)
420 |> Option.map ~f:Pos.filename
421 |> Option.value ~default:entry.Provider_context.path
423 let (ctx, entry) =
424 Provider_context.add_entry_if_missing ~ctx ~path
426 (ctx, env_and_ty, entry, occurrence, def_opt))
427 |> make_hover_info_with_fallback
428 |> filter_class_and_constructor
429 |> List.remove_consecutive_duplicates ~equal:equal_hover_info