From 5712b609979647cd8d0106a3ad856f7f2dcc3b1c Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 4 Oct 2019 06:11:30 -0700 Subject: [PATCH] Add comprehensive hover docs for attributes Summary: Allow users to find out about attributes by hovering over them in Nuclide. This documents all the built-in attributes that are available to Hack programs. Differential Revision: D17670715 fbshipit-source-id: 7bea2dab37b5e223745e3083144f925dd5140793 --- .../src/ide_rpc/nuclide_rpc_message_printer.ml | 3 +- hphp/hack/src/server/identifySymbolService.ml | 8 + hphp/hack/src/server/serverFindRefs.ml | 1 + hphp/hack/src/server/serverHover.ml | 270 ++++++++++++++++++++- hphp/hack/src/server/serverSymbolDefinition.ml | 1 + hphp/hack/src/typing/typing_symbol_json_builder.ml | 1 + hphp/hack/src/utils/symbolOccurrence.ml | 2 + hphp/hack/src/utils/symbolOccurrence.mli | 1 + 8 files changed, 280 insertions(+), 7 deletions(-) diff --git a/hphp/hack/src/ide_rpc/nuclide_rpc_message_printer.ml b/hphp/hack/src/ide_rpc/nuclide_rpc_message_printer.ml index d29100331e0..78bf3f52b6f 100644 --- a/hphp/hack/src/ide_rpc/nuclide_rpc_message_printer.ml +++ b/hphp/hack/src/ide_rpc/nuclide_rpc_message_printer.ml @@ -53,7 +53,8 @@ let identify_symbol_response_to_json results = | Property _ -> "property" | ClassConst _ -> "class_const" | Typeconst _ -> "typeconst" - | GConst -> "global_const") + | GConst -> "global_const" + | Attribute -> "attribute") in let symbol_to_json (occurrence, definition) = let (definition_pos, definition_span, definition_id) = diff --git a/hphp/hack/src/server/identifySymbolService.ml b/hphp/hack/src/server/identifySymbolService.ml index 5d08e1edb9b..404d3c5564f 100644 --- a/hphp/hack/src/server/identifySymbolService.ml +++ b/hphp/hack/src/server/identifySymbolService.ml @@ -25,6 +25,10 @@ let is_target target_line target_char { pos; _ } = let process_class_id ?(is_declaration = false) (pos, cid) = Result_set.singleton { name = cid; type_ = Class; is_declaration; pos } +let process_attribute (pos, cid) = + Result_set.singleton + { name = cid; type_ = Attribute; is_declaration = false; pos } + let clean_member_name name = String_utils.lstrip name "$" let process_member ?(is_declaration = false) c_name id ~is_method ~is_const = @@ -320,6 +324,10 @@ let visitor = process_class_id cid + process_member (snd cid) mid ~is_method:false ~is_const:true + super#on_SFclass_const env cid mid + + method! on_user_attribute env ua = + let acc = process_attribute ua.Aast.ua_name in + self#plus acc (super#on_user_attribute env ua) end let all_symbols tast = visitor#go tast |> Result_set.elements diff --git a/hphp/hack/src/server/serverFindRefs.ml b/hphp/hack/src/server/serverFindRefs.ml index 32fe0760deb..000ec1593bc 100644 --- a/hphp/hack/src/server/serverFindRefs.ml +++ b/hphp/hack/src/server/serverFindRefs.ml @@ -173,6 +173,7 @@ let get_action symbol (filename, file_content, line, char) = | SymbolOccurrence.GConst -> Some (GConst name) | SymbolOccurrence.LocalVar -> Some (LocalVar { filename; file_content; line; char }) + | SymbolOccurrence.Attribute -> None let go_from_file (labelled_file, line, char) env = let (filename, content) = diff --git a/hphp/hack/src/server/serverHover.ml b/hphp/hack/src/server/serverHover.ml index d7218136d41..5eff8dfe7d7 100644 --- a/hphp/hack/src/server/serverHover.ml +++ b/hphp/hack/src/server/serverHover.ml @@ -60,6 +60,261 @@ let make_hover_full_name env_and_ty occurrence def_opt = [Printf.sprintf "Full name: `%s`" (Utils.strip_ns name)] | _ -> [])) +(* Return a markdown description of built-in Hack attributes. *) +let make_hover_attr_docs name = + match name with + | "__AcceptDisposable" -> + [ + "Allows passing values that implement `IDisposable` or `IAsyncDisposable`." + ^ " Normally these values cannot be passed to functions." + ^ "\n\nYou cannot save references to `__AcceptDisposable` parameters, to ensure they are disposed at the end of their using block."; + ] + | "__AllowStatic" -> + [ + "Allows this instance method to be called statically on the class." + ^ "\n\nThis purely for PHP compatibility."; + ] + | "__AtMostRxAsArgs" -> + [ + "Marks a reactive function as being conditionally reactive, depending on the type of its arguments." + ^ "\n\nThe function is 'at most as reactive as its arguments'.." + ^ " For example, a `__RxLocal` argument makes this function `__RxLocal`" + ^ "\n\nThis attribute must be used with `__OnlyRxIfImpl` or `__AtMostRxAsFunc` parameters."; + ] + | "__AtMostRxAsFunc" -> + [ + "Marks a reactive function as being conditionally reactive, depending on the type of this function argument." + ^ "\n\nThe enclosing function is 'at most as reactive as this function'." + ^ " For example, a `__RxLocal` function argument makes the enclosing function `__RxLocal`"; + ] + | "__ALWAYS_INLINE" -> + [ + "Instructs HHVM to always inline this function." + ^ " Only used for testing HHVM." + ^ "\n\nSee also `__NEVER_INLINE`."; + ] + | "__ConsistentConstruct" -> + [ + "Requires all child classes to have the same constructor signature. " + ^ " This allows `new static(...)` and `new $the_class_name(...)`."; + ] + | "__Const" -> + [ + "Marks a class or property as immutable." + ^ " When applied to a class, all the properties are considered `__Const`." + ^ " `__Const` properties can only be set in the constructor."; + ] + | "__Deprecated" -> + [ + "Mark a function/method as deprecated. " + ^ " The type checker will show an error at call sites, and a runtime warning is logged if this function/method is called." + ^ "\n\nThe optional second argument specifies a rate limit for warning logs." + ^ " If the rate limit is 100, a warning is only issued every 1/100 calls."; + ] + | "__DynamicallyCallable" -> + [ + "Allows this function/method to be called dynamically, based on a string of its name. " + ^ " HHVM will warn or error (depending on settings) on dynamic calls to functions without this attribute." + ^ "\n\nSee also `HH\\dynamic_fun()` and ``HH\\dynamic_fun()`."; + ] + | "__DynamicallyConstructible" -> + [ + "Allows this class to be instantiated dynamically, based on a string of its name." + ^ " HHVM will warn or error (depending on settings) on dynamic instantiations without this attribute."; + ] + | "__Enforceable" -> + [ + "Ensures that this type is enforceable." + ^ " Enforceable types can be used with `is` and `as`." + ^ " This forbids usage of function types and erased (not reified) generics."; + ] + | "__EntryPoint" -> + [ + "Execution of the program will start here." + ^ " This only applies in the first file executed, `__EntryPoint` in required or autoloaded files has no effect."; + ] + | "__Explicit" -> + [ + "Requires callers to explicitly specify this type." + ^ "\n\nNormally Hack allows generics to be inferred at the call site."; + ] + | "__HasReifiedParent" -> + [ + "Marks a class as extending a class that uses reified generics." + ^ " This is an internal attribute used for byte compilation, and is banned in user code."; + ] + | "__HipHopSpecific" -> + [ + "Marks a class or function as specific to HHVM, so it is shown on http://docs.hhvm.com."; + ] + | "__IsFoldable" -> + [ + "Marks this function can be constant-folded if all arguments are constants." + ^ " Used by hhbbc."; + ] + | "__LateInit" -> + [ + "Marks a property as late initialized." + ^ " Normally properties are required to be initialized in the constructor."; + ] + | "__LSB" -> + [ + "Marks this property as implicitly redeclared on all subclasses." + ^ " This ensures each subclass has its own value for the property."; + ] + | "__MockClass" -> + [ + "Allows subclasses of final classes and overriding of final methods." + ^ " This is useful for writing mock classes." + ^ "\n\nYou cannot use this to subclass `vec`, `keyset`, `dict`, `Vector`, `Map` or `Set`."; + ] + | "__Memoize" -> + [ + "Cache the return values from this function/method." + ^ " Calls with the same arguments will return the cached value." + ^ "\n\nCaching is per-request and shared between subclasses (see also `__MemoizeLSB`)."; + ] + | "__MemoizeLSB" -> + [ + "Cache the return values from this method." + ^ " Calls with the same arguments will return the cached value." + ^ "\n\nCaching is per-request and has Late Static Binding, so subclasses do not share the cache."; + ] + | "__MaybeMutable" -> + [ + "Allows a reactive function/method to accept both mutable and immutable values." + ^ " This combines the restrictions of immutable values (no modification) with `__Mutable` (no aliases)." + ^ "\n\nThis applies to the parameter specified, or `$this` when `__MaybeMutable` is used on a method."; + ] + | "__Mutable" -> + [ + "Allows mutation of this parameter inside this reactive function/method. " + ^ " When `__Mutable` is used on a method, allows mutation of `$this`." + ^ "\n\nAnnotated values are marked as borrowed mutable, unliked `__OwnedMutable`."; + ] + | "__MutableReturn" -> + [ + "Marks this reactive function/method as returning a mutable value that is owned by the caller."; + ] + | "__Native" -> + [ + "Declares a native function." + ^ " This declares the signature, the implementation will be in an HHVM extension (usually C++)."; + ] + | "__NativeData" -> + [ + "Associates this class with a native data type (usually a C++ class)." + ^ " When instantiating this class, the corresponding native object will also be allocated."; + ] + | "__Newable" -> + [ + "Ensures the class can be constructed." + ^ "\n\nThis forbids abstract classes, and ensures that the constructor has a consistent signature." + ^ " Classes must use `__ConsistentConstruct` or be final."; + ] + | "__NonRx" -> + [ + "Mark this function as intentionally not reactive, so readers do not attempt to refactor it to being reactive." + ^ " When used on methods and global functions, a reason argument is required."; + ] + | "__NoFlatten" -> + [ + "Instructs hhbbc to never inline this trait into classes that use it." + ^ " Used for testing hhbbc optimizations."; + ] + | "__NEVER_INLINE" -> + [ + "Instructs HHVM to never inline this function." + ^ " Only used for testing HHVM." + ^ "\n\nSee also `__ALWAYS_INLINE`."; + ] + | "__OnlyRxIfImpl" -> + [ + "Marks a reactive function as being conditionally reactive, depending on the type of this argument." + ^ " If the argument implements the interface or extends the class specified, then the function is reactive."; + ] + | "__Override" -> ["Ensures there's a parent method being overridden."] + | "__PHPStdLib" -> + [ + "Ignore this built-in function or class, so the type checker errors if code uses it." + ^ " This only applies to code in .hhi files by default, but can apply everywhere with `deregister_php_stdlib`."; + ] + | "__PPL" -> + [ + "Converts all methods to coroutines, except the constructor." + ^ " Local method calls are converted suspend calls." + ^ " The functions `sample`, `factor`, `observe` and `condition` are converted to method calls on an implicit `Infer` object."; + ] + | "__ProvenanceSkipFrame" -> + [ + "Don't track Hack arrays created by this function." + ^ " This is useful when migrating code from PHP arrays to Hack arrays." + ^ "\n\nThe HHVM option LogArrayProvenance is necessary to observe array provenance."; + ] + | "__Reifiable" -> + [ + "Requires this type to be reifiable." + ^ " This bans PHP arrays (varray and darray)."; + ] + | "__Reified" -> + [ + "Marks a function as taking reified generics." + ^ " This is an internal attribute used for byte compilation, and is banned in user code."; + ] + | "__ReturnDisposable" -> + [ + "Allows a function/method to return a value that implements `IDisposable` or `IAsyncDisposable`." + ^ " The function must return a fresh disposable value by either instantiating a class or " + ^ " returning a value from another method/function marked `__ReturnDisposable`."; + ] + | "__ReturnsVoidToRx" -> + [ + "Requires that reactive functions do not use the return value of this function/method." + ^ " This enables classes to provide a fluent builder API `->setFoo()->setBar()` but " + ^ " ensures that reactive functions do `$x->setFoo(); $x->setBar();`."; + ] + | "__Rx" -> + [ + "Ensures this function/method is fully reactive (pure)." + ^ " It cannot access global state, cannot read from unmanaged sources, and cannot have side effects." + ^ " Function calls are only permitted to other `__Rx` functions." + ^ "\n\nFully reactive functions can use the reactive runtime, unlike `__RxShallow` and `__RxLocal`."; + ] + | "__RxLocal" -> + [ + "Ensures this function/method is reactive, ignoring function calls." + ^ " All the restrictions of `__Rx` apply, but calls to non-rx functions are permitted." + ^ "\n\nThis allows gradual migration of code to being fully reactive, and permits calls from `__RxShallow`."; + ] + | "__RxShallow" -> + [ + "Ensures this function/method is reactive, but allows calls to `__RxShallow` and `__RxLocal` functions." + ^ " All the restrictions of `__Rx` apply otherwise." + ^ "\n\nThis allows gradual migration of code to being fully reactive."; + ] + | "__Sealed" -> + [ + "Only the named classes can extend this class or interface." + ^ " Child classes may still be extended unless they are marked `final`."; + ] + | "__Soft" -> + [ + "This parameter/property will not be type checked at runtime." + ^ " This is useful for migrating partial code where you're unsure about the type." + ^ "\n\nThe type checker will still use the type, so callers will get checked." + ^ " If the type is wrong at runtime, a warning will be logged."; + ] + | "__VMSwitchMode" -> + [ + "Used by HHVM to decide how it handles catch data when switching to the debugger."; + ] + | "__Warn" -> + [ + "Ensures that incorrect reified types are a warning rather than error." + ^ "\n\nThis is intended to help gradually migrate code to reified types."; + ] + | _ -> [] + let make_hover_info env_and_ty file (occurrence, def_opt) = SymbolOccurrence.( Typing_defs.( @@ -95,12 +350,15 @@ let make_hover_info env_and_ty file (occurrence, def_opt) = Tast_env.print_ty_with_identity env (LoclTy ty) occurrence def_opt in let addendum = - List.concat - [ - make_hover_doc_block file occurrence def_opt; - make_hover_return_type env_and_ty occurrence; - make_hover_full_name env_and_ty occurrence def_opt; - ] + match occurrence with + | { name; type_ = Attribute; _ } -> make_hover_attr_docs name + | _ -> + List.concat + [ + make_hover_doc_block file occurrence def_opt; + make_hover_return_type env_and_ty occurrence; + make_hover_full_name env_and_ty occurrence def_opt; + ] in HoverService. { snippet; addendum; pos = Some occurrence.SymbolOccurrence.pos })) diff --git a/hphp/hack/src/server/serverSymbolDefinition.ml b/hphp/hack/src/server/serverSymbolDefinition.ml index a4178918800..abfbcf86379 100644 --- a/hphp/hack/src/server/serverSymbolDefinition.ml +++ b/hphp/hack/src/server/serverSymbolDefinition.ml @@ -177,6 +177,7 @@ let go ast result = result.SymbolOccurrence.name result.SymbolOccurrence.pos end + | SymbolOccurrence.Attribute -> None let get_definition_cst_node_from_pos kind source_text pos = try diff --git a/hphp/hack/src/typing/typing_symbol_json_builder.ml b/hphp/hack/src/typing/typing_symbol_json_builder.ml index 57718f8dab1..505d420af15 100644 --- a/hphp/hack/src/typing/typing_symbol_json_builder.ml +++ b/hphp/hack/src/typing/typing_symbol_json_builder.ml @@ -282,6 +282,7 @@ let json_of_symbol_occurrence_shared symbol_occurrence progress symbol_def = ("property_name", key_ (JSON_String s2)); ] ) | GConst -> (7, JSON_Null) + | Attribute -> (0, JSON_Null) (* TODO(T44306013): Update schema to support records. See D15927952 for an example. *) | Record -> (0, JSON_Null) in diff --git a/hphp/hack/src/utils/symbolOccurrence.ml b/hphp/hack/src/utils/symbolOccurrence.ml index abfcb80e2ae..98b867f3a7b 100644 --- a/hphp/hack/src/utils/symbolOccurrence.ml +++ b/hphp/hack/src/utils/symbolOccurrence.ml @@ -17,6 +17,7 @@ type kind = | ClassConst of string * string | Typeconst of string * string | GConst + | Attribute type 'a t = { name: string; @@ -38,6 +39,7 @@ let kind_to_string = function | ClassConst _ -> "member_const" | Typeconst _ -> "typeconst" | GConst -> "global_const" + | Attribute -> "attribute" let enclosing_class occurrence = match occurrence.type_ with diff --git a/hphp/hack/src/utils/symbolOccurrence.mli b/hphp/hack/src/utils/symbolOccurrence.mli index be8d22bdf06..f126c9e348d 100644 --- a/hphp/hack/src/utils/symbolOccurrence.mli +++ b/hphp/hack/src/utils/symbolOccurrence.mli @@ -23,6 +23,7 @@ type kind = | ClassConst of string * string | Typeconst of string * string | GConst + | Attribute type 'a t = { name: string; -- 2.11.4.GIT