Skip to content

Commit

Permalink
Define a __Docs attribute on classes
Browse files Browse the repository at this point in the history
Summary:
Define a `<<__Docs("http://example.com/my_project")>>` attribute that allows users to mark the docs homepage for their classes. This makes docs URLs visible to the toolchain, which will enable them to be inherited and shown in the IDE (see later diffs in this stack).

This diff introduces the `__Docs` attribute, adds the field to the AAST and updates the lowerer to set this field.

Ignore hhvm-and-hack-ubuntu which is failing on trunk too.

Reviewed By: hgoldstein

Differential Revision: D37003893

fbshipit-source-id: 131c4174f4d11a36c4f82206a92ad0b9f232d560
  • Loading branch information
Wilfred authored and facebook-github-bot committed Jul 5, 2022
1 parent 16c94fb commit 9e284df
Show file tree
Hide file tree
Showing 123 changed files with 575 additions and 344 deletions.
1 change: 1 addition & 0 deletions hphp/hack/src/annotated_ast/aast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@ and ('ex, 'en) class_ = {
c_namespace: nsenv;
c_user_attributes: ('ex, 'en) user_attribute list;
c_file_attributes: ('ex, 'en) file_attribute list;
c_docs_url: string option;
c_enum: enum_ option;
c_doc_comment: doc_comment option;
c_emit_id: emit_id option;
Expand Down
1 change: 1 addition & 0 deletions hphp/hack/src/hackc/compile/closure_convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,7 @@ fn make_closure(
internal: false,
// TODO: closures should have the visibility of the module they are defined in
module: None,
docs_url: None,
};

// TODO(hrust): can we reconstruct fd here from the scratch?
Expand Down
1 change: 1 addition & 0 deletions hphp/hack/src/naming/naming.ml
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ let rec class_ ctx c =
N.c_emit_id = c.Aast.c_emit_id;
N.c_internal = c.Aast.c_internal;
N.c_module = c.Aast.c_module;
N.c_docs_url = c.Aast.c_docs_url;
}

and user_attributes env attrl =
Expand Down
8 changes: 8 additions & 0 deletions hphp/hack/src/naming/naming_special_names.ml
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ module UserAttributes = struct

let uaDeprecated = "__Deprecated"

let uaDocs = "__Docs"

let uaEntryPoint = "__EntryPoint"

let uaMemoize = "__Memoize"
Expand Down Expand Up @@ -401,6 +403,12 @@ module UserAttributes = struct
^ " If the sampling rate is 100, a notice is only raised every 1/100 calls. If omitted, the default sampling rate is 1 (i.e. all calls raise notices)."
^ " To disable runtime notices, use a sampling rate of 0.";
} );
( uaDocs,
{
contexts = [cls];
autocomplete = true;
doc = "Shows the linked URL when hovering over this type.";
} );
( uaEntryPoint,
{
contexts = [fn];
Expand Down
3 changes: 3 additions & 0 deletions hphp/hack/src/naming/naming_special_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ pub mod user_attributes {

pub const DEPRECATED: &str = "__Deprecated";

pub const DOCS: &str = "__Docs";

pub const ENTRY_POINT: &str = "__EntryPoint";

pub const MEMOIZE: &str = "__Memoize";
Expand Down Expand Up @@ -326,6 +328,7 @@ pub mod user_attributes {
CONSISTENT_CONSTRUCT,
CONST,
DEPRECATED,
DOCS,
ENTRY_POINT,
MEMOIZE,
MEMOIZE_LSB,
Expand Down
3 changes: 2 additions & 1 deletion hphp/hack/src/oxidized/aast_visitor/node_impl_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the "hack" directory of this source tree.
//
// @generated SignedSource<<7f946d2ac10b9b206329cbc119e54016>>
// @generated SignedSource<<e1ad32b29dd247f9d61fc11411c303cb>>
//
// To regenerate this file, run:
// hphp/hack/src/oxidized_regen.sh
Expand Down Expand Up @@ -468,6 +468,7 @@ impl<P: Params> Node<P> for Class_<P::Ex, P::En> {
self.namespace.accept(c, v)?;
self.user_attributes.accept(c, v)?;
self.file_attributes.accept(c, v)?;
self.docs_url.accept(c, v)?;
self.enum_.accept(c, v)?;
self.doc_comment.accept(c, v)?;
self.emit_id.accept(c, v)?;
Expand Down
3 changes: 2 additions & 1 deletion hphp/hack/src/oxidized/aast_visitor/node_mut_impl_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the "hack" directory of this source tree.
//
// @generated SignedSource<<f0a0d5ca179c975cc43c092d869d512b>>
// @generated SignedSource<<76a71fa7428d740c66237265628d4493>>
//
// To regenerate this file, run:
// hphp/hack/src/oxidized_regen.sh
Expand Down Expand Up @@ -468,6 +468,7 @@ impl<P: Params> NodeMut<P> for Class_<P::Ex, P::En> {
self.namespace.accept(c, v)?;
self.user_attributes.accept(c, v)?;
self.file_attributes.accept(c, v)?;
self.docs_url.accept(c, v)?;
self.enum_.accept(c, v)?;
self.doc_comment.accept(c, v)?;
self.emit_id.accept(c, v)?;
Expand Down
3 changes: 2 additions & 1 deletion hphp/hack/src/oxidized/gen/aast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the "hack" directory of this source tree.
//
// @generated SignedSource<<71e7d74c1497c94a5c83d21a60f09212>>
// @generated SignedSource<<2abc1b50c5c8dbc44a30c38131a22352>>
//
// To regenerate this file, run:
// hphp/hack/src/oxidized_regen.sh
Expand Down Expand Up @@ -1338,6 +1338,7 @@ pub struct Class_<Ex, En> {
pub namespace: Nsenv,
pub user_attributes: Vec<UserAttribute<Ex, En>>,
pub file_attributes: Vec<FileAttribute<Ex, En>>,
pub docs_url: Option<String>,
pub enum_: Option<Enum_>,
pub doc_comment: Option<DocComment>,
pub emit_id: Option<EmitId>,
Expand Down
4 changes: 3 additions & 1 deletion hphp/hack/src/oxidized_by_ref/gen/aast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the "hack" directory of this source tree.
//
// @generated SignedSource<<2b43d33ace096c053aa8f91b8a3f7787>>
// @generated SignedSource<<e8b27d9579188e6f6d400168bcea586a>>
//
// To regenerate this file, run:
// hphp/hack/src/oxidized_regen.sh
Expand Down Expand Up @@ -1696,6 +1696,8 @@ pub struct Class_<'a, Ex, En> {
#[serde(deserialize_with = "arena_deserializer::arena", borrow)]
pub file_attributes: &'a [&'a FileAttribute<'a, Ex, En>],
#[serde(deserialize_with = "arena_deserializer::arena", borrow)]
pub docs_url: Option<&'a str>,
#[serde(deserialize_with = "arena_deserializer::arena", borrow)]
pub enum_: Option<&'a Enum_<'a>>,
#[serde(deserialize_with = "arena_deserializer::arena", borrow)]
pub doc_comment: Option<&'a DocComment<'a>>,
Expand Down
42 changes: 42 additions & 0 deletions hphp/hack/src/parser/lowerer/lowerer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4236,6 +4236,43 @@ fn p_user_attributes<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::User
Ok(attributes.into_iter().flatten().collect())
}

/// Extract the URL in `<<__Docs("http://example.com")>>` if the __Docs attribute
/// is present.
fn p_docs_url<'a>(attrs: &[ast::UserAttribute], env: &mut Env<'a>) -> Option<String> {
let mut url = None;

for attr in attrs {
if attr.name.1 == naming_special_names_rust::user_attributes::DOCS {
match attr.params.as_slice() {
[param] => match &param.2 {
ast::Expr_::String(s) => match String::from_utf8(s.to_vec()) {
Ok(s) => {
url = Some(s);
}
Err(_) => raise_parsing_error_pos(
&attr.name.0,
env,
"`__Docs` URLs must be valid UTF-8",
),
},
_ => raise_parsing_error_pos(
&attr.name.0,
env,
"`__Docs` URLs must be a string literal",
),
},
_ => {
// Wrong number of arguments to __Docs,
// ignore. The attribute arity checks will tell
// the user their code is wrong.
}
}
}
}

url
}

fn map_yielding<'a, F, R>(node: S<'a>, env: &mut Env<'a>, p: F) -> Result<(R, bool)>
where
F: FnOnce(S<'a>, &mut Env<'a>) -> Result<R>,
Expand Down Expand Up @@ -5124,6 +5161,8 @@ fn p_def<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Def>> {
let env = env.as_mut();
let mode = env.file_mode();
let user_attributes = p_user_attributes(&c.attribute, env)?;
let docs_url = p_docs_url(&user_attributes, env);

let kinds = p_kinds(&c.modifiers, env)?;
let final_ = kinds.has(modifier::FINAL);
let is_xhp = matches!(
Expand Down Expand Up @@ -5186,6 +5225,7 @@ fn p_def<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Def>> {
emit_id: None,
internal: kinds.has(modifier::INTERNAL),
module: None,
docs_url,
};
match &c.body.children {
ClassishBody(c1) => {
Expand Down Expand Up @@ -5385,6 +5425,7 @@ fn p_def<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Def>> {
emit_id: None,
internal: kinds.has(modifier::INTERNAL),
module: None,
docs_url: None,
})])
}

Expand Down Expand Up @@ -5459,6 +5500,7 @@ fn p_def<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Def>> {
emit_id: None,
internal: kinds.has(modifier::INTERNAL),
module: None,
docs_url: None,
};

for n in c.elements.syntax_node_to_list_skip_separator() {
Expand Down
1 change: 1 addition & 0 deletions hphp/hack/src/rupro/hackrs/typing/ast/typing_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ impl<R: Reason> Infer<R> for oxidized::aast::Class_<(), ()> {
// TODO(T116039119): Populate value with presence of internal attribute
internal: false,
module: None,
docs_url: None,
};
Ok(res)
}
Expand Down
6 changes: 6 additions & 0 deletions hphp/hack/src/typing/nast_check/attribute_nast_checks.ml
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,10 @@ let handler =
Errors.add_nast_check_error
@@ Nast_check_error.Variadic_memoize p.param_pos
| None -> ()

method! at_class_ _env c =
check_attribute_arity
c.c_user_attributes
SN.UserAttributes.uaDocs
(`Exact 1)
end
1 change: 1 addition & 0 deletions hphp/hack/src/typing/typing_class.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,7 @@ let class_def_ env c tc =
Aast.c_emit_id = c.c_emit_id;
Aast.c_internal = c.c_internal;
Aast.c_module = c.c_module;
Aast.c_docs_url = c.c_docs_url;
},
global_inference_envs )

Expand Down
1 change: 1 addition & 0 deletions hphp/hack/src/typing/typing_type_wellformedness.ml
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ let class_ tenv c =
c_emit_id = _;
c_internal = _;
c_module = _;
c_docs_url = _;
} =
c
in
Expand Down
1 change: 1 addition & 0 deletions hphp/hack/src/typing/typing_variance.ml
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,7 @@ let class_def : Typing_env_types.env -> Nast.class_ -> unit =
c_doc_comment = _;
c_emit_id = _;
c_internal = _;
c_docs_url = _;
} =
class_
in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
__ConsistentConstruct built-in attribute
__Docs built-in attribute
__DynamicallyConstructible built-in attribute
__EnableMethodTraitDiamond built-in attribute
__Sealed built-in attribute
5 changes: 3 additions & 2 deletions hphp/hack/test/holes/assign_class_get.php.holes.exp
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,6 @@
{ Namespace_env.ns_ns_uses = <opaque>; ns_class_uses = <opaque>;
ns_fun_uses = <opaque>; ns_const_uses = <opaque>; ns_name = None;
ns_auto_ns_map = []; ns_is_codegen = false; ns_disable_xhp_element_mangling = false };
c_user_attributes = []; c_file_attributes = []; c_enum = None;
c_doc_comment = None; c_emit_id = None; c_internal = false; c_module = None })
c_user_attributes = []; c_file_attributes = []; c_docs_url = None;
c_enum = None; c_doc_comment = None; c_emit_id = None; c_internal = false;
c_module = None })
5 changes: 3 additions & 2 deletions hphp/hack/test/holes/assign_obj_get.php.holes.exp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
{ Namespace_env.ns_ns_uses = <opaque>; ns_class_uses = <opaque>;
ns_fun_uses = <opaque>; ns_const_uses = <opaque>; ns_name = None;
ns_auto_ns_map = []; ns_is_codegen = false; ns_disable_xhp_element_mangling = false };
c_user_attributes = []; c_file_attributes = []; c_enum = None;
c_doc_comment = None; c_emit_id = None; c_internal = false; c_module = None });
c_user_attributes = []; c_file_attributes = []; c_docs_url = None;
c_enum = None; c_doc_comment = None; c_emit_id = None; c_internal = false;
c_module = None });
(Fun
{ fd_namespace =
{ Namespace_env.ns_ns_uses = <opaque>; ns_class_uses = <opaque>;
Expand Down
5 changes: 3 additions & 2 deletions hphp/hack/test/holes/call_collection.php.holes.exp