1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use std::collections::BTreeMap;
11 collections::{String, Vec},
15 use hh_autoimport_rust as hh_autoimport;
16 use naming_special_names_rust as naming_special_names;
18 use arena_collections::{AssocListMut, List, MultiSetMut};
19 use flatten_smart_constructors::{FlattenOp, FlattenSmartConstructors};
20 use namespaces::ElaborateKind;
21 use namespaces_rust as namespaces;
22 use oxidized_by_ref::{
25 Abstraction, Bop, ClassishKind, ConstraintKind, FunKind, Id, ShapeFieldName, Uop, Variance,
28 decl_parser_options::DeclParserOptions,
29 direct_decl_parser::Decls,
31 method_flags::MethodFlags,
32 namespace_env::Env as NamespaceEnv,
35 prop_flags::PropFlags,
36 relative_path::RelativePath,
39 self, Decl, ShallowClassConst, ShallowMethod, ShallowProp, ShallowTypeconst,
41 shape_map::ShapeField,
42 t_shape_map::TShapeField,
44 self, AbstractTypeconst, Capability::*, ClassConstKind, ConcreteTypeconst, ConstDecl,
45 Enforcement, EnumType, FunArity, FunElt, FunImplicitParams, FunParam, FunParams, FunType,
46 IfcFunDecl, ParamMode, PosByteString, PosId, PosString, PossiblyEnforcedTy, RecordFieldReq,
47 ShapeFieldType, ShapeKind, TaccessType, Tparam, TshapeFieldName, Ty, Ty_, Typeconst,
48 TypedefType, WhereConstraint, XhpAttrTag,
50 typing_defs_flags::{FunParamFlags, FunTypeFlags},
51 typing_modules::Module_,
52 typing_reason::Reason,
54 use parser_core_types::{
55 compact_token::CompactToken, indexed_source_text::IndexedSourceText, source_text::SourceText,
56 syntax_kind::SyntaxKind, token_factory::SimpleTokenFactoryImpl, token_kind::TokenKind,
59 mod direct_decl_smart_constructors_generated;
63 type SSet<'a> = arena_collections::SortedSet<'a, &'a str>;
66 pub struct DirectDeclSmartConstructors<'a, 'text, S: SourceTextAllocator<'text, 'a>> {
67 pub token_factory: SimpleTokenFactoryImpl<CompactToken>,
69 pub source_text: IndexedSourceText<'text>,
70 pub arena: &'a bumpalo::Bump,
72 pub file_attributes: List<'a, &'a typing_defs::UserAttribute<'a>>,
73 // const_refs will accumulate all scope-resolution-expressions it enconuters while it's "Some"
74 const_refs: Option<arena_collections::set::Set<'a, typing_defs::ClassConstRef<'a>>>,
75 opts: &'a DeclParserOptions<'a>,
76 filename: &'a RelativePath<'a>,
78 namespace_builder: Rc<NamespaceBuilder<'a>>,
79 classish_name_builder: ClassishNameBuilder<'a>,
80 type_parameters: Rc<Vec<'a, SSet<'a>>>,
81 retain_or_omit_user_attributes_for_facts: bool,
82 simplify_naming_for_facts: bool,
83 previous_token_kind: TokenKind,
85 source_text_allocator: S,
88 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> DirectDeclSmartConstructors<'a, 'text, S> {
90 opts: &'a DeclParserOptions<'a>,
91 src: &SourceText<'text>,
94 source_text_allocator: S,
95 retain_or_omit_user_attributes_for_facts: bool,
96 simplify_naming_for_facts: bool,
98 let source_text = IndexedSourceText::new(src.clone());
99 let path = source_text.source_text().file_path();
100 let prefix = path.prefix();
101 let path = String::from_str_in(path.path_str(), arena).into_bump_str();
102 let filename = RelativePath::make(prefix, path);
104 token_factory: SimpleTokenFactoryImpl::new(),
109 filename: arena.alloc(filename),
111 decls: Decls::empty(),
112 file_attributes: List::empty(),
114 namespace_builder: Rc::new(NamespaceBuilder::new_in(
115 opts.auto_namespace_map,
116 opts.disable_xhp_element_mangling,
117 simplify_naming_for_facts,
120 classish_name_builder: ClassishNameBuilder::new(),
121 type_parameters: Rc::new(Vec::new_in(arena)),
122 // EndOfFile is used here as a None value (signifying "beginning of
123 // file") to save space. There is no legitimate circumstance where
124 // we would parse a token and the previous token kind would be
126 previous_token_kind: TokenKind::EndOfFile,
127 source_text_allocator,
128 retain_or_omit_user_attributes_for_facts,
129 simplify_naming_for_facts,
134 pub fn alloc<T>(&self, val: T) -> &'a T {
135 self.arena.alloc(val)
138 fn qualified_name_from_parts(&self, parts: &'a [Node<'a>], pos: &'a Pos<'a>) -> Id<'a> {
139 // Count the length of the qualified name, so that we can allocate
140 // exactly the right amount of space for it in our arena.
144 Node::Name(&(name, _)) => len += name.len(),
145 Node::Token(t) if t.kind() == TokenKind::Backslash => len += 1,
146 Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => len += name.len() + 1,
147 Node::ListItem(&(Node::Token(t), _backslash))
148 if t.kind() == TokenKind::Namespace =>
150 len += t.width() + 1;
155 // If there's no internal trivia, then we can just reference the
156 // qualified name in the original source text instead of copying it.
157 let source_len = pos.end_cnum() - pos.start_cnum();
158 if source_len == len {
159 let qualified_name: &'a str = self.str_from_utf8(self.source_text_at_pos(pos));
160 return Id(pos, qualified_name);
162 // Allocate `len` bytes and fill them with the fully qualified name.
163 let mut qualified_name = String::with_capacity_in(len, self.arena);
166 Node::Name(&(name, _pos)) => qualified_name.push_str(&name),
167 Node::Token(t) if t.kind() == TokenKind::Backslash => qualified_name.push('\\'),
168 &Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => {
169 qualified_name.push_str(&name);
170 qualified_name.push_str("\\");
172 &Node::ListItem(&(Node::Token(t), _backslash))
173 if t.kind() == TokenKind::Namespace =>
175 qualified_name.push_str("namespace\\");
180 debug_assert_eq!(len, qualified_name.len());
181 debug_assert_eq!(len, qualified_name.capacity());
182 Id(pos, qualified_name.into_bump_str())
185 /// If the given node is an identifier, XHP name, or qualified name,
186 /// elaborate it in the current namespace and return Some. To be used for
187 /// the name of a decl in its definition (e.g., "C" in `class C {}` or "f"
188 /// in `function f() {}`).
189 fn elaborate_defined_id(&self, name: Node<'a>) -> Option<Id<'a>> {
190 let id = match name {
191 Node::Name(&(name, pos)) => Id(pos, name),
192 Node::XhpName(&(name, pos)) => Id(pos, name),
193 Node::QualifiedName(&(parts, pos)) => self.qualified_name_from_parts(parts, pos),
194 // This is always an error; e.g. using a reserved word where a name
196 Node::Token(t) | Node::IgnoredToken(t) => {
197 let pos = self.token_pos(t);
198 let text = self.str_from_utf8(self.source_text_at_pos(pos));
203 Some(self.namespace_builder.elaborate_defined_id(id))
206 /// If the given node is a name (i.e., an identifier or a qualified name),
207 /// return Some. No namespace elaboration is performed.
208 fn expect_name(&self, name: Node<'a>) -> Option<Id<'a>> {
209 // If it's a simple identifier, return it.
210 if let id @ Some(_) = name.as_id() {
214 Node::QualifiedName(&(parts, pos)) => Some(self.qualified_name_from_parts(parts, pos)),
215 // The IgnoredToken case is always an error; e.g. using a reserved
216 // word where a name is expected. The Token case is not an error if
217 // the token is TokenKind::XHP (which is legal to use as a name),
218 // but an error otherwise (since we expect a Name or QualifiedName
219 // here, and the Name case would have been handled in `as_id`
221 Node::Token(t) | Node::IgnoredToken(t) => {
222 let pos = self.token_pos(t);
223 let text = self.str_from_utf8(self.source_text_at_pos(pos));
230 /// Fully qualify the given identifier as a type name (with consideration
231 /// to `use` statements in scope).
232 fn elaborate_id(&self, id: Id<'a>) -> Id<'a> {
233 let Id(pos, name) = id;
234 Id(pos, self.elaborate_raw_id(name))
237 /// Fully qualify the given identifier as a type name (with consideration
238 /// to `use` statements in scope).
239 fn elaborate_raw_id(&self, id: &'a str) -> &'a str {
240 self.namespace_builder
241 .elaborate_raw_id(ElaborateKind::Class, id)
244 /// Fully qualify the given identifier as a constant name (with
245 /// consideration to `use` statements in scope).
246 fn elaborate_const_id(&self, id: Id<'a>) -> Id<'a> {
247 let Id(pos, name) = id;
250 self.namespace_builder
251 .elaborate_raw_id(ElaborateKind::Const, name),
255 fn slice<T>(&self, iter: impl Iterator<Item = T>) -> &'a [T] {
256 let mut result = match iter.size_hint().1 {
257 Some(upper_bound) => Vec::with_capacity_in(upper_bound, self.arena),
258 None => Vec::new_in(self.arena),
263 result.into_bump_slice()
266 fn start_accumulating_const_refs(&mut self) {
267 self.const_refs = Some(arena_collections::set::Set::empty());
270 fn accumulate_const_ref(&mut self, class_id: &'a aast::ClassId<'_, (), ()>, value_id: &Id<'a>) {
271 // The decl for a class constant stores a list of all the scope-resolution expressions
272 // it contains. For example "const C=A::X" stores A::X, and "const D=self::Y" stores self::Y.
273 // (This is so we can detect cross-type circularity in constant initializers).
274 // TODO: Hack is the wrong place to detect circularity (because we can never do it completely soundly,
275 // and because it's a cross-body problem). The right place to do it is in a linter. All this should be
276 // removed from here and put into a linter.
277 if let Some(const_refs) = self.const_refs {
279 nast::ClassId_::CI(sid) => {
280 self.const_refs = Some(const_refs.add(
282 typing_defs::ClassConstRef(
283 typing_defs::ClassConstFrom::From(sid.1),
288 nast::ClassId_::CIself => {
289 self.const_refs = Some(const_refs.add(
291 typing_defs::ClassConstRef(typing_defs::ClassConstFrom::Self_, value_id.1),
295 nast::ClassId_::CIparent
296 | nast::ClassId_::CIstatic
297 | nast::ClassId_::CIexpr(_) => {}
302 fn stop_accumulating_const_refs(&mut self) -> &'a [typing_defs::ClassConstRef<'a>] {
303 let const_refs = self.const_refs;
304 self.const_refs = None;
306 Some(const_refs) => {
307 let mut elements: Vec<'_, typing_defs::ClassConstRef<'_>> =
308 bumpalo::collections::Vec::with_capacity_in(const_refs.count(), self.arena);
309 elements.extend(const_refs.into_iter());
310 elements.into_bump_slice()
317 pub trait SourceTextAllocator<'text, 'target>: Clone {
318 fn alloc(&self, text: &'text str) -> &'target str;
322 pub struct NoSourceTextAllocator;
324 impl<'text> SourceTextAllocator<'text, 'text> for NoSourceTextAllocator {
326 fn alloc(&self, text: &'text str) -> &'text str {
332 pub struct ArenaSourceTextAllocator<'arena>(pub &'arena bumpalo::Bump);
334 impl<'text, 'arena> SourceTextAllocator<'text, 'arena> for ArenaSourceTextAllocator<'arena> {
336 fn alloc(&self, text: &'text str) -> &'arena str {
337 self.0.alloc_str(text)
341 fn prefix_slash<'a>(arena: &'a Bump, name: &str) -> &'a str {
342 let mut s = String::with_capacity_in(1 + name.len(), arena);
348 fn prefix_colon<'a>(arena: &'a Bump, name: &str) -> &'a str {
349 let mut s = String::with_capacity_in(1 + name.len(), arena);
355 fn concat<'a>(arena: &'a Bump, str1: &str, str2: &str) -> &'a str {
356 let mut result = String::with_capacity_in(str1.len() + str2.len(), arena);
357 result.push_str(str1);
358 result.push_str(str2);
359 result.into_bump_str()
362 fn strip_dollar_prefix<'a>(name: &'a str) -> &'a str {
363 name.trim_start_matches("$")
366 const TANY_: Ty_<'_> = Ty_::Tany(oxidized_by_ref::tany_sentinel::TanySentinel);
367 const TANY: &Ty<'_> = &Ty(Reason::none(), TANY_);
369 fn tany() -> &'static Ty<'static> {
373 fn default_ifc_fun_decl<'a>() -> IfcFunDecl<'a> {
374 IfcFunDecl::FDPolicied(Some("PUBLIC"))
380 visibility: aast::Visibility,
386 fn read_member_modifiers<'a: 'b, 'b>(modifiers: impl Iterator<Item = &'b Node<'a>>) -> Modifiers {
387 let mut ret = Modifiers {
389 visibility: aast::Visibility::Public,
394 for modifier in modifiers {
395 if let Some(vis) = modifier.as_visibility() {
396 ret.visibility = vis;
398 match modifier.token_kind() {
399 Some(TokenKind::Static) => ret.is_static = true,
400 Some(TokenKind::Abstract) => ret.is_abstract = true,
401 Some(TokenKind::Final) => ret.is_final = true,
402 Some(TokenKind::Readonly) => ret.is_readonly = true,
409 #[derive(Clone, Debug)]
410 struct NamespaceBuilder<'a> {
412 stack: Vec<'a, NamespaceEnv<'a>>,
413 auto_ns_map: &'a [(&'a str, &'a str)],
414 simplify_naming_for_facts: bool,
417 impl<'a> NamespaceBuilder<'a> {
419 auto_ns_map: &'a [(&'a str, &'a str)],
420 disable_xhp_element_mangling: bool,
421 simplify_naming_for_facts: bool,
424 let mut ns_uses = SMap::empty();
425 for &alias in hh_autoimport::NAMESPACES {
426 ns_uses = ns_uses.add(arena, alias, concat(arena, "HH\\", alias));
428 for (alias, ns) in auto_ns_map.iter() {
429 ns_uses = ns_uses.add(arena, alias, ns);
432 let mut class_uses = SMap::empty();
433 for &alias in hh_autoimport::TYPES {
434 class_uses = class_uses.add(arena, alias, concat(arena, "HH\\", alias));
439 stack: bumpalo::vec![in arena; NamespaceEnv {
442 fun_uses: SMap::empty(),
443 const_uses: SMap::empty(),
444 record_def_uses: SMap::empty(),
448 disable_xhp_element_mangling,
451 simplify_naming_for_facts,
455 fn push_namespace(&mut self, name: Option<&str>) {
456 let current = self.current_namespace();
457 let nsenv = self.stack.last().unwrap().clone(); // shallow clone
458 if let Some(name) = name {
459 let mut fully_qualified = match current {
460 None => String::with_capacity_in(name.len(), self.arena),
462 let mut fully_qualified =
463 String::with_capacity_in(current.len() + name.len() + 1, self.arena);
464 fully_qualified.push_str(current);
465 fully_qualified.push('\\');
469 fully_qualified.push_str(name);
470 self.stack.push(NamespaceEnv {
471 name: Some(fully_qualified.into_bump_str()),
475 self.stack.push(NamespaceEnv {
482 fn pop_namespace(&mut self) {
483 // We'll never push a namespace for a declaration of items in the global
484 // namespace (e.g., `namespace { ... }`), so only pop if we are in some
485 // namespace other than the global one.
486 if self.stack.len() > 1 {
487 self.stack.pop().unwrap();
491 // push_namespace(Y) + pop_namespace() + push_namespace(X) should be equivalent to
492 // push_namespace(Y) + push_namespace(X) + pop_previous_namespace()
493 fn pop_previous_namespace(&mut self) {
494 if self.stack.len() > 2 {
495 let last = self.stack.pop().unwrap().name.unwrap_or("\\");
496 let previous = self.stack.pop().unwrap().name.unwrap_or("\\");
497 assert!(last.starts_with(previous));
498 let name = &last[previous.len() + 1..last.len()];
499 self.push_namespace(Some(name));
503 fn current_namespace(&self) -> Option<&'a str> {
504 self.stack.last().and_then(|nsenv| nsenv.name)
507 fn add_import(&mut self, kind: NamespaceUseKind, name: &'a str, aliased_name: Option<&'a str>) {
508 let stack_top = &mut self
511 .expect("Attempted to get the current import map, but namespace stack was empty");
512 let aliased_name = aliased_name.unwrap_or_else(|| {
513 name.rsplit_terminator('\\')
515 .expect("Expected at least one entry in import name")
517 let name = name.trim_end_matches('\\');
518 let name = if name.starts_with('\\') {
521 prefix_slash(self.arena, name)
524 NamespaceUseKind::Type => {
525 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
527 NamespaceUseKind::Namespace => {
528 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
530 NamespaceUseKind::Mixed => {
531 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
532 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
537 fn elaborate_raw_id(&self, kind: ElaborateKind, name: &'a str) -> &'a str {
538 if name.starts_with('\\') {
541 let env = self.stack.last().unwrap();
542 namespaces::elaborate_raw_id_in(self.arena, env, kind, name, self.simplify_naming_for_facts)
545 fn elaborate_defined_id(&self, id: Id<'a>) -> Id<'a> {
546 let Id(pos, name) = id;
547 let env = self.stack.last().unwrap();
548 let name = if env.disable_xhp_element_mangling && name.contains(':') {
549 let xhp_name_opt = namespaces::elaborate_xhp_namespace(name);
550 let name = xhp_name_opt.map_or(name, |s| self.arena.alloc_str(&s));
551 if !name.starts_with('\\') {
552 namespaces::elaborate_into_current_ns_in(self.arena, env, name)
557 namespaces::elaborate_into_current_ns_in(self.arena, env, name)
563 #[derive(Clone, Debug)]
564 enum ClassishNameBuilder<'a> {
565 /// We are not in a classish declaration.
568 /// We saw a classish keyword token followed by a Name, so we make it
569 /// available as the name of the containing class declaration.
570 InClassish(&'a (&'a str, &'a Pos<'a>, TokenKind)),
573 impl<'a> ClassishNameBuilder<'a> {
575 ClassishNameBuilder::NotInClassish
578 fn lexed_name_after_classish_keyword(
583 token_kind: TokenKind,
585 use ClassishNameBuilder::*;
588 let name = if name.starts_with(':') {
589 prefix_slash(arena, name)
593 *self = InClassish(arena.alloc((name, pos, token_kind)))
599 fn parsed_classish_declaration(&mut self) {
600 *self = ClassishNameBuilder::NotInClassish;
603 fn get_current_classish_name(&self) -> Option<(&'a str, &'a Pos<'a>)> {
604 use ClassishNameBuilder::*;
606 NotInClassish => None,
607 InClassish((name, pos, _)) => Some((name, pos)),
611 fn in_interface(&self) -> bool {
612 use ClassishNameBuilder::*;
614 InClassish((_, _, TokenKind::Interface)) => true,
615 InClassish((_, _, _)) | NotInClassish => false,
621 pub struct FunParamDecl<'a> {
622 attributes: Node<'a>,
623 visibility: Node<'a>,
628 name: Option<&'a str>,
630 initializer: Node<'a>,
634 pub struct FunctionHeader<'a> {
637 type_params: Node<'a>,
638 param_list: Node<'a>,
639 capability: Node<'a>,
641 readonly_return: Node<'a>,
642 where_constraints: Node<'a>,
646 pub struct RequireClause<'a> {
647 require_type: Node<'a>,
652 pub struct TypeParameterDecl<'a> {
654 reified: aast::ReifyKind,
656 constraints: &'a [(ConstraintKind, Node<'a>)],
657 tparam_params: &'a [&'a Tparam<'a>],
658 user_attributes: &'a [&'a UserAttributeNode<'a>],
662 pub struct ClosureTypeHint<'a> {
668 pub struct NamespaceUseClause<'a> {
669 kind: NamespaceUseKind,
671 as_: Option<&'a str>,
674 #[derive(Copy, Clone, Debug)]
675 enum NamespaceUseKind {
682 pub struct ConstructorNode<'a> {
683 method: &'a ShallowMethod<'a>,
684 properties: &'a [ShallowProp<'a>],
688 pub struct MethodNode<'a> {
689 method: &'a ShallowMethod<'a>,
694 pub struct PropertyNode<'a> {
695 decls: &'a [ShallowProp<'a>],
700 pub struct XhpClassAttributeDeclarationNode<'a> {
701 xhp_attr_enum_values: &'a [(&'a str, &'a [XhpEnumValue<'a>])],
702 xhp_attr_decls: &'a [ShallowProp<'a>],
703 xhp_attr_uses_decls: &'a [Node<'a>],
707 pub struct XhpClassAttributeNode<'a> {
709 tag: Option<XhpAttrTag>,
716 pub struct ShapeFieldNode<'a> {
717 name: &'a ShapeField<'a>,
718 type_: &'a ShapeFieldType<'a>,
721 #[derive(Copy, Clone, Debug)]
722 struct ClassNameParam<'a> {
724 full_pos: &'a Pos<'a>, // Position of the full expression `Foo::class`
728 pub struct UserAttributeNode<'a> {
730 classname_params: &'a [ClassNameParam<'a>],
731 string_literal_params: &'a [&'a BStr], // this is only used for __Deprecated attribute message and Cipp parameters
734 mod fixed_width_token {
735 use parser_core_types::token_kind::TokenKind;
737 #[derive(Copy, Clone)]
738 pub struct FixedWidthToken(u64); // { offset: u56, kind: TokenKind }
740 const KIND_BITS: u8 = 8;
741 const KIND_MASK: u64 = u8::MAX as u64;
742 const MAX_OFFSET: u64 = !(KIND_MASK << (64 - KIND_BITS));
744 impl FixedWidthToken {
745 pub fn new(kind: TokenKind, offset: usize) -> Self {
746 // We don't want to spend bits tracking the width of fixed-width
747 // tokens. Since we don't track width, verify that this token kind
748 // is in fact a fixed-width kind.
749 debug_assert!(kind.fixed_width().is_some());
751 let offset: u64 = offset.try_into().unwrap();
752 if offset > MAX_OFFSET {
753 panic!("FixedWidthToken: offset too large: {}", offset);
755 Self(offset << KIND_BITS | kind as u8 as u64)
758 pub fn offset(self) -> usize {
759 (self.0 >> KIND_BITS).try_into().unwrap()
762 pub fn kind(self) -> TokenKind {
763 TokenKind::try_from_u8(self.0 as u8).unwrap()
766 pub fn width(self) -> usize {
767 self.kind().fixed_width().unwrap().get()
771 impl std::fmt::Debug for FixedWidthToken {
772 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
773 fmt.debug_struct("FixedWidthToken")
774 .field("kind", &self.kind())
775 .field("offset", &self.offset())
780 use fixed_width_token::FixedWidthToken;
782 #[derive(Copy, Clone, Debug)]
784 // Nodes which are not useful in constructing a decl are ignored. We keep
785 // track of the SyntaxKind for two reasons.
787 // One is that the parser needs to know the SyntaxKind of a parsed node in
788 // some circumstances (this information is exposed to the parser via an
789 // implementation of `smart_constructors::NodeType`). An adapter called
790 // WithKind exists to provide a `NodeType` implementation for arbitrary
791 // nodes by pairing each node with a SyntaxKind, but in the direct decl
792 // parser, we want to avoid the extra 8 bytes of overhead on each node.
794 // The second reason is that debugging is difficult when nodes are silently
795 // ignored, and providing at least the SyntaxKind of an ignored node helps
796 // in tracking down the reason it was ignored.
799 // For tokens with a fixed width (like `using`), we also keep its offset in
800 // the source text, so that we can reference the text of the token if it's
801 // (erroneously) used as an identifier (e.g., `function using() {}`).
802 IgnoredToken(FixedWidthToken),
804 List(&'a &'a [Node<'a>]),
805 BracketedList(&'a (&'a Pos<'a>, &'a [Node<'a>], &'a Pos<'a>)),
806 Name(&'a (&'a str, &'a Pos<'a>)),
807 XhpName(&'a (&'a str, &'a Pos<'a>)),
808 Variable(&'a (&'a str, &'a Pos<'a>)),
809 QualifiedName(&'a (&'a [Node<'a>], &'a Pos<'a>)),
810 StringLiteral(&'a (&'a BStr, &'a Pos<'a>)), // For shape keys and const expressions.
811 IntLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
812 FloatingLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
813 BooleanLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
815 XhpEnumTy(&'a (&'a Ty<'a>, &'a [XhpEnumValue<'a>])),
816 ListItem(&'a (Node<'a>, Node<'a>)),
817 Const(&'a ShallowClassConst<'a>), // For the "X=1" in enums "enum E {X=1}" and enum-classes "enum class C {int X=1}", and also for consts via make_const_declaration
818 ConstInitializer(&'a (Node<'a>, Node<'a>, &'a [typing_defs::ClassConstRef<'a>])), // Stores (X,1,refs) for "X=1" in top-level "const int X=1" and class-const "public const int X=1".
819 FunParam(&'a FunParamDecl<'a>),
820 Attribute(&'a UserAttributeNode<'a>),
821 FunctionHeader(&'a FunctionHeader<'a>),
822 Constructor(&'a ConstructorNode<'a>),
823 Method(&'a MethodNode<'a>),
824 Property(&'a PropertyNode<'a>),
825 EnumUse(&'a Node<'a>),
826 TraitUse(&'a Node<'a>),
827 XhpClassAttributeDeclaration(&'a XhpClassAttributeDeclarationNode<'a>),
828 XhpClassAttribute(&'a XhpClassAttributeNode<'a>),
829 XhpAttributeUse(&'a Node<'a>),
830 TypeConstant(&'a ShallowTypeconst<'a>),
831 ContextConstraint(&'a (ConstraintKind, Node<'a>)),
832 RequireClause(&'a RequireClause<'a>),
833 ClassishBody(&'a &'a [Node<'a>]),
834 TypeParameter(&'a TypeParameterDecl<'a>),
835 TypeConstraint(&'a (ConstraintKind, Node<'a>)),
836 ShapeFieldSpecifier(&'a ShapeFieldNode<'a>),
837 NamespaceUseClause(&'a NamespaceUseClause<'a>),
838 Expr(&'a nast::Expr<'a>),
839 TypeParameters(&'a &'a [&'a Tparam<'a>]),
840 WhereConstraint(&'a WhereConstraint<'a>),
841 RecordField(&'a (Id<'a>, RecordFieldReq)),
843 // Non-ignored, fixed-width tokens (e.g., keywords, operators, braces, etc.).
844 Token(FixedWidthToken),
847 impl<'a> smart_constructors::NodeType for Node<'a> {
850 fn extract(self) -> Self::R {
854 fn is_abstract(&self) -> bool {
855 self.is_token(TokenKind::Abstract) || self.is_ignored_token_with_kind(TokenKind::Abstract)
857 fn is_name(&self) -> bool {
858 matches!(self, Node::Name(..)) || self.is_ignored_token_with_kind(TokenKind::Name)
860 fn is_qualified_name(&self) -> bool {
861 matches!(self, Node::QualifiedName(..)) || matches!(self, Node::Ignored(SK::QualifiedName))
863 fn is_prefix_unary_expression(&self) -> bool {
864 matches!(self, Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(..))))
865 || matches!(self, Node::Ignored(SK::PrefixUnaryExpression))
867 fn is_scope_resolution_expression(&self) -> bool {
870 Node::Expr(aast::Expr(_, _, aast::Expr_::ClassConst(..)))
871 ) || matches!(self, Node::Ignored(SK::ScopeResolutionExpression))
873 fn is_missing(&self) -> bool {
874 matches!(self, Node::Ignored(SK::Missing))
876 fn is_variable_expression(&self) -> bool {
877 matches!(self, Node::Ignored(SK::VariableExpression))
879 fn is_subscript_expression(&self) -> bool {
880 matches!(self, Node::Ignored(SK::SubscriptExpression))
882 fn is_member_selection_expression(&self) -> bool {
883 matches!(self, Node::Ignored(SK::MemberSelectionExpression))
885 fn is_object_creation_expression(&self) -> bool {
886 matches!(self, Node::Ignored(SK::ObjectCreationExpression))
888 fn is_safe_member_selection_expression(&self) -> bool {
889 matches!(self, Node::Ignored(SK::SafeMemberSelectionExpression))
891 fn is_function_call_expression(&self) -> bool {
892 matches!(self, Node::Ignored(SK::FunctionCallExpression))
894 fn is_list_expression(&self) -> bool {
895 matches!(self, Node::Ignored(SK::ListExpression))
900 fn is_token(self, kind: TokenKind) -> bool {
901 self.token_kind() == Some(kind)
904 fn token_kind(self) -> Option<TokenKind> {
906 Node::Token(token) => Some(token.kind()),
911 fn is_ignored_token_with_kind(self, kind: TokenKind) -> bool {
913 Node::IgnoredToken(token) => token.kind() == kind,
918 fn as_slice(self, b: &'a Bump) -> &'a [Self] {
920 Node::List(&items) | Node::BracketedList(&(_, items, _)) => items,
921 n if n.is_ignored() => &[],
922 n => std::slice::from_ref(b.alloc(n)),
926 fn iter<'b>(&'b self) -> NodeIterHelper<'a, 'b>
931 &Node::List(&items) | Node::BracketedList(&(_, items, _)) => {
932 NodeIterHelper::Vec(items.iter())
934 n if n.is_ignored() => NodeIterHelper::Empty,
935 n => NodeIterHelper::Single(n),
939 // The number of elements which would be yielded by `self.iter()`.
940 // Must return the upper bound returned by NodeIterHelper::size_hint.
941 fn len(&self) -> usize {
943 &Node::List(&items) | Node::BracketedList(&(_, items, _)) => items.len(),
944 n if n.is_ignored() => 0,
949 fn as_visibility(&self) -> Option<aast::Visibility> {
950 match self.token_kind() {
951 Some(TokenKind::Private) => Some(aast::Visibility::Private),
952 Some(TokenKind::Protected) => Some(aast::Visibility::Protected),
953 Some(TokenKind::Public) => Some(aast::Visibility::Public),
958 // If this node is a simple unqualified identifier, return its position and text.
959 fn as_id(&self) -> Option<Id<'a>> {
961 Node::Name(&(name, pos)) | Node::XhpName(&(name, pos)) => Some(Id(pos, name)),
966 // If this node is a Variable token, return its position and text.
967 // As an attempt at error recovery (when the dollar sign is omitted), also
968 // return other unqualified identifiers (i.e., the Name token kind).
969 fn as_variable(&self) -> Option<Id<'a>> {
971 Node::Variable(&(name, pos)) | Node::Name(&(name, pos)) => Some(Id(pos, name)),
976 fn is_ignored(&self) -> bool {
977 matches!(self, Node::Ignored(..) | Node::IgnoredToken(..))
980 fn is_present(&self) -> bool {
986 struct Attributes<'a> {
987 deprecated: Option<&'a str>,
988 reifiable: Option<&'a Pos<'a>>,
994 enforceable: Option<&'a Pos<'a>>,
995 accept_disposable: bool,
996 dynamically_callable: bool,
997 returns_disposable: bool,
999 ifc_attribute: IfcFunDecl<'a>,
1004 support_dynamic_type: bool,
1005 module: Option<&'a Module_<'a>>,
1009 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> DirectDeclSmartConstructors<'a, 'text, S> {
1010 fn add_class(&mut self, name: &'a str, decl: &'a shallow_decl_defs::ShallowClass<'a>) {
1011 self.decls.add(name, Decl::Class(decl), self.arena);
1013 fn add_fun(&mut self, name: &'a str, decl: &'a typing_defs::FunElt<'a>) {
1014 self.decls.add(name, Decl::Fun(decl), self.arena);
1016 fn add_typedef(&mut self, name: &'a str, decl: &'a typing_defs::TypedefType<'a>) {
1017 self.decls.add(name, Decl::Typedef(decl), self.arena);
1019 fn add_const(&mut self, name: &'a str, decl: &'a typing_defs::ConstDecl<'a>) {
1020 self.decls.add(name, Decl::Const(decl), self.arena);
1022 fn add_record(&mut self, name: &'a str, decl: &'a typing_defs::RecordDefType<'a>) {
1023 self.decls.add(name, Decl::Record(decl), self.arena);
1027 fn concat(&self, str1: &str, str2: &str) -> &'a str {
1028 concat(self.arena, str1, str2)
1031 fn token_bytes(&self, token: &CompactToken) -> &'text [u8] {
1034 .sub(token.start_offset(), token.width())
1037 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1038 // the same data. Otherwise, copy the slice into our arena using
1039 // String::from_utf8_lossy_in, and return a reference to the arena str.
1040 fn str_from_utf8(&self, slice: &'text [u8]) -> &'a str {
1041 if let Ok(s) = std::str::from_utf8(slice) {
1042 self.source_text_allocator.alloc(s)
1044 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1048 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1049 // the same data. Otherwise, copy the slice into our arena using
1050 // String::from_utf8_lossy_in, and return a reference to the arena str.
1051 fn str_from_utf8_for_bytes_in_arena(&self, slice: &'a [u8]) -> &'a str {
1052 if let Ok(s) = std::str::from_utf8(slice) {
1055 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1061 pos1: impl Into<Option<&'a Pos<'a>>>,
1062 pos2: impl Into<Option<&'a Pos<'a>>>,
1064 match (pos1.into(), pos2.into()) {
1065 (None, None) => Pos::none(),
1066 (Some(pos), None) | (None, Some(pos)) => pos,
1067 (Some(pos1), Some(pos2)) => match (pos1.is_none(), pos2.is_none()) {
1068 (true, true) => Pos::none(),
1069 (true, false) => pos2,
1070 (false, true) => pos1,
1071 (false, false) => Pos::merge_without_checking_filename(self.arena, pos1, pos2),
1076 fn merge_positions(&self, node1: Node<'a>, node2: Node<'a>) -> &'a Pos<'a> {
1077 self.merge(self.get_pos(node1), self.get_pos(node2))
1080 fn pos_from_slice(&self, nodes: &[Node<'a>]) -> &'a Pos<'a> {
1081 nodes.iter().fold(Pos::none(), |acc, &node| {
1082 self.merge(acc, self.get_pos(node))
1086 fn get_pos(&self, node: Node<'a>) -> &'a Pos<'a> {
1087 self.get_pos_opt(node).unwrap_or(Pos::none())
1090 fn get_pos_opt(&self, node: Node<'a>) -> Option<&'a Pos<'a>> {
1091 let pos = match node {
1092 Node::Name(&(_, pos)) | Node::Variable(&(_, pos)) => pos,
1093 Node::Ty(ty) => return ty.get_pos(),
1094 Node::XhpName(&(_, pos)) => pos,
1095 Node::QualifiedName(&(_, pos)) => pos,
1096 Node::IntLiteral(&(_, pos))
1097 | Node::FloatingLiteral(&(_, pos))
1098 | Node::StringLiteral(&(_, pos))
1099 | Node::BooleanLiteral(&(_, pos)) => pos,
1100 Node::ListItem(&(fst, snd)) => self.merge_positions(fst, snd),
1101 Node::List(items) => self.pos_from_slice(&items),
1102 Node::BracketedList(&(first_pos, inner_list, second_pos)) => self.merge(
1104 self.merge(self.pos_from_slice(inner_list), second_pos),
1106 Node::Expr(&aast::Expr(_, pos, _)) => pos,
1107 Node::Token(token) => self.token_pos(token),
1110 if pos.is_none() { None } else { Some(pos) }
1113 fn token_pos(&self, token: FixedWidthToken) -> &'a Pos<'a> {
1114 let start = token.offset();
1115 let end = start + token.width();
1116 let start = self.source_text.offset_to_file_pos_triple(start);
1117 let end = self.source_text.offset_to_file_pos_triple(end);
1118 Pos::from_lnum_bol_cnum(self.arena, self.filename, start, end)
1121 fn node_to_expr(&self, node: Node<'a>) -> Option<&'a nast::Expr<'a>> {
1122 let expr_ = match node {
1123 Node::Expr(expr) => return Some(expr),
1124 Node::IntLiteral(&(s, _)) => aast::Expr_::Int(s),
1125 Node::FloatingLiteral(&(s, _)) => aast::Expr_::Float(s),
1126 Node::StringLiteral(&(s, _)) => aast::Expr_::String(s),
1127 Node::BooleanLiteral((s, _)) => {
1128 if s.eq_ignore_ascii_case("true") {
1134 Node::Token(t) if t.kind() == TokenKind::NullLiteral => aast::Expr_::Null,
1135 Node::Name(..) | Node::QualifiedName(..) => {
1136 aast::Expr_::Id(self.alloc(self.elaborate_const_id(self.expect_name(node)?)))
1140 let pos = self.get_pos(node);
1141 Some(self.alloc(aast::Expr((), pos, expr_)))
1144 fn node_to_non_ret_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1145 self.node_to_ty_(node, false)
1148 fn node_to_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1149 self.node_to_ty_(node, true)
1152 fn node_to_ty_(&self, node: Node<'a>, allow_non_ret_ty: bool) -> Option<&'a Ty<'a>> {
1154 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tvoid))) if !allow_non_ret_ty => {
1155 Some(self.alloc(Ty(reason, Ty_::Terr)))
1157 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tnoreturn))) if !allow_non_ret_ty => {
1158 Some(self.alloc(Ty(reason, Ty_::Terr)))
1160 Node::Ty(ty) => Some(ty),
1161 Node::Expr(expr) => {
1162 fn expr_to_ty<'a>(arena: &'a Bump, expr: &'a nast::Expr<'a>) -> Option<Ty_<'a>> {
1165 Null => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tnull))),
1166 This => Some(Ty_::Tthis),
1167 True | False => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tbool))),
1168 Int(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tint))),
1169 Float(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tfloat))),
1170 String(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1171 String2(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1172 PrefixedString(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1173 Unop(&(_op, expr)) => expr_to_ty(arena, expr),
1174 Hole(&(expr, _, _, _)) => expr_to_ty(arena, expr),
1176 ArrayGet(_) | As(_) | Await(_) | Binop(_) | Call(_) | Cast(_)
1177 | ClassConst(_) | ClassGet(_) | Clone(_) | Collection(_) | Darray(_)
1178 | Dollardollar(_) | Efun(_) | Eif(_) | EnumClassLabel(_) | ETSplice(_)
1179 | ExpressionTree(_) | FunctionPointer(_) | FunId(_) | Id(_) | Import(_)
1180 | Is(_) | KeyValCollection(_) | Lfun(_) | List(_) | Lplaceholder(_)
1181 | Lvar(_) | MethodCaller(_) | MethodId(_) | New(_) | ObjGet(_)
1182 | Omitted | Pair(_) | Pipe(_) | ReadonlyExpr(_) | Record(_) | Shape(_)
1183 | SmethodId(_) | Tuple(_) | Upcast(_) | ValCollection(_) | Varray(_)
1184 | Xml(_) | Yield(_) => None,
1188 self.alloc(Reason::witness_from_decl(expr.1)),
1189 expr_to_ty(self.arena, expr)?,
1192 Node::IntLiteral((_, pos)) => Some(self.alloc(Ty(
1193 self.alloc(Reason::witness_from_decl(pos)),
1194 Ty_::Tprim(self.alloc(aast::Tprim::Tint)),
1196 Node::FloatingLiteral((_, pos)) => Some(self.alloc(Ty(
1197 self.alloc(Reason::witness_from_decl(pos)),
1198 Ty_::Tprim(self.alloc(aast::Tprim::Tfloat)),
1200 Node::StringLiteral((_, pos)) => Some(self.alloc(Ty(
1201 self.alloc(Reason::witness_from_decl(pos)),
1202 Ty_::Tprim(self.alloc(aast::Tprim::Tstring)),
1204 Node::BooleanLiteral((_, pos)) => Some(self.alloc(Ty(
1205 self.alloc(Reason::witness_from_decl(pos)),
1206 Ty_::Tprim(self.alloc(aast::Tprim::Tbool)),
1208 Node::Token(t) if t.kind() == TokenKind::Varray => {
1209 let pos = self.token_pos(t);
1210 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1211 let ty_ = Ty_::Tapply(self.alloc((
1212 (self.token_pos(t), naming_special_names::collections::VEC),
1215 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1217 Node::Token(t) if t.kind() == TokenKind::Darray => {
1218 let pos = self.token_pos(t);
1219 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1220 let ty_ = Ty_::Tapply(self.alloc((
1221 (self.token_pos(t), naming_special_names::collections::DICT),
1222 self.alloc([tany, tany]),
1224 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1226 Node::Token(t) if t.kind() == TokenKind::This => {
1227 Some(self.alloc(Ty(self.alloc(Reason::hint(self.token_pos(t))), Ty_::Tthis)))
1229 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1230 let pos = self.token_pos(t);
1232 self.alloc(Reason::hint(pos)),
1233 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1236 // In coeffects contexts, we get types like `ctx $f` or `$v::C`.
1237 // Node::Variable is used for the `$f` and `$v`, so that we don't
1238 // incorrectly attempt to elaborate them as names.
1239 Node::Variable(&(name, pos)) => Some(self.alloc(Ty(
1240 self.alloc(Reason::hint(pos)),
1241 Ty_::Tapply(self.alloc(((pos, name), &[][..]))),
1244 let Id(pos, name) = self.expect_name(node)?;
1245 let reason = self.alloc(Reason::hint(pos));
1246 let ty_ = if self.is_type_param_in_scope(name) {
1247 // TODO (T69662957) must fill type args of Tgeneric
1248 Ty_::Tgeneric(self.alloc((name, &[])))
1251 "nothing" => Ty_::Tunion(&[]),
1253 if self.opts.everything_sdt {
1254 Ty_::Tsupportdynamic
1259 "dynamic" => Ty_::Tdynamic,
1260 "supportdynamic" => Ty_::Tsupportdynamic,
1261 "varray_or_darray" | "vec_or_dict" => {
1262 let key_type = self.vec_or_dict_key(pos);
1263 let value_type = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1264 Ty_::TvecOrDict(self.alloc((key_type, value_type)))
1268 let name = self.elaborate_raw_id(name);
1269 Ty_::Tapply(self.alloc(((pos, name), &[][..])))
1273 Some(self.alloc(Ty(reason, ty_)))
1278 fn rewrite_ty_for_global_inference(
1280 ty: Option<&'a Ty<'a>>,
1282 ) -> Option<&'a Ty<'a>> {
1283 if !self.opts.global_inference {
1286 let tvar = self.alloc(Ty(self.alloc(reason), Ty_::Tvar(0)));
1290 fn cut_namespace<'a>(id: &'a str) -> &'a str {
1291 id.rsplit_terminator('\\').next().unwrap()
1293 fn reinfer_type_to_string_opt<'a>(arena: &'a Bump, ty: Ty_<'a>) -> Option<&'a str> {
1295 Ty_::Tmixed => Some("mixed"),
1296 Ty_::Tnonnull => Some("nonnull"),
1297 Ty_::Tdynamic => Some("dynamic"),
1298 Ty_::Tunion([]) => Some("nothing"),
1299 Ty_::Tthis => Some("this"),
1300 Ty_::Tprim(prim) => Some(match prim {
1301 aast::Tprim::Tnull => "null",
1302 aast::Tprim::Tvoid => "void",
1303 aast::Tprim::Tint => "int",
1304 aast::Tprim::Tnum => "num",
1305 aast::Tprim::Tfloat => "float",
1306 aast::Tprim::Tstring => "string",
1307 aast::Tprim::Tarraykey => "arraykey",
1308 aast::Tprim::Tresource => "resource",
1309 aast::Tprim::Tnoreturn => "noreturn",
1310 aast::Tprim::Tbool => "bool",
1312 Ty_::Tapply(((_p, id), _tyl)) => Some(cut_namespace(id)),
1313 Ty_::Taccess(TaccessType(ty, id)) => {
1314 reinfer_type_to_string_opt(arena, ty.1).map(|s| {
1315 bumpalo::format!(in arena, "{}::{}", s, id.1).into_bump_str()
1321 fn create_vars_for_reinfer_types<'a, 'text, S: SourceTextAllocator<'text, 'a>>(
1322 this: &DirectDeclSmartConstructors<'a, 'text, S>,
1326 let mk = |r, ty_| this.alloc(Ty(r, ty_));
1327 let must_reinfer_type = |ty| match reinfer_type_to_string_opt(this.arena, ty) {
1329 Some(ty_str) => this.opts.gi_reinfer_types.contains(&ty_str),
1332 Ty(r, Ty_::Tapply(&(id, [ty1]))) if id.1 == "\\HH\\Awaitable" => {
1333 let ty1 = this.alloc(create_vars_for_reinfer_types(this, *ty1, tvar));
1334 mk(r, Ty_::Tapply(this.alloc((id, std::slice::from_ref(ty1)))))
1336 Ty(r, Ty_::Toption(ty1)) => {
1337 let ty1 = create_vars_for_reinfer_types(this, ty1, tvar);
1338 mk(r, Ty_::Toption(ty1))
1340 Ty(r, Ty_::Tapply(((_p, id), [])))
1341 if cut_namespace(id) == "PHPism_FIXME_Array" =>
1343 if must_reinfer_type(ty.1) {
1344 let tvar = mk(r, Ty_::Tvar(0));
1345 mk(r, Ty_::TvarrayOrDarray(this.alloc((tvar, tvar))))
1350 Ty(r, Ty_::Tvarray(ty)) => {
1351 let ty = create_vars_for_reinfer_types(this, ty, tvar);
1352 mk(r, Ty_::Tvarray(ty))
1354 Ty(r, Ty_::Tdarray((tyk, tyv))) => {
1355 let tyk = create_vars_for_reinfer_types(this, tyk, tvar);
1356 let tyv = create_vars_for_reinfer_types(this, tyv, tvar);
1357 mk(r, Ty_::Tdarray(this.alloc((tyk, tyv))))
1359 Ty(r, Ty_::TvarrayOrDarray((tyk, tyv))) => {
1360 let tyk = create_vars_for_reinfer_types(this, tyk, tvar);
1361 let tyv = create_vars_for_reinfer_types(this, tyv, tvar);
1362 mk(r, Ty_::TvarrayOrDarray(this.alloc((tyk, tyv))))
1365 if must_reinfer_type(ty_) {
1373 create_vars_for_reinfer_types(self, ty, tvar)
1379 fn to_attributes(&self, node: Node<'a>) -> Attributes<'a> {
1380 let mut attributes = Attributes {
1389 accept_disposable: false,
1390 dynamically_callable: false,
1391 returns_disposable: false,
1393 ifc_attribute: default_ifc_fun_decl(),
1398 support_dynamic_type: false,
1403 let nodes = match node {
1404 Node::List(&nodes) | Node::BracketedList(&(_, nodes, _)) => nodes,
1405 _ => return attributes,
1408 let mut ifc_already_policied = false;
1410 // Iterate in reverse, to match the behavior of OCaml decl in error conditions.
1411 for attribute in nodes.iter().rev() {
1412 if let Node::Attribute(attribute) = attribute {
1413 match attribute.name.1.as_ref() {
1415 attributes.deprecated = attribute
1416 .string_literal_params
1418 .map(|&x| self.str_from_utf8_for_bytes_in_arena(x));
1420 "__Reifiable" => attributes.reifiable = Some(attribute.name.0),
1422 attributes.late_init = true;
1425 attributes.const_ = true;
1428 attributes.lsb = true;
1431 attributes.memoizelsb = true;
1434 attributes.override_ = true;
1436 "__Enforceable" => {
1437 attributes.enforceable = Some(attribute.name.0);
1439 "__AcceptDisposable" => {
1440 attributes.accept_disposable = true;
1442 "__DynamicallyCallable" => {
1443 attributes.dynamically_callable = true;
1445 "__ReturnDisposable" => {
1446 attributes.returns_disposable = true;
1449 attributes.php_std_lib = true;
1452 let string_literal_params = || {
1454 .string_literal_params
1456 .map(|&x| self.str_from_utf8_for_bytes_in_arena(x))
1458 // Take the classname param by default
1459 attributes.ifc_attribute =
1460 IfcFunDecl::FDPolicied(attribute.classname_params.first().map_or_else(
1461 string_literal_params, // default
1462 |&x| Some(x.name.1), // f
1464 ifc_already_policied = true;
1467 if !ifc_already_policied {
1468 attributes.ifc_attribute = IfcFunDecl::FDInferFlows;
1472 attributes.external = true;
1475 attributes.can_call = true;
1477 naming_special_names::user_attributes::VIA_LABEL => {
1478 attributes.via_label = true;
1481 attributes.soft = true;
1483 "__SupportDynamicType" => {
1484 attributes.support_dynamic_type = true;
1487 attributes.module = attribute
1488 .string_literal_params
1490 .map(|&x| self.str_from_utf8_for_bytes_in_arena(x))
1492 let mut chars = x.split('.');
1493 match chars.next() {
1496 let rest = chars.collect::<std::vec::Vec<_>>();
1497 Some(self.alloc(Module_(s, self.alloc(rest))))
1503 attributes.internal = true;
1513 // Limited version of node_to_ty that matches behavior of Decl_utils.infer_const
1514 fn infer_const(&self, name: Node<'a>, node: Node<'a>) -> Option<&'a Ty<'a>> {
1516 Node::StringLiteral(_)
1517 | Node::BooleanLiteral(_)
1518 | Node::IntLiteral(_)
1519 | Node::FloatingLiteral(_)
1520 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uminus, _))))
1521 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uplus, _))))
1522 | Node::Expr(aast::Expr(_, _, aast::Expr_::String(..))) => self.node_to_ty(node),
1523 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1524 let pos = self.token_pos(t);
1526 self.alloc(Reason::witness_from_decl(pos)),
1527 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1530 _ => Some(self.tany_with_pos(self.get_pos(name))),
1534 fn pop_type_params(&mut self, node: Node<'a>) -> &'a [&'a Tparam<'a>] {
1536 Node::TypeParameters(tparams) => {
1537 Rc::make_mut(&mut self.type_parameters).pop().unwrap();
1544 fn ret_from_fun_kind(&self, kind: FunKind, type_: &'a Ty<'a>) -> &'a Ty<'a> {
1545 let pos = type_.get_pos().unwrap_or_else(|| Pos::none());
1547 FunKind::FAsyncGenerator => self.alloc(Ty(
1548 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1549 Ty_::Tapply(self.alloc((
1550 (pos, naming_special_names::classes::ASYNC_GENERATOR),
1551 self.alloc([type_, type_, type_]),
1554 FunKind::FGenerator => self.alloc(Ty(
1555 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1556 Ty_::Tapply(self.alloc((
1557 (pos, naming_special_names::classes::GENERATOR),
1558 self.alloc([type_, type_, type_]),
1561 FunKind::FAsync => self.alloc(Ty(
1562 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1563 Ty_::Tapply(self.alloc((
1564 (pos, naming_special_names::classes::AWAITABLE),
1565 self.alloc([type_]),
1572 fn is_type_param_in_scope(&self, name: &str) -> bool {
1573 self.type_parameters.iter().any(|tps| tps.contains(name))
1576 fn as_fun_implicit_params(
1578 capability: Node<'a>,
1579 default_pos: &'a Pos<'a>,
1580 ) -> &'a FunImplicitParams<'a> {
1581 /* Note: do not simplify intersections, keep empty / singleton intersections
1582 * for coeffect contexts
1584 let capability = match self.node_to_ty(capability) {
1585 Some(ty) => CapTy(ty),
1586 None => CapDefaults(default_pos),
1588 self.alloc(FunImplicitParams { capability })
1594 attributes: Node<'a>,
1595 header: &'a FunctionHeader<'a>,
1597 ) -> Option<(PosId<'a>, &'a Ty<'a>, &'a [ShallowProp<'a>])> {
1598 let id_opt = match (is_method, header.name) {
1599 // If the name is missing, we use the left paren here, just to get a
1600 // position to point to.
1601 (_, Node::Token(t)) if t.kind() == TokenKind::LeftParen => {
1602 let pos = self.token_pos(t);
1605 (true, Node::Token(t)) if t.kind() == TokenKind::Construct => {
1606 let pos = self.token_pos(t);
1607 Some(Id(pos, naming_special_names::members::__CONSTRUCT))
1609 (true, _) => self.expect_name(header.name),
1610 (false, _) => self.elaborate_defined_id(header.name),
1612 let id = id_opt.unwrap_or(Id(self.get_pos(header.name), ""));
1613 let (params, properties, arity) = self.as_fun_params(header.param_list)?;
1614 let f_pos = self.get_pos(header.name);
1615 let implicit_params = self.as_fun_implicit_params(header.capability, f_pos);
1617 let type_ = match header.name {
1618 Node::Token(t) if t.kind() == TokenKind::Construct => {
1619 let pos = self.token_pos(t);
1621 self.alloc(Reason::witness_from_decl(pos)),
1622 Ty_::Tprim(self.alloc(aast::Tprim::Tvoid)),
1626 .rewrite_ty_for_global_inference(
1627 self.node_to_ty(header.ret_hint),
1628 Reason::RglobalFunRet(f_pos),
1630 .unwrap_or_else(|| self.tany_with_pos(f_pos)),
1635 .any(|n| n.is_token(TokenKind::Async));
1636 let readonly = header
1639 .any(|n| n.is_token(TokenKind::Readonly));
1641 let fun_kind = if body.iter().any(|node| node.is_token(TokenKind::Yield)) {
1643 FunKind::FAsyncGenerator
1654 let type_ = if !header.ret_hint.is_present() {
1655 self.ret_from_fun_kind(fun_kind, type_)
1659 let attributes = self.to_attributes(attributes);
1660 // TODO(hrust) Put this in a helper. Possibly do this for all flags.
1661 let mut flags = match fun_kind {
1662 FunKind::FSync => FunTypeFlags::empty(),
1663 FunKind::FAsync => FunTypeFlags::ASYNC,
1664 FunKind::FGenerator => FunTypeFlags::GENERATOR,
1665 FunKind::FAsyncGenerator => FunTypeFlags::ASYNC | FunTypeFlags::GENERATOR,
1668 if attributes.returns_disposable {
1669 flags |= FunTypeFlags::RETURN_DISPOSABLE;
1671 if attributes.support_dynamic_type {
1672 flags |= FunTypeFlags::SUPPORT_DYNAMIC_TYPE;
1674 if header.readonly_return.is_token(TokenKind::Readonly) {
1675 flags |= FunTypeFlags::RETURNS_READONLY;
1678 flags |= FunTypeFlags::READONLY_THIS
1681 let ifc_decl = attributes.ifc_attribute;
1683 // Pop the type params stack only after creating all inner types.
1684 let tparams = self.pop_type_params(header.type_params);
1686 let where_constraints =
1687 self.slice(header.where_constraints.iter().filter_map(|&x| match x {
1688 Node::WhereConstraint(x) => Some(x),
1692 let (params, tparams, implicit_params, where_constraints) =
1693 self.rewrite_effect_polymorphism(params, tparams, implicit_params, where_constraints);
1695 let ft = self.alloc(FunType {
1701 ret: self.alloc(PossiblyEnforcedTy {
1702 enforced: Enforcement::Unenforced,
1709 let ty = self.alloc(Ty(
1710 self.alloc(Reason::witness_from_decl(id.0)),
1713 Some((id.into(), ty, properties))
1719 ) -> Option<(&'a FunParams<'a>, &'a [ShallowProp<'a>], FunArity<'a>)> {
1721 Node::List(nodes) => {
1722 let mut params = Vec::with_capacity_in(nodes.len(), self.arena);
1723 let mut properties = Vec::new_in(self.arena);
1724 let mut arity = FunArity::Fstandard;
1725 for node in nodes.iter() {
1727 Node::FunParam(&FunParamDecl {
1738 let attributes = self.to_attributes(attributes);
1740 if let Some(visibility) = visibility.as_visibility() {
1741 let name = name.unwrap_or("");
1742 let name = strip_dollar_prefix(name);
1743 let mut flags = PropFlags::empty();
1744 flags.set(PropFlags::CONST, attributes.const_);
1745 flags.set(PropFlags::NEEDS_INIT, self.file_mode != Mode::Mhhi);
1746 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
1747 flags.set(PropFlags::READONLY, readonly);
1748 properties.push(ShallowProp {
1751 type_: self.rewrite_ty_for_global_inference(
1752 self.node_to_ty(hint),
1753 Reason::RglobalFunParam(pos),
1755 visibility: if attributes.internal
1756 && visibility == aast::Visibility::Public
1758 aast::Visibility::Internal
1767 .rewrite_ty_for_global_inference(
1768 self.node_to_ty(hint),
1769 Reason::RglobalFunParam(pos),
1771 .unwrap_or_else(|| self.tany_with_pos(pos));
1772 // These are illegal here--they can only be used on
1773 // parameters in a function type hint (see
1774 // make_closure_type_specifier and unwrap_mutability).
1775 // Unwrap them here anyway for better error recovery.
1776 let type_ = match type_ {
1777 Ty(_, Ty_::Tapply(((_, "\\Mutable"), [t]))) => t,
1778 Ty(_, Ty_::Tapply(((_, "\\OwnedMutable"), [t]))) => t,
1779 Ty(_, Ty_::Tapply(((_, "\\MaybeMutable"), [t]))) => t,
1782 let mut flags = FunParamFlags::empty();
1783 if attributes.accept_disposable {
1784 flags |= FunParamFlags::ACCEPT_DISPOSABLE
1786 if attributes.external {
1787 flags |= FunParamFlags::IFC_EXTERNAL
1789 if attributes.can_call {
1790 flags |= FunParamFlags::IFC_CAN_CALL
1792 if attributes.via_label {
1793 flags |= FunParamFlags::VIA_LABEL
1796 flags |= FunParamFlags::READONLY
1799 ParamMode::FPinout => {
1800 flags |= FunParamFlags::INOUT;
1802 ParamMode::FPnormal => {}
1805 if initializer.is_present() {
1806 flags |= FunParamFlags::HAS_DEFAULT;
1808 let variadic = initializer.is_ignored() && variadic;
1809 let type_ = if variadic {
1811 self.alloc(if name.is_some() {
1812 Reason::RvarParamFromDecl(pos)
1814 Reason::witness_from_decl(pos)
1821 let param = self.alloc(FunParam {
1824 type_: self.alloc(PossiblyEnforcedTy {
1825 enforced: Enforcement::Unenforced,
1830 arity = match arity {
1831 FunArity::Fstandard if variadic => FunArity::Fvariadic(param),
1842 params.into_bump_slice(),
1843 properties.into_bump_slice(),
1847 n if n.is_ignored() => Some((&[], &[], FunArity::Fstandard)),
1852 fn make_shape_field_name(&self, name: Node<'a>) -> Option<ShapeFieldName<'a>> {
1854 Node::StringLiteral(&(s, pos)) => ShapeFieldName::SFlitStr(self.alloc((pos, s))),
1855 // TODO: OCaml decl produces SFlitStr here instead of SFlitInt, so
1856 // we must also. Looks like int literal keys have become a parse
1857 // error--perhaps that's why.
1858 Node::IntLiteral(&(s, pos)) => ShapeFieldName::SFlitStr(self.alloc((pos, s.into()))),
1859 Node::Expr(aast::Expr(
1862 aast::Expr_::ClassConst(&(
1863 aast::ClassId(_, _, aast::ClassId_::CI(&class_name)),
1866 )) => ShapeFieldName::SFclassConst(self.alloc((class_name, const_name))),
1867 Node::Expr(aast::Expr(
1870 aast::Expr_::ClassConst(&(
1871 aast::ClassId(_, pos, aast::ClassId_::CIself),
1874 )) => ShapeFieldName::SFclassConst(self.alloc((
1877 self.classish_name_builder.get_current_classish_name()?.0,
1887 fn make_t_shape_field_name(&mut self, ShapeField(field): &ShapeField<'a>) -> TShapeField<'a> {
1888 TShapeField(match field {
1889 ShapeFieldName::SFlitInt(&(pos, x)) => {
1890 TshapeFieldName::TSFlitInt(self.alloc(PosString(pos, x)))
1892 ShapeFieldName::SFlitStr(&(pos, x)) => {
1893 TshapeFieldName::TSFlitStr(self.alloc(PosByteString(pos, x)))
1895 ShapeFieldName::SFclassConst(&(id, &(pos, x))) => {
1896 TshapeFieldName::TSFclassConst(self.alloc((id.into(), PosString(pos, x))))
1904 type_arguments: Node<'a>,
1905 pos_to_merge: &'a Pos<'a>,
1907 let type_arguments = self.slice(
1910 .filter_map(|&node| self.node_to_ty(node)),
1913 let pos = self.merge(base_ty.0, pos_to_merge);
1915 // OCaml decl creates a capability with a hint pointing to the entire
1916 // type (i.e., pointing to `Rx<(function(): void)>` rather than just
1917 // `(function(): void)`), so we extend the hint position similarly here.
1918 let extend_capability_pos = |implicit_params: &'a FunImplicitParams<'_>| {
1919 let capability = match implicit_params.capability {
1921 let ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), ty.1));
1924 CapDefaults(_) => CapDefaults(pos),
1926 self.alloc(FunImplicitParams {
1932 let ty_ = match (base_ty, type_arguments) {
1933 ((_, name), &[&Ty(_, Ty_::Tfun(f))]) if name == "\\Pure" => {
1934 Ty_::Tfun(self.alloc(FunType {
1935 implicit_params: extend_capability_pos(f.implicit_params),
1939 _ => Ty_::Tapply(self.alloc((base_ty, type_arguments))),
1942 self.hint_ty(pos, ty_)
1945 fn hint_ty(&self, pos: &'a Pos<'a>, ty_: Ty_<'a>) -> Node<'a> {
1946 Node::Ty(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1949 fn prim_ty(&self, tprim: aast::Tprim, pos: &'a Pos<'a>) -> Node<'a> {
1950 self.hint_ty(pos, Ty_::Tprim(self.alloc(tprim)))
1953 fn tany_with_pos(&self, pos: &'a Pos<'a>) -> &'a Ty<'a> {
1954 self.alloc(Ty(self.alloc(Reason::witness_from_decl(pos)), TANY_))
1957 /// The type used when a `vec_or_dict` typehint is missing its key type argument.
1958 fn vec_or_dict_key(&self, pos: &'a Pos<'a>) -> &'a Ty<'a> {
1960 self.alloc(Reason::RvecOrDictKey(pos)),
1961 Ty_::Tprim(self.alloc(aast::Tprim::Tarraykey)),
1965 fn source_text_at_pos(&self, pos: &'a Pos<'a>) -> &'text [u8] {
1966 let start = pos.start_cnum();
1967 let end = pos.end_cnum();
1968 self.source_text.source_text().sub(start, end - start)
1971 // While we usually can tell whether to allocate a Tapply or Tgeneric based
1972 // on our type_parameters stack, *constraints* on type parameters may
1973 // reference type parameters which we have not parsed yet. When constructing
1974 // a type parameter list, we use this function to rewrite the type of each
1975 // constraint, considering the full list of type parameters to be in scope.
1976 fn convert_tapply_to_tgeneric(&self, ty: &'a Ty<'a>) -> &'a Ty<'a> {
1977 let ty_ = match ty.1 {
1978 Ty_::Tapply(&(id, targs)) => {
1979 let converted_targs = self.slice(
1982 .map(|&targ| self.convert_tapply_to_tgeneric(targ)),
1984 match self.tapply_should_be_tgeneric(ty.0, id) {
1985 Some(name) => Ty_::Tgeneric(self.alloc((name, converted_targs))),
1986 None => Ty_::Tapply(self.alloc((id, converted_targs))),
1989 Ty_::Tlike(ty) => Ty_::Tlike(self.convert_tapply_to_tgeneric(ty)),
1990 Ty_::Toption(ty) => Ty_::Toption(self.convert_tapply_to_tgeneric(ty)),
1991 Ty_::Tfun(fun_type) => {
1992 let convert_param = |param: &'a FunParam<'a>| {
1993 self.alloc(FunParam {
1994 type_: self.alloc(PossiblyEnforcedTy {
1995 enforced: param.type_.enforced,
1996 type_: self.convert_tapply_to_tgeneric(param.type_.type_),
2001 let arity = match fun_type.arity {
2002 FunArity::Fstandard => FunArity::Fstandard,
2003 FunArity::Fvariadic(param) => FunArity::Fvariadic(convert_param(param)),
2005 let params = self.slice(fun_type.params.iter().copied().map(convert_param));
2006 let implicit_params = fun_type.implicit_params;
2007 let ret = self.alloc(PossiblyEnforcedTy {
2008 enforced: fun_type.ret.enforced,
2009 type_: self.convert_tapply_to_tgeneric(fun_type.ret.type_),
2011 Ty_::Tfun(self.alloc(FunType {
2019 Ty_::Tshape(&(kind, fields)) => {
2020 let mut converted_fields = AssocListMut::with_capacity_in(fields.len(), self.arena);
2021 for (&name, ty) in fields.iter() {
2022 converted_fields.insert(
2024 self.alloc(ShapeFieldType {
2025 optional: ty.optional,
2026 ty: self.convert_tapply_to_tgeneric(ty.ty),
2030 Ty_::Tshape(self.alloc((kind, converted_fields.into())))
2032 Ty_::Tdarray(&(tk, tv)) => Ty_::Tdarray(self.alloc((
2033 self.convert_tapply_to_tgeneric(tk),
2034 self.convert_tapply_to_tgeneric(tv),
2036 Ty_::Tvarray(ty) => Ty_::Tvarray(self.convert_tapply_to_tgeneric(ty)),
2037 Ty_::TvarrayOrDarray(&(tk, tv)) => Ty_::TvarrayOrDarray(self.alloc((
2038 self.convert_tapply_to_tgeneric(tk),
2039 self.convert_tapply_to_tgeneric(tv),
2041 Ty_::TvecOrDict(&(tk, tv)) => Ty_::TvecOrDict(self.alloc((
2042 self.convert_tapply_to_tgeneric(tk),
2043 self.convert_tapply_to_tgeneric(tv),
2045 Ty_::Ttuple(tys) => Ty_::Ttuple(
2048 .map(|&targ| self.convert_tapply_to_tgeneric(targ)),
2053 self.alloc(Ty(ty.0, ty_))
2056 // This is the logic for determining if convert_tapply_to_tgeneric should turn
2057 // a Tapply into a Tgeneric
2058 fn tapply_should_be_tgeneric(&self, reason: &'a Reason<'a>, id: PosId<'a>) -> Option<&'a str> {
2059 match reason.pos() {
2060 // If the name contained a namespace delimiter in the original
2061 // source text, then it can't have referred to a type parameter
2062 // (since type parameters cannot be namespaced).
2064 if self.source_text_at_pos(pos).contains(&b'\\') {
2068 None => return None,
2070 // However, the direct decl parser will unconditionally prefix
2071 // the name with the current namespace (as it does for any
2072 // Tapply). We need to remove it.
2073 match id.1.rsplit('\\').next() {
2074 Some(name) if self.is_type_param_in_scope(name) => return Some(name),
2079 fn rewrite_taccess_reasons(&self, ty: &'a Ty<'a>, r: &'a Reason<'a>) -> &'a Ty<'a> {
2080 let ty_ = match ty.1 {
2081 Ty_::Taccess(&TaccessType(ty, id)) => {
2082 Ty_::Taccess(self.alloc(TaccessType(self.rewrite_taccess_reasons(ty, r), id)))
2086 self.alloc(Ty(r, ty_))
2089 fn user_attribute_to_decl(
2091 attr: &UserAttributeNode<'a>,
2092 ) -> &'a shallow_decl_defs::UserAttribute<'a> {
2093 self.alloc(shallow_decl_defs::UserAttribute {
2094 name: attr.name.into(),
2095 classname_params: self.slice(attr.classname_params.iter().map(|p| p.name.1)),
2099 fn namespace_use_kind(use_kind: &Node<'_>) -> Option<NamespaceUseKind> {
2100 match use_kind.token_kind() {
2101 Some(TokenKind::Const) => None,
2102 Some(TokenKind::Function) => None,
2103 Some(TokenKind::Type) => Some(NamespaceUseKind::Type),
2104 Some(TokenKind::Namespace) => Some(NamespaceUseKind::Namespace),
2105 _ if !use_kind.is_present() => Some(NamespaceUseKind::Mixed),
2110 fn has_polymorphic_context(contexts: &[&Ty<'_>]) -> bool {
2111 contexts.iter().any(|&ty| match ty.1 {
2112 Ty_::Tapply((root, &[])) // Hfun_context in the AST
2113 | Ty_::Taccess(TaccessType(Ty(_, Ty_::Tapply((root, &[]))), _)) => root.1.contains('$'),
2114 | Ty_::Taccess(TaccessType(t, _)) => Self::taccess_root_is_generic(t),
2119 fn ctx_generic_for_fun(&self, name: &str) -> &'a str {
2120 bumpalo::format!(in self.arena, "T/[ctx {}]", name).into_bump_str()
2123 fn ctx_generic_for_dependent(&self, name: &str, cst: &str) -> &'a str {
2124 bumpalo::format!(in self.arena, "T/[{}::{}]", name, cst).into_bump_str()
2127 // Note: the reason for the divergence between this and the lowerer is that
2128 // hint Haccess is a flat list, whereas decl ty Taccess is a tree.
2129 fn taccess_root_is_generic(ty: &Ty<'_>) -> bool {
2131 Ty(_, Ty_::Tgeneric((_, &[]))) => true,
2132 Ty(_, Ty_::Taccess(&TaccessType(t, _))) => Self::taccess_root_is_generic(t),
2137 fn ctx_generic_for_generic_taccess_inner(&self, ty: &Ty<'_>, cst: &str) -> std::string::String {
2138 let left = match ty {
2139 Ty(_, Ty_::Tgeneric((name, &[]))) => name.to_string(),
2140 Ty(_, Ty_::Taccess(&TaccessType(ty, cst))) => {
2141 self.ctx_generic_for_generic_taccess_inner(ty, cst.1)
2143 _ => panic!("Unexpected element in Taccess"),
2145 format!("{}::{}", left, cst)
2147 fn ctx_generic_for_generic_taccess(&self, ty: &Ty<'_>, cst: &str) -> &'a str {
2148 bumpalo::format!(in self.arena, "T/[{}]", self.ctx_generic_for_generic_taccess_inner(ty, cst))
2152 fn rewrite_effect_polymorphism(
2154 params: &'a [&'a FunParam<'a>],
2155 tparams: &'a [&'a Tparam<'a>],
2156 implicit_params: &'a FunImplicitParams<'a>,
2157 where_constraints: &'a [&'a WhereConstraint<'a>],
2159 &'a [&'a FunParam<'a>],
2160 &'a [&'a Tparam<'a>],
2161 &'a FunImplicitParams<'a>,
2162 &'a [&'a WhereConstraint<'a>],
2164 let (cap_reason, context_tys) = match implicit_params.capability {
2165 CapTy(&Ty(r, Ty_::Tintersection(tys))) if Self::has_polymorphic_context(tys) => {
2168 CapTy(ty) if Self::has_polymorphic_context(&[ty]) => {
2169 (ty.0, std::slice::from_ref(self.alloc(ty)))
2171 _ => return (params, tparams, implicit_params, where_constraints),
2173 let tp = |name, constraints| {
2175 variance: Variance::Invariant,
2179 reified: aast::ReifyKind::Erased,
2180 user_attributes: &[],
2184 // For a polymorphic context with form `ctx $f` (represented here as
2185 // `Tapply "$f"`), add a type parameter named `Tctx$f`, and rewrite the
2186 // parameter `(function (ts)[_]: t) $f` as `(function (ts)[Tctx$f]: t) $f`
2187 let rewrite_fun_ctx =
2188 |tparams: &mut Vec<'_, &'a Tparam<'a>>, ty: &Ty<'a>, param_name: &str| -> Ty<'a> {
2189 let ft = match ty.1 {
2190 Ty_::Tfun(ft) => ft,
2191 _ => return ty.clone(),
2193 let cap_ty = match ft.implicit_params.capability {
2194 CapTy(&Ty(_, Ty_::Tintersection(&[ty]))) | CapTy(ty) => ty,
2195 _ => return ty.clone(),
2197 let pos = match cap_ty.1 {
2198 Ty_::Tapply(((pos, "_"), _)) => pos,
2199 _ => return ty.clone(),
2201 let name = self.ctx_generic_for_fun(param_name);
2202 let tparam = tp((pos, name), &[]);
2203 tparams.push(tparam);
2204 let cap_ty = self.alloc(Ty(cap_ty.0, Ty_::Tgeneric(self.alloc((name, &[])))));
2205 let ft = self.alloc(FunType {
2206 implicit_params: self.alloc(FunImplicitParams {
2207 capability: CapTy(cap_ty),
2211 Ty(ty.0, Ty_::Tfun(ft))
2214 // For a polymorphic context with form `$g::C`, if we have a function
2215 // parameter `$g` with type `G` (where `G` is not a type parameter),
2216 // - add a type parameter constrained by $g's type: `T/$g as G`
2217 // - replace $g's type hint (`G`) with the new type parameter `T/$g`
2218 // Then, for each polymorphic context with form `$g::C`,
2219 // - add a type parameter `T/[$g::C]`
2220 // - add a where constraint `T/[$g::C] = T$g :: C`
2221 let rewrite_arg_ctx = |
2222 tparams: &mut Vec<'_, &'a Tparam<'a>>,
2223 where_constraints: &mut Vec<'_, &'a WhereConstraint<'a>>,
2225 param_pos: &'a Pos<'a>,
2227 context_reason: &'a Reason<'a>,
2230 let rewritten_ty = match ty.1 {
2231 // If the type hint for this function parameter is a type
2232 // parameter introduced in this function declaration, don't add
2233 // a new type parameter.
2234 Ty_::Tgeneric(&(type_name, _))
2235 if tparams.iter().any(|tp| tp.name.1 == type_name) =>
2239 // Otherwise, if the parameter is `G $g`, create tparam
2240 // `T$g as G` and replace $g's type hint
2242 let id = (param_pos, self.concat("T/", name));
2245 std::slice::from_ref(
2246 self.alloc((ConstraintKind::ConstraintAs, self.alloc(ty.clone()))),
2250 self.alloc(Reason::hint(param_pos)),
2251 Ty_::Tgeneric(self.alloc((id.1, &[]))),
2255 let ty = self.alloc(Ty(context_reason, rewritten_ty.1));
2256 let right = self.alloc(Ty(
2258 Ty_::Taccess(self.alloc(TaccessType(ty, cst))),
2261 context_reason.pos().unwrap_or(Pos::none()),
2262 self.ctx_generic_for_dependent(name, &cst.1),
2264 tparams.push(tp(left_id, &[]));
2265 let left = self.alloc(Ty(
2267 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2269 where_constraints.push(self.alloc(WhereConstraint(
2271 ConstraintKind::ConstraintEq,
2277 let mut tparams = Vec::from_iter_in(tparams.iter().copied(), self.arena);
2278 let mut where_constraints =
2279 Vec::from_iter_in(where_constraints.iter().copied(), self.arena);
2281 // The divergence here from the lowerer comes from using oxidized_by_ref instead of oxidized
2282 let mut ty_by_param: BTreeMap<&str, (Ty<'a>, &'a Pos<'a>)> = params
2284 .filter_map(|param| Some((param.name?, (param.type_.type_.clone(), param.pos))))
2287 for context_ty in context_tys {
2288 match context_ty.1 {
2289 // Hfun_context in the AST.
2290 Ty_::Tapply(((_, name), _)) if name.starts_with('$') => {
2291 if let Some((param_ty, _)) = ty_by_param.get_mut(name) {
2293 Ty_::Tlike(ref mut ty) => match ty {
2294 Ty(r, Ty_::Toption(tinner)) => {
2295 *ty = self.alloc(Ty(
2297 Ty_::Toption(self.alloc(rewrite_fun_ctx(
2305 *ty = self.alloc(rewrite_fun_ctx(&mut tparams, ty, name));
2308 Ty_::Toption(ref mut ty) => {
2309 *ty = self.alloc(rewrite_fun_ctx(&mut tparams, ty, name));
2312 *param_ty = rewrite_fun_ctx(&mut tparams, param_ty, name);
2317 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), _))), cst)) => {
2318 if let Some((param_ty, param_pos)) = ty_by_param.get_mut(name) {
2319 let mut rewrite = |t| {
2322 &mut where_constraints,
2331 Ty_::Tlike(ref mut ty) => match ty {
2332 Ty(r, Ty_::Toption(tinner)) => {
2334 self.alloc(Ty(r, Ty_::Toption(self.alloc(rewrite(tinner)))))
2337 *ty = self.alloc(rewrite(ty));
2340 Ty_::Toption(ref mut ty) => {
2341 *ty = self.alloc(rewrite(ty));
2344 *param_ty = rewrite(param_ty);
2349 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2351 context_ty.0.pos().unwrap_or(Pos::none()),
2352 self.ctx_generic_for_generic_taccess(t, &cst.1),
2354 tparams.push(tp(left_id, &[]));
2355 let left = self.alloc(Ty(
2357 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2359 where_constraints.push(self.alloc(WhereConstraint(
2361 ConstraintKind::ConstraintEq,
2369 let params = self.slice(params.iter().copied().map(|param| match param.name {
2371 Some(name) => match ty_by_param.get(name) {
2372 Some((type_, _)) if param.type_.type_ != type_ => self.alloc(FunParam {
2373 type_: self.alloc(PossiblyEnforcedTy {
2374 type_: self.alloc(type_.clone()),
2383 let context_tys = self.slice(context_tys.iter().copied().map(|ty| {
2384 let ty_ = match ty.1 {
2385 Ty_::Tapply(((_, name), &[])) if name.starts_with('$') => {
2386 Ty_::Tgeneric(self.alloc((self.ctx_generic_for_fun(name), &[])))
2388 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), &[]))), cst))
2389 if name.starts_with('$') =>
2391 let name = self.ctx_generic_for_dependent(name, &cst.1);
2392 Ty_::Tgeneric(self.alloc((name, &[])))
2394 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2395 let name = self.ctx_generic_for_generic_taccess(t, &cst.1);
2396 Ty_::Tgeneric(self.alloc((name, &[])))
2400 self.alloc(Ty(ty.0, ty_))
2402 let cap_ty = match context_tys {
2404 _ => self.alloc(Ty(cap_reason, Ty_::Tintersection(context_tys))),
2406 let implicit_params = self.alloc(FunImplicitParams {
2407 capability: CapTy(cap_ty),
2412 tparams.into_bump_slice(),
2414 where_constraints.into_bump_slice(),
2419 enum NodeIterHelper<'a, 'b> {
2421 Single(&'b Node<'a>),
2422 Vec(std::slice::Iter<'b, Node<'a>>),
2425 impl<'a, 'b> Iterator for NodeIterHelper<'a, 'b> {
2426 type Item = &'b Node<'a>;
2428 fn next(&mut self) -> Option<Self::Item> {
2430 NodeIterHelper::Empty => None,
2431 NodeIterHelper::Single(node) => {
2433 *self = NodeIterHelper::Empty;
2436 NodeIterHelper::Vec(ref mut iter) => iter.next(),
2440 // Must return the upper bound returned by Node::len.
2441 fn size_hint(&self) -> (usize, Option<usize>) {
2443 NodeIterHelper::Empty => (0, Some(0)),
2444 NodeIterHelper::Single(_) => (1, Some(1)),
2445 NodeIterHelper::Vec(iter) => iter.size_hint(),
2450 impl<'a, 'b> DoubleEndedIterator for NodeIterHelper<'a, 'b> {
2451 fn next_back(&mut self) -> Option<Self::Item> {
2453 NodeIterHelper::Empty => None,
2454 NodeIterHelper::Single(_) => self.next(),
2455 NodeIterHelper::Vec(ref mut iter) => iter.next_back(),
2460 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> FlattenOp
2461 for DirectDeclSmartConstructors<'a, 'text, S>
2465 fn flatten(&self, kind: SyntaxKind, lst: std::vec::Vec<Self::S>) -> Self::S {
2469 Node::List(children) => children.len(),
2471 if Self::is_zero(x) {
2479 let mut r = Vec::with_capacity_in(size, self.arena);
2480 for s in lst.into_iter() {
2482 Node::List(children) => r.extend(children.iter().copied()),
2484 if !Self::is_zero(&x) {
2490 match r.into_bump_slice() {
2491 [] => Node::Ignored(kind),
2493 slice => Node::List(self.alloc(slice)),
2497 fn zero(kind: SyntaxKind) -> Self::S {
2501 fn is_zero(s: &Self::S) -> bool {
2503 Node::Token(token) => match token.kind() {
2504 TokenKind::Yield | TokenKind::Required | TokenKind::Lateinit => false,
2507 Node::List(inner) => inner.iter().all(Self::is_zero),
2513 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>>
2514 FlattenSmartConstructors<'a, DirectDeclSmartConstructors<'a, 'text, S>>
2515 for DirectDeclSmartConstructors<'a, 'text, S>
2517 fn make_token(&mut self, token: CompactToken) -> Self::R {
2518 let token_text = |this: &Self| this.str_from_utf8(this.token_bytes(&token));
2519 let token_pos = |this: &Self| {
2522 .offset_to_file_pos_triple(token.start_offset());
2525 .offset_to_file_pos_triple(token.end_offset());
2526 Pos::from_lnum_bol_cnum(this.arena, this.filename, start, end)
2528 let kind = token.kind();
2530 let result = match kind {
2531 TokenKind::Name | TokenKind::XHPClassName => {
2532 let text = token_text(self);
2533 let pos = token_pos(self);
2535 let name = if kind == TokenKind::XHPClassName {
2536 Node::XhpName(self.alloc((text, pos)))
2538 Node::Name(self.alloc((text, pos)))
2541 if self.previous_token_kind == TokenKind::Class
2542 || self.previous_token_kind == TokenKind::Trait
2543 || self.previous_token_kind == TokenKind::Interface
2545 if let Some(current_class_name) = self.elaborate_defined_id(name) {
2546 self.classish_name_builder
2547 .lexed_name_after_classish_keyword(
2549 current_class_name.1,
2551 self.previous_token_kind,
2557 TokenKind::Class => Node::Name(self.alloc((token_text(self), token_pos(self)))),
2558 TokenKind::Variable => Node::Variable(self.alloc((token_text(self), token_pos(self)))),
2559 // There are a few types whose string representations we have to
2560 // grab anyway, so just go ahead and treat them as generic names.
2565 | TokenKind::Classname
2566 | TokenKind::SelfToken => Node::Name(self.alloc((token_text(self), token_pos(self)))),
2567 TokenKind::XHPElementName => {
2568 Node::XhpName(self.alloc((token_text(self), token_pos(self))))
2570 TokenKind::SingleQuotedStringLiteral => match escaper::unescape_single_in(
2571 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2574 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2575 Err(_) => Node::Ignored(SK::Token(kind)),
2577 TokenKind::DoubleQuotedStringLiteral => match escaper::unescape_double_in(
2578 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2581 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2582 Err(_) => Node::Ignored(SK::Token(kind)),
2584 TokenKind::HeredocStringLiteral => match escaper::unescape_heredoc_in(
2585 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2588 Ok(text) if !self.retain_or_omit_user_attributes_for_facts => {
2589 Node::StringLiteral(self.alloc((text.into(), token_pos(self))))
2591 _ => Node::Ignored(SK::Token(kind)),
2593 TokenKind::NowdocStringLiteral => match escaper::unescape_nowdoc_in(
2594 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2597 Ok(text) if !self.retain_or_omit_user_attributes_for_facts => {
2598 Node::StringLiteral(self.alloc((text.into(), token_pos(self))))
2600 _ => Node::Ignored(SK::Token(kind)),
2602 TokenKind::DecimalLiteral
2603 | TokenKind::OctalLiteral
2604 | TokenKind::HexadecimalLiteral
2605 | TokenKind::BinaryLiteral => {
2606 Node::IntLiteral(self.alloc((token_text(self), token_pos(self))))
2608 TokenKind::FloatingLiteral => {
2609 Node::FloatingLiteral(self.alloc((token_text(self), token_pos(self))))
2611 TokenKind::BooleanLiteral => {
2612 Node::BooleanLiteral(self.alloc((token_text(self), token_pos(self))))
2614 TokenKind::String => self.prim_ty(aast::Tprim::Tstring, token_pos(self)),
2615 TokenKind::Int => self.prim_ty(aast::Tprim::Tint, token_pos(self)),
2616 TokenKind::Float => self.prim_ty(aast::Tprim::Tfloat, token_pos(self)),
2617 // "double" and "boolean" are parse errors--they should be written
2618 // "float" and "bool". The decl-parser treats the incorrect names as
2619 // type names rather than primitives.
2620 TokenKind::Double | TokenKind::Boolean => self.hint_ty(
2622 Ty_::Tapply(self.alloc(((token_pos(self), token_text(self)), &[][..]))),
2624 TokenKind::Num => self.prim_ty(aast::Tprim::Tnum, token_pos(self)),
2625 TokenKind::Bool => self.prim_ty(aast::Tprim::Tbool, token_pos(self)),
2626 TokenKind::Mixed => {
2627 if self.opts.everything_sdt {
2628 Node::Ty(self.alloc(Ty(
2629 self.alloc(Reason::hint(token_pos(self))),
2630 Ty_::Toption(self.alloc(Ty(
2631 self.alloc(Reason::hint(token_pos(self))),
2632 Ty_::Tsupportdynamic,
2636 Node::Ty(self.alloc(Ty(self.alloc(Reason::hint(token_pos(self))), Ty_::Tmixed)))
2639 TokenKind::Void => self.prim_ty(aast::Tprim::Tvoid, token_pos(self)),
2640 TokenKind::Arraykey => self.prim_ty(aast::Tprim::Tarraykey, token_pos(self)),
2641 TokenKind::Noreturn => self.prim_ty(aast::Tprim::Tnoreturn, token_pos(self)),
2642 TokenKind::Resource => self.prim_ty(aast::Tprim::Tresource, token_pos(self)),
2643 TokenKind::NullLiteral
2646 | TokenKind::Backslash
2647 | TokenKind::Construct
2648 | TokenKind::LeftParen
2649 | TokenKind::RightParen
2650 | TokenKind::LeftBracket
2651 | TokenKind::RightBracket
2653 | TokenKind::Question
2656 | TokenKind::Exclamation
2659 | TokenKind::PlusPlus
2660 | TokenKind::MinusMinus
2664 | TokenKind::EqualEqual
2665 | TokenKind::EqualEqualEqual
2666 | TokenKind::StarStar
2667 | TokenKind::AmpersandAmpersand
2669 | TokenKind::LessThan
2670 | TokenKind::LessThanEqual
2671 | TokenKind::GreaterThan
2672 | TokenKind::GreaterThanEqual
2674 | TokenKind::Ampersand
2676 | TokenKind::LessThanLessThan
2677 | TokenKind::GreaterThanGreaterThan
2678 | TokenKind::Percent
2679 | TokenKind::QuestionQuestion
2681 | TokenKind::Abstract
2685 | TokenKind::DotDotDot
2686 | TokenKind::Extends
2688 | TokenKind::Implements
2690 | TokenKind::Interface
2692 | TokenKind::Newtype
2695 | TokenKind::Semicolon
2696 | TokenKind::Private
2697 | TokenKind::Protected
2702 | TokenKind::Lateinit
2703 | TokenKind::RecordDec
2704 | TokenKind::RightBrace
2707 | TokenKind::Function
2708 | TokenKind::Namespace
2710 | TokenKind::Required
2712 | TokenKind::Readonly => Node::Token(FixedWidthToken::new(kind, token.start_offset())),
2713 _ if kind.fixed_width().is_some() => {
2714 Node::IgnoredToken(FixedWidthToken::new(kind, token.start_offset()))
2716 _ => Node::Ignored(SK::Token(kind)),
2718 self.previous_token_kind = kind;
2722 fn make_error(&mut self, error: Self::R) -> Self::R {
2723 // If it's a Token or IgnoredToken, we can use it for error recovery.
2724 // For instance, in `function using() {}`, the `using` keyword will be a
2725 // token wrapped in an error CST node, since the keyword isn't legal in
2730 fn make_missing(&mut self, _: usize) -> Self::R {
2731 Node::Ignored(SK::Missing)
2734 fn make_list(&mut self, items: std::vec::Vec<Self::R>, _: usize) -> Self::R {
2735 if let Some(&yield_) = items
2737 .flat_map(|node| node.iter())
2738 .find(|node| node.is_token(TokenKind::Yield))
2742 let size = items.iter().filter(|node| node.is_present()).count();
2743 let items_iter = items.into_iter();
2744 let mut items = Vec::with_capacity_in(size, self.arena);
2745 for node in items_iter {
2746 if node.is_present() {
2750 let items = items.into_bump_slice();
2751 if items.is_empty() {
2752 Node::Ignored(SK::SyntaxList)
2754 Node::List(self.alloc(items))
2759 fn make_qualified_name(&mut self, parts: Self::R) -> Self::R {
2760 let pos = self.get_pos(parts);
2762 Node::List(nodes) => Node::QualifiedName(self.alloc((nodes, pos))),
2763 node if node.is_ignored() => Node::Ignored(SK::QualifiedName),
2764 node => Node::QualifiedName(
2765 self.alloc((bumpalo::vec![in self.arena; node].into_bump_slice(), pos)),
2770 fn make_simple_type_specifier(&mut self, specifier: Self::R) -> Self::R {
2771 // Return this explicitly because flatten filters out zero nodes, and
2772 // we treat most non-error nodes as zeroes.
2776 fn make_literal_expression(&mut self, expression: Self::R) -> Self::R {
2780 fn make_simple_initializer(&mut self, equals: Self::R, expr: Self::R) -> Self::R {
2781 // If the expr is Ignored, bubble up the assignment operator so that we
2782 // can tell that *some* initializer was here. Useful for class
2783 // properties, where we need to enforce that properties without default
2784 // values are initialized in the constructor.
2785 if expr.is_ignored() { equals } else { expr }
2788 fn make_anonymous_function(
2790 _attribute_spec: Self::R,
2791 _async_keyword: Self::R,
2792 _function_keyword: Self::R,
2793 _left_paren: Self::R,
2794 _parameters: Self::R,
2795 _right_paren: Self::R,
2798 _readonly_return: Self::R,
2803 // do not allow Yield to bubble up
2804 Node::Ignored(SK::AnonymousFunction)
2807 fn make_lambda_expression(
2809 _attribute_spec: Self::R,
2811 _signature: Self::R,
2815 // do not allow Yield to bubble up
2816 Node::Ignored(SK::LambdaExpression)
2819 fn make_awaitable_creation_expression(
2821 _attribute_spec: Self::R,
2823 _compound_statement: Self::R,
2825 // do not allow Yield to bubble up
2826 Node::Ignored(SK::AwaitableCreationExpression)
2829 fn make_element_initializer(
2835 Node::ListItem(self.alloc((key, value)))
2838 fn make_prefix_unary_expression(&mut self, op: Self::R, value: Self::R) -> Self::R {
2839 let pos = self.merge_positions(op, value);
2840 let op = match op.token_kind() {
2841 Some(TokenKind::Tilde) => Uop::Utild,
2842 Some(TokenKind::Exclamation) => Uop::Unot,
2843 Some(TokenKind::Plus) => Uop::Uplus,
2844 Some(TokenKind::Minus) => Uop::Uminus,
2845 Some(TokenKind::PlusPlus) => Uop::Uincr,
2846 Some(TokenKind::MinusMinus) => Uop::Udecr,
2847 Some(TokenKind::At) => Uop::Usilence,
2848 _ => return Node::Ignored(SK::PrefixUnaryExpression),
2850 let value = match self.node_to_expr(value) {
2851 Some(value) => value,
2852 None => return Node::Ignored(SK::PrefixUnaryExpression),
2854 Node::Expr(self.alloc(aast::Expr(
2857 aast::Expr_::Unop(self.alloc((op, value))),
2861 fn make_postfix_unary_expression(&mut self, value: Self::R, op: Self::R) -> Self::R {
2862 let pos = self.merge_positions(value, op);
2863 let op = match op.token_kind() {
2864 Some(TokenKind::PlusPlus) => Uop::Upincr,
2865 Some(TokenKind::MinusMinus) => Uop::Updecr,
2866 _ => return Node::Ignored(SK::PostfixUnaryExpression),
2868 let value = match self.node_to_expr(value) {
2869 Some(value) => value,
2870 None => return Node::Ignored(SK::PostfixUnaryExpression),
2872 Node::Expr(self.alloc(aast::Expr(
2875 aast::Expr_::Unop(self.alloc((op, value))),
2879 fn make_binary_expression(&mut self, lhs: Self::R, op_node: Self::R, rhs: Self::R) -> Self::R {
2880 let op = match op_node.token_kind() {
2881 Some(TokenKind::Plus) => Bop::Plus,
2882 Some(TokenKind::Minus) => Bop::Minus,
2883 Some(TokenKind::Star) => Bop::Star,
2884 Some(TokenKind::Slash) => Bop::Slash,
2885 Some(TokenKind::Equal) => Bop::Eq(None),
2886 Some(TokenKind::EqualEqual) => Bop::Eqeq,
2887 Some(TokenKind::EqualEqualEqual) => Bop::Eqeqeq,
2888 Some(TokenKind::StarStar) => Bop::Starstar,
2889 Some(TokenKind::AmpersandAmpersand) => Bop::Ampamp,
2890 Some(TokenKind::BarBar) => Bop::Barbar,
2891 Some(TokenKind::LessThan) => Bop::Lt,
2892 Some(TokenKind::LessThanEqual) => Bop::Lte,
2893 Some(TokenKind::LessThanLessThan) => Bop::Ltlt,
2894 Some(TokenKind::GreaterThan) => Bop::Gt,
2895 Some(TokenKind::GreaterThanEqual) => Bop::Gte,
2896 Some(TokenKind::GreaterThanGreaterThan) => Bop::Gtgt,
2897 Some(TokenKind::Dot) => Bop::Dot,
2898 Some(TokenKind::Ampersand) => Bop::Amp,
2899 Some(TokenKind::Bar) => Bop::Bar,
2900 Some(TokenKind::Percent) => Bop::Percent,
2901 Some(TokenKind::QuestionQuestion) => Bop::QuestionQuestion,
2902 _ => return Node::Ignored(SK::BinaryExpression),
2905 match (&op, rhs.is_token(TokenKind::Yield)) {
2906 (Bop::Eq(_), true) => return rhs,
2910 let pos = self.merge(self.merge_positions(lhs, op_node), self.get_pos(rhs));
2912 let lhs = match self.node_to_expr(lhs) {
2914 None => return Node::Ignored(SK::BinaryExpression),
2916 let rhs = match self.node_to_expr(rhs) {
2918 None => return Node::Ignored(SK::BinaryExpression),
2921 Node::Expr(self.alloc(aast::Expr(
2924 aast::Expr_::Binop(self.alloc((op, lhs, rhs))),
2928 fn make_parenthesized_expression(
2934 if self.retain_or_omit_user_attributes_for_facts {
2935 Node::Ignored(SK::ParenthesizedExpression)
2941 fn make_keyset_intrinsic_expression(
2943 _keyset_intrinsic_keyword: Self::R,
2945 _left_bracket: Self::R,
2947 _right_bracket: Self::R,
2949 if !self.retain_or_omit_user_attributes_for_facts {
2950 Node::Ignored(SK::KeysetIntrinsicExpression)
2951 } else if let Node::List([node]) = expr {
2958 fn make_varray_intrinsic_expression(
2960 _varray_intrinsic_keyword: Self::R,
2962 _left_bracket: Self::R,
2964 _right_bracket: Self::R,
2966 if !self.retain_or_omit_user_attributes_for_facts {
2967 Node::Ignored(SK::VarrayIntrinsicExpression)
2968 } else if let Node::List([node]) = expr {
2975 fn make_darray_intrinsic_expression(
2977 _varray_intrinsic_keyword: Self::R,
2979 _left_bracket: Self::R,
2981 _right_bracket: Self::R,
2983 if !self.retain_or_omit_user_attributes_for_facts {
2984 Node::Ignored(SK::DarrayIntrinsicExpression)
2985 } else if let Node::List([node]) = expr {
2992 fn make_vector_intrinsic_expression(
2994 _vector_intrinsic_keyword: Self::R,
2996 _left_bracket: Self::R,
2998 _right_bracket: Self::R,
3000 if !self.retain_or_omit_user_attributes_for_facts {
3001 Node::Ignored(SK::VectorIntrinsicExpression)
3002 } else if let Node::List([node]) = expr {
3009 fn make_list_item(&mut self, item: Self::R, sep: Self::R) -> Self::R {
3010 match (item.is_ignored(), sep.is_ignored()) {
3011 (true, true) => Node::Ignored(SK::ListItem),
3012 (false, true) => item,
3013 (true, false) => sep,
3014 (false, false) => Node::ListItem(self.alloc((item, sep))),
3018 fn make_type_arguments(
3022 greater_than: Self::R,
3024 Node::BracketedList(self.alloc((
3025 self.get_pos(less_than),
3026 arguments.as_slice(self.arena),
3027 self.get_pos(greater_than),
3031 fn make_generic_type_specifier(
3033 class_type: Self::R,
3034 type_arguments: Self::R,
3036 let class_id = match self.expect_name(class_type) {
3038 None => return Node::Ignored(SK::GenericTypeSpecifier),
3040 match class_id.1.trim_start_matches("\\") {
3041 "varray_or_darray" | "vec_or_dict" => {
3042 let id_pos = class_id.0;
3043 let pos = self.merge(id_pos, self.get_pos(type_arguments));
3044 let type_arguments = type_arguments.as_slice(self.arena);
3045 let ty_ = match type_arguments {
3046 [tk, tv] => Ty_::TvecOrDict(
3048 self.node_to_ty(*tk)
3049 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
3050 self.node_to_ty(*tv)
3051 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
3054 [tv] => Ty_::TvecOrDict(
3056 self.vec_or_dict_key(pos),
3057 self.node_to_ty(*tv)
3058 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
3063 self.hint_ty(pos, ty_)
3066 let Id(pos, class_type) = class_id;
3067 match class_type.rsplit('\\').next() {
3068 Some(name) if self.is_type_param_in_scope(name) => {
3069 let pos = self.merge(pos, self.get_pos(type_arguments));
3070 let type_arguments = self.slice(
3073 .filter_map(|&node| self.node_to_ty(node)),
3075 let ty_ = if self.simplify_naming_for_facts {
3076 Ty_::Tgeneric(self.alloc((class_type, type_arguments)))
3078 Ty_::Tgeneric(self.alloc((name, type_arguments)))
3080 self.hint_ty(pos, ty_)
3083 let class_type = self.elaborate_raw_id(class_type);
3087 self.get_pos(type_arguments),
3095 fn make_record_declaration(
3097 attribute_spec: Self::R,
3099 record_keyword: Self::R,
3101 _extends_keyword: Self::R,
3102 extends_opt: Self::R,
3103 _left_brace: Self::R,
3105 right_brace: Self::R,
3107 let name = match self.elaborate_defined_id(name) {
3109 None => return Node::Ignored(SK::RecordDeclaration),
3111 let parsed_attributes = self.to_attributes(attribute_spec);
3114 self.alloc(typing_defs::RecordDefType {
3115 module: self.alloc(parsed_attributes.module),
3118 .expect_name(extends_opt)
3119 .map(|id| self.elaborate_id(id).into()),
3120 fields: self.slice(fields.iter().filter_map(|node| match node {
3121 Node::RecordField(&(id, req)) => Some((id.into(), req)),
3124 abstract_: modifier.is_token(TokenKind::Abstract),
3125 pos: self.pos_from_slice(&[attribute_spec, modifier, record_keyword, right_brace]),
3128 Node::Ignored(SK::RecordDeclaration)
3131 fn make_record_field(
3135 initializer: Self::R,
3136 _semicolon: Self::R,
3138 let name = match self.expect_name(name) {
3140 None => return Node::Ignored(SK::RecordField),
3142 let field_req = if initializer.is_ignored() {
3143 RecordFieldReq::ValueRequired
3145 RecordFieldReq::HasDefaultValue
3147 Node::RecordField(self.alloc((name, field_req)))
3150 fn make_alias_declaration(
3152 attributes: Self::R,
3155 generic_params: Self::R,
3156 constraint: Self::R,
3158 aliased_type: Self::R,
3159 _semicolon: Self::R,
3161 if name.is_ignored() {
3162 return Node::Ignored(SK::AliasDeclaration);
3164 let Id(pos, name) = match self.elaborate_defined_id(name) {
3166 None => return Node::Ignored(SK::AliasDeclaration),
3168 let ty = match self.node_to_ty(aliased_type) {
3170 None => return Node::Ignored(SK::AliasDeclaration),
3172 let constraint = match constraint {
3173 Node::TypeConstraint(&(_kind, hint)) => self.node_to_ty(hint),
3177 // Pop the type params stack only after creating all inner types.
3178 let tparams = self.pop_type_params(generic_params);
3179 let parsed_attributes = self.to_attributes(attributes);
3180 let user_attributes = if self.retain_or_omit_user_attributes_for_facts {
3181 self.slice(attributes.iter().rev().filter_map(|attribute| {
3182 if let Node::Attribute(attr) = attribute {
3183 Some(self.user_attribute_to_decl(attr))
3191 let typedef = self.alloc(TypedefType {
3192 module: self.alloc(parsed_attributes.module),
3194 vis: if parsed_attributes.internal {
3195 aast::TypedefVisibility::Tinternal
3197 match keyword.token_kind() {
3198 Some(TokenKind::Type) => aast::TypedefVisibility::Transparent,
3199 Some(TokenKind::Newtype) => aast::TypedefVisibility::Opaque,
3200 _ => aast::TypedefVisibility::Transparent,
3207 attributes: user_attributes,
3210 self.add_typedef(name, typedef);
3212 Node::Ignored(SK::AliasDeclaration)
3215 fn make_context_alias_declaration(
3217 attributes: Self::R,
3220 generic_params: Self::R,
3221 constraint: Self::R,
3224 _semicolon: Self::R,
3226 if name.is_ignored() {
3227 return Node::Ignored(SK::ContextAliasDeclaration);
3229 let Id(pos, name) = match self.elaborate_defined_id(name) {
3231 None => return Node::Ignored(SK::ContextAliasDeclaration),
3233 let ty = match self.node_to_ty(ctx_list) {
3235 None => self.alloc(Ty(
3236 self.alloc(Reason::hint(pos)),
3237 Ty_::Tapply(self.alloc(((pos, "\\HH\\Contexts\\defaults"), &[]))),
3241 // lowerer ensures there is only one as constraint
3242 let mut as_constraint = None;
3243 for c in constraint.iter() {
3244 if let Node::ContextConstraint(&(kind, hint)) = c {
3245 let ty = self.node_to_ty(hint);
3247 ConstraintKind::ConstraintAs => as_constraint = ty,
3252 // Pop the type params stack only after creating all inner types.
3253 let tparams = self.pop_type_params(generic_params);
3254 let parsed_attributes = self.to_attributes(attributes);
3255 let user_attributes = if self.retain_or_omit_user_attributes_for_facts {
3256 self.slice(attributes.iter().rev().filter_map(|attribute| {
3257 if let Node::Attribute(attr) = attribute {
3258 Some(self.user_attribute_to_decl(attr))
3266 let typedef = self.alloc(TypedefType {
3267 module: self.alloc(parsed_attributes.module),
3269 vis: if parsed_attributes.internal {
3270 aast::TypedefVisibility::Tinternal
3272 aast::TypedefVisibility::Opaque
3275 constraint: as_constraint,
3278 attributes: user_attributes,
3281 self.add_typedef(name, typedef);
3283 Node::Ignored(SK::ContextAliasDeclaration)
3286 fn make_type_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3287 let kind = match kind.token_kind() {
3288 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3289 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3290 _ => return Node::Ignored(SK::TypeConstraint),
3292 Node::TypeConstraint(self.alloc((kind, value)))
3295 fn make_context_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3296 let kind = match kind.token_kind() {
3297 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3298 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3299 _ => return Node::Ignored(SK::ContextConstraint),
3301 Node::ContextConstraint(self.alloc((kind, value)))
3304 fn make_type_parameter(
3306 user_attributes: Self::R,
3310 tparam_params: Self::R,
3311 constraints: Self::R,
3313 let user_attributes = match user_attributes {
3314 Node::BracketedList((_, attributes, _)) => {
3315 self.slice(attributes.into_iter().filter_map(|x| match x {
3316 Node::Attribute(a) => Some(*a),
3323 let constraints = self.slice(constraints.iter().filter_map(|node| match node {
3324 Node::TypeConstraint(&constraint) => Some(constraint),
3328 // TODO(T70068435) Once we add support for constraints on higher-kinded types
3329 // (in particular, constraints on nested type parameters), we need to ensure
3330 // that we correctly handle the scoping of nested type parameters.
3331 // This includes making sure that the call to convert_type_appl_to_generic
3332 // in make_type_parameters handles nested constraints.
3333 // For now, we just make sure that the nested type parameters that make_type_parameters
3334 // added to the global list of in-scope type parameters are removed immediately:
3335 self.pop_type_params(tparam_params);
3337 let tparam_params = match tparam_params {
3338 Node::TypeParameters(¶ms) => params,
3342 Node::TypeParameter(self.alloc(TypeParameterDecl {
3344 variance: match variance.token_kind() {
3345 Some(TokenKind::Minus) => Variance::Contravariant,
3346 Some(TokenKind::Plus) => Variance::Covariant,
3347 _ => Variance::Invariant,
3349 reified: if reify.is_token(TokenKind::Reify) {
3350 if user_attributes.iter().any(|node| node.name.1 == "__Soft") {
3351 aast::ReifyKind::SoftReified
3353 aast::ReifyKind::Reified
3356 aast::ReifyKind::Erased
3364 fn make_type_parameters(&mut self, _lt: Self::R, tparams: Self::R, _gt: Self::R) -> Self::R {
3365 let size = tparams.len();
3366 let mut tparams_with_name = Vec::with_capacity_in(size, self.arena);
3367 let mut tparam_names = MultiSetMut::with_capacity_in(size, self.arena);
3368 for node in tparams.iter() {
3370 &Node::TypeParameter(decl) => {
3371 let name = match decl.name.as_id() {
3373 None => return Node::Ignored(SK::TypeParameters),
3375 tparam_names.insert(name.1);
3376 tparams_with_name.push((decl, name));
3381 Rc::make_mut(&mut self.type_parameters).push(tparam_names.into());
3382 let mut tparams = Vec::with_capacity_in(tparams_with_name.len(), self.arena);
3383 for (decl, name) in tparams_with_name.into_iter() {
3384 let &TypeParameterDecl {
3392 let constraints = self.slice(constraints.iter().filter_map(|constraint| {
3393 let &(kind, ty) = constraint;
3394 let ty = self.node_to_ty(ty)?;
3395 let ty = self.convert_tapply_to_tgeneric(ty);
3399 let user_attributes = self.slice(
3403 .map(|x| self.user_attribute_to_decl(x)),
3405 tparams.push(self.alloc(Tparam {
3411 tparams: tparam_params,
3414 Node::TypeParameters(self.alloc(tparams.into_bump_slice()))
3417 fn make_parameter_declaration(
3419 attributes: Self::R,
3420 visibility: Self::R,
3425 initializer: Self::R,
3427 let (variadic, pos, name) = match name {
3428 Node::ListItem(&(ellipsis, id)) => {
3429 let Id(pos, name) = match id.as_variable() {
3431 None => return Node::Ignored(SK::ParameterDeclaration),
3433 let variadic = ellipsis.is_token(TokenKind::DotDotDot);
3434 (variadic, pos, Some(name))
3437 let Id(pos, name) = match name.as_variable() {
3439 None => return Node::Ignored(SK::ParameterDeclaration),
3441 (false, pos, Some(name))
3444 let kind = if inout.is_token(TokenKind::Inout) {
3449 let is_readonly = readonly.is_token(TokenKind::Readonly);
3450 let hint = if self.opts.interpret_soft_types_as_like_types {
3451 let attributes = self.to_attributes(attributes);
3452 if attributes.soft {
3454 Node::Ty(ty) => self.hint_ty(self.get_pos(hint), Ty_::Tlike(ty)),
3463 Node::FunParam(self.alloc(FunParamDecl {
3467 readonly: is_readonly,
3476 fn make_variadic_parameter(&mut self, _: Self::R, hint: Self::R, ellipsis: Self::R) -> Self::R {
3478 self.alloc(FunParamDecl {
3479 attributes: Node::Ignored(SK::Missing),
3480 visibility: Node::Ignored(SK::Missing),
3481 kind: ParamMode::FPnormal,
3486 .unwrap_or_else(|| self.get_pos(ellipsis)),
3489 initializer: Node::Ignored(SK::Missing),
3494 fn make_function_declaration(
3496 attributes: Self::R,
3500 let parsed_attributes = self.to_attributes(attributes);
3502 Node::FunctionHeader(header) => {
3503 let is_method = false;
3504 let ((pos, name), type_, _) =
3505 match self.function_to_ty(is_method, attributes, header, body) {
3507 None => return Node::Ignored(SK::FunctionDeclaration),
3509 let deprecated = parsed_attributes.deprecated.map(|msg| {
3510 let mut s = String::new_in(self.arena);
3511 s.push_str("The function ");
3512 s.push_str(name.trim_start_matches("\\"));
3513 s.push_str(" is deprecated: ");
3517 let fun_elt = self.alloc(FunElt {
3518 module: self.alloc(parsed_attributes.module),
3519 internal: parsed_attributes.internal,
3523 php_std_lib: parsed_attributes.php_std_lib,
3524 support_dynamic_type: self.opts.everything_sdt
3525 || parsed_attributes.support_dynamic_type,
3527 self.add_fun(name, fun_elt);
3528 Node::Ignored(SK::FunctionDeclaration)
3530 _ => Node::Ignored(SK::FunctionDeclaration),
3536 left_bracket: Self::R,
3538 right_bracket: Self::R,
3540 let tys = self.slice(tys.iter().filter_map(|ty| match ty {
3541 Node::ListItem(&(ty, _)) | &ty => {
3542 // A wildcard is used for the context of a closure type on a
3543 // parameter of a function with a function context (e.g.,
3544 // `function f((function ()[_]: void) $f)[ctx $f]: void {}`).
3545 if let Some(Id(pos, "_")) = self.expect_name(ty) {
3546 return Some(self.alloc(Ty(
3547 self.alloc(Reason::hint(pos)),
3548 Ty_::Tapply(self.alloc(((pos, "_"), &[]))),
3551 let ty = self.node_to_ty(ty)?;
3553 // Only three forms of type can appear here in a valid program:
3554 // - function contexts (`ctx $f`)
3555 // - value-dependent paths (`$v::C`)
3556 // - built-in contexts (`rx`, `cipp_of<EntFoo>`)
3557 // The first and last will be represented with `Tapply`,
3558 // but function contexts will use a variable name
3559 // (containing a `$`). Built-in contexts are always in the
3560 // \HH\Contexts namespace, so we rewrite those names here.
3561 Ty_::Tapply(&((pos, name), targs)) if !name.starts_with('$') => {
3562 // The name will have been elaborated in the current
3563 // namespace, but we actually want it to be in the
3564 // \HH\Contexts namespace. Grab the last component of
3565 // the name, and rewrite it in the correct namespace.
3566 // Note that this makes it impossible to express names
3567 // in any sub-namespace of \HH\Contexts (e.g.,
3568 // "Unsafe\\cipp" will be rewritten as
3569 // "\\HH\\Contexts\\cipp" rather than
3570 // "\\HH\\Contexts\\Unsafe\\cipp").
3571 let name = match name.trim_end_matches('\\').split('\\').next_back() {
3573 if let Some(first_char) = ctxname.chars().nth(0) {
3574 if first_char.is_lowercase() {
3575 self.concat("\\HH\\Contexts\\", ctxname)
3585 Some(self.alloc(Ty(ty.0, Ty_::Tapply(self.alloc(((pos, name), targs))))))
3591 /* Like in as_fun_implicit_params, we keep the intersection as is: we do not simplify
3592 * empty or singleton intersections.
3594 let pos = self.merge_positions(left_bracket, right_bracket);
3595 self.hint_ty(pos, Ty_::Tintersection(tys))
3598 fn make_function_ctx_type_specifier(
3600 ctx_keyword: Self::R,
3603 match variable.as_variable() {
3604 Some(Id(pos, name)) => {
3605 Node::Variable(self.alloc((name, self.merge(pos, self.get_pos(ctx_keyword)))))
3607 None => Node::Ignored(SK::FunctionCtxTypeSpecifier),
3611 fn make_function_declaration_header(
3616 type_params: Self::R,
3617 left_paren: Self::R,
3618 param_list: Self::R,
3619 _right_paren: Self::R,
3620 capability: Self::R,
3622 readonly_return: Self::R,
3624 where_constraints: Self::R,
3626 // Use the position of the left paren if the name is missing.
3627 // Keep the name if it's an IgnoredToken rather than an Ignored. An
3628 // IgnoredToken here should always be an error, but it's better to treat
3629 // a keyword as a name than to claim the function has no name at all.
3630 let name = if matches!(name, Node::Ignored(..)) {
3635 Node::FunctionHeader(self.alloc(FunctionHeader {
3647 fn make_yield_expression(&mut self, keyword: Self::R, _operand: Self::R) -> Self::R {
3648 assert!(keyword.token_kind() == Some(TokenKind::Yield));
3652 fn make_const_declaration(
3655 const_keyword: Self::R,
3664 .classish_name_builder
3665 .get_current_classish_name()
3668 let ty = self.node_to_ty(hint);
3670 self.alloc(self.slice(consts.iter().filter_map(|cst| match cst {
3671 Node::ConstInitializer(&(name, initializer, refs)) => {
3672 let id = name.as_id()?;
3673 let modifiers = read_member_modifiers(modifiers.iter());
3674 let abstract_ = if modifiers.is_abstract {
3675 ClassConstKind::CCAbstract(!initializer.is_ignored())
3677 ClassConstKind::CCConcrete
3680 .or_else(|| self.infer_const(name, initializer))
3681 .unwrap_or_else(|| tany());
3682 Some(Node::Const(self.alloc(
3683 shallow_decl_defs::ShallowClassConst {
3696 Node::List(consts) => {
3697 // This case always returns Node::Ignored,
3698 // but has the side effect of calling self.add_const
3700 // Note: given "const int X=1,Y=2;", the legacy decl-parser
3701 // allows both decls, and it gives them both an identical text-span -
3702 // from start of "const" to end of semicolon. This is a bug but
3703 // the code here preserves it.
3704 let pos = self.merge_positions(const_keyword, semicolon);
3705 for cst in consts.iter() {
3707 Node::ConstInitializer(&(name, initializer, _refs)) => {
3708 if let Some(Id(id_pos, id)) = self.elaborate_defined_id(name) {
3711 .or_else(|| self.infer_const(name, initializer))
3712 .unwrap_or_else(|| self.tany_with_pos(id_pos));
3713 self.add_const(id, self.alloc(ConstDecl { pos, type_: ty }));
3719 Node::Ignored(SK::ConstDeclaration)
3721 _ => Node::Ignored(SK::ConstDeclaration),
3725 fn begin_constant_declarator(&mut self) {
3726 self.start_accumulating_const_refs();
3729 fn make_constant_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
3730 // The "X=1" part of either a member const "class C {const int X=1;}" or a top-level const "const int X=1;"
3731 // Note: the the declarator itself doesn't yet know whether a type was provided by the user;
3732 // that's only known in the parent, make_const_declaration
3733 let refs = self.stop_accumulating_const_refs();
3734 if name.is_ignored() {
3735 Node::Ignored(SK::ConstantDeclarator)
3737 Node::ConstInitializer(self.alloc((name, initializer, refs)))
3741 fn make_namespace_declaration(&mut self, _name: Self::R, body: Self::R) -> Self::R {
3742 if let Node::Ignored(SK::NamespaceBody) = body {
3743 Rc::make_mut(&mut self.namespace_builder).pop_namespace();
3745 Node::Ignored(SK::NamespaceDeclaration)
3748 fn make_namespace_declaration_header(&mut self, _keyword: Self::R, name: Self::R) -> Self::R {
3749 let name = self.expect_name(name).map(|Id(_, name)| name);
3750 // if this is header of semicolon-style (one with NamespaceEmptyBody) namespace, we should pop
3751 // the previous namespace first, but we don't have the body yet. We'll fix it retroactively in
3752 // make_namespace_empty_body
3753 Rc::make_mut(&mut self.namespace_builder).push_namespace(name);
3754 Node::Ignored(SK::NamespaceDeclarationHeader)
3757 fn make_namespace_body(
3759 _left_brace: Self::R,
3760 _declarations: Self::R,
3761 _right_brace: Self::R,
3763 Node::Ignored(SK::NamespaceBody)
3766 fn make_namespace_empty_body(&mut self, _semicolon: Self::R) -> Self::R {
3767 Rc::make_mut(&mut self.namespace_builder).pop_previous_namespace();
3768 Node::Ignored(SK::NamespaceEmptyBody)
3771 fn make_namespace_use_declaration(
3774 namespace_use_kind: Self::R,
3776 _semicolon: Self::R,
3778 if let Some(import_kind) = Self::namespace_use_kind(&namespace_use_kind) {
3779 for clause in clauses.iter() {
3780 if let Node::NamespaceUseClause(nuc) = clause {
3781 Rc::make_mut(&mut self.namespace_builder).add_import(
3789 Node::Ignored(SK::NamespaceUseDeclaration)
3792 fn make_namespace_group_use_declaration(
3797 _left_brace: Self::R,
3799 _right_brace: Self::R,
3800 _semicolon: Self::R,
3802 let Id(_, prefix) = match self.expect_name(prefix) {
3804 None => return Node::Ignored(SK::NamespaceGroupUseDeclaration),
3806 for clause in clauses.iter() {
3807 if let Node::NamespaceUseClause(nuc) = clause {
3808 let mut id = String::new_in(self.arena);
3809 id.push_str(prefix);
3810 id.push_str(nuc.id.1);
3811 Rc::make_mut(&mut self.namespace_builder).add_import(
3818 Node::Ignored(SK::NamespaceGroupUseDeclaration)
3821 fn make_namespace_use_clause(
3823 clause_kind: Self::R,
3826 aliased_name: Self::R,
3828 let id = match self.expect_name(name) {
3830 None => return Node::Ignored(SK::NamespaceUseClause),
3832 let as_ = if as_.is_token(TokenKind::As) {
3833 match aliased_name.as_id() {
3834 Some(name) => Some(name.1),
3835 None => return Node::Ignored(SK::NamespaceUseClause),
3840 if let Some(kind) = Self::namespace_use_kind(&clause_kind) {
3841 Node::NamespaceUseClause(self.alloc(NamespaceUseClause { kind, id, as_ }))
3843 Node::Ignored(SK::NamespaceUseClause)
3847 fn make_where_clause(&mut self, _: Self::R, where_constraints: Self::R) -> Self::R {
3851 fn make_where_constraint(
3855 right_type: Self::R,
3857 Node::WhereConstraint(self.alloc(WhereConstraint(
3858 self.node_to_ty(left_type).unwrap_or_else(|| tany()),
3859 match operator.token_kind() {
3860 Some(TokenKind::Equal) => ConstraintKind::ConstraintEq,
3861 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3862 _ => ConstraintKind::ConstraintAs,
3864 self.node_to_ty(right_type).unwrap_or_else(|| tany()),
3868 fn make_classish_declaration(
3870 attributes: Self::R,
3872 xhp_keyword: Self::R,
3873 class_keyword: Self::R,
3876 _extends_keyword: Self::R,
3878 _implements_keyword: Self::R,
3879 implements: Self::R,
3880 where_clause: Self::R,
3883 let raw_name = match self.expect_name(name) {
3884 Some(Id(_, name)) => name,
3885 None => return Node::Ignored(SK::ClassishDeclaration),
3887 let Id(pos, name) = match self.elaborate_defined_id(name) {
3889 None => return Node::Ignored(SK::ClassishDeclaration),
3891 let is_xhp = raw_name.starts_with(':') || xhp_keyword.is_present();
3893 let mut final_ = false;
3894 let mut abstract_ = false;
3896 for modifier in modifiers.iter() {
3897 match modifier.token_kind() {
3898 Some(TokenKind::Abstract) => {
3901 Some(TokenKind::Final) => final_ = true,
3905 let class_kind = match class_keyword.token_kind() {
3906 Some(TokenKind::Interface) => ClassishKind::Cinterface,
3907 Some(TokenKind::Trait) => ClassishKind::Ctrait,
3908 _ => ClassishKind::Cclass(if abstract_ {
3909 &Abstraction::Abstract
3911 &Abstraction::Concrete
3915 let where_constraints = self.slice(where_clause.iter().filter_map(|&x| match x {
3916 Node::WhereConstraint(x) => Some(x),
3920 let body = match body {
3921 Node::ClassishBody(body) => body,
3922 _ => return Node::Ignored(SK::ClassishDeclaration),
3925 let mut uses_len = 0;
3926 let mut xhp_attr_uses_len = 0;
3927 let mut xhp_enum_values = SMap::empty();
3928 let mut req_extends_len = 0;
3929 let mut req_implements_len = 0;
3930 let mut consts_len = 0;
3931 let mut typeconsts_len = 0;
3932 let mut props_len = 0;
3933 let mut sprops_len = 0;
3934 let mut static_methods_len = 0;
3935 let mut methods_len = 0;
3937 let mut user_attributes_len = 0;
3938 for attribute in attributes.iter() {
3940 &Node::Attribute(..) => user_attributes_len += 1,
3945 for element in body.iter().copied() {
3947 Node::TraitUse(names) => uses_len += names.len(),
3948 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
3950 xhp_attr_uses_decls,
3951 xhp_attr_enum_values,
3953 props_len += xhp_attr_decls.len();
3954 xhp_attr_uses_len += xhp_attr_uses_decls.len();
3956 for (name, values) in xhp_attr_enum_values {
3957 xhp_enum_values = xhp_enum_values.add(self.arena, name, *values);
3960 Node::TypeConstant(..) => typeconsts_len += 1,
3961 Node::RequireClause(require) => match require.require_type.token_kind() {
3962 Some(TokenKind::Extends) => req_extends_len += 1,
3963 Some(TokenKind::Implements) => req_implements_len += 1,
3966 Node::List(consts @ [Node::Const(..), ..]) => consts_len += consts.len(),
3967 Node::Property(&PropertyNode { decls, is_static }) => {
3969 sprops_len += decls.len()
3971 props_len += decls.len()
3974 Node::Constructor(&ConstructorNode { properties, .. }) => {
3975 props_len += properties.len()
3977 Node::Method(&MethodNode { is_static, .. }) => {
3979 static_methods_len += 1
3988 let mut constructor = None;
3990 let mut uses = Vec::with_capacity_in(uses_len, self.arena);
3991 let mut xhp_attr_uses = Vec::with_capacity_in(xhp_attr_uses_len, self.arena);
3992 let mut req_extends = Vec::with_capacity_in(req_extends_len, self.arena);
3993 let mut req_implements = Vec::with_capacity_in(req_implements_len, self.arena);
3994 let mut consts = Vec::with_capacity_in(consts_len, self.arena);
3995 let mut typeconsts = Vec::with_capacity_in(typeconsts_len, self.arena);
3996 let mut props = Vec::with_capacity_in(props_len, self.arena);
3997 let mut sprops = Vec::with_capacity_in(sprops_len, self.arena);
3998 let mut static_methods = Vec::with_capacity_in(static_methods_len, self.arena);
3999 let mut methods = Vec::with_capacity_in(methods_len, self.arena);
4001 let mut user_attributes = Vec::with_capacity_in(user_attributes_len, self.arena);
4002 for attribute in attributes.iter() {
4004 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(&attr)),
4008 // Match ordering of attributes produced by the OCaml decl parser (even
4009 // though it's the reverse of the syntactic ordering).
4010 user_attributes.reverse();
4012 let class_attributes = self.to_attributes(attributes);
4014 // xhp props go after regular props, regardless of their order in file
4015 let mut xhp_props = vec![];
4017 for element in body.iter().copied() {
4019 Node::TraitUse(names) => {
4020 uses.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
4022 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
4024 xhp_attr_uses_decls,
4027 xhp_props.extend(xhp_attr_decls);
4028 xhp_attr_uses.extend(
4031 .filter_map(|&node| self.node_to_ty(node)),
4034 Node::TypeConstant(constant) => typeconsts.push(constant),
4035 Node::RequireClause(require) => match require.require_type.token_kind() {
4036 Some(TokenKind::Extends) => {
4037 req_extends.extend(self.node_to_ty(require.name).iter())
4039 Some(TokenKind::Implements) => {
4040 req_implements.extend(self.node_to_ty(require.name).iter())
4044 Node::List(&const_nodes @ [Node::Const(..), ..]) => {
4045 for node in const_nodes {
4046 if let &Node::Const(decl) = node {
4051 Node::Property(&PropertyNode { decls, is_static }) => {
4052 for property in decls {
4054 sprops.push(property)
4056 props.push(property)
4060 Node::Constructor(&ConstructorNode { method, properties }) => {
4061 constructor = Some(method);
4062 for property in properties {
4063 props.push(property)
4066 Node::Method(&MethodNode { method, is_static }) => {
4067 // Annoyingly, the <<__SupportDynamicType>> annotation on a
4068 // class implicitly changes the decls of every method inside
4069 // it, so we have to reallocate them here.
4070 let method = if class_attributes.support_dynamic_type
4071 && !method.flags.contains(MethodFlags::SUPPORT_DYNAMIC_TYPE)
4073 let type_ = match method.type_.1 {
4075 let flags = ft.flags | FunTypeFlags::SUPPORT_DYNAMIC_TYPE;
4076 let ft = self.alloc(FunType { flags, ..*ft });
4077 self.alloc(Ty(method.type_.0, Ty_::Tfun(ft)))
4081 let flags = method.flags | MethodFlags::SUPPORT_DYNAMIC_TYPE;
4082 self.alloc(ShallowMethod {
4091 static_methods.push(method);
4093 methods.push(method);
4096 _ => {} // It's not our job to report errors here.
4100 props.extend(xhp_props.into_iter());
4102 if class_attributes.const_ {
4103 for prop in props.iter_mut() {
4104 if !prop.flags.contains(PropFlags::CONST) {
4105 *prop = self.alloc(ShallowProp {
4106 flags: prop.flags | PropFlags::CONST,
4113 let uses = uses.into_bump_slice();
4114 let xhp_attr_uses = xhp_attr_uses.into_bump_slice();
4115 let req_extends = req_extends.into_bump_slice();
4116 let req_implements = req_implements.into_bump_slice();
4117 let consts = consts.into_bump_slice();
4118 let typeconsts = typeconsts.into_bump_slice();
4119 let props = props.into_bump_slice();
4120 let sprops = sprops.into_bump_slice();
4121 let static_methods = static_methods.into_bump_slice();
4122 let methods = methods.into_bump_slice();
4123 let user_attributes = user_attributes.into_bump_slice();
4124 let extends = self.slice(extends.iter().filter_map(|&node| self.node_to_ty(node)));
4125 let implements = self.slice(implements.iter().filter_map(|&node| self.node_to_ty(node)));
4126 let support_dynamic_type =
4127 self.opts.everything_sdt || class_attributes.support_dynamic_type;
4128 // Pop the type params stack only after creating all inner types.
4129 let tparams = self.pop_type_params(tparams);
4130 let module = class_attributes.module;
4132 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4133 mode: self.file_mode,
4137 has_xhp_keyword: xhp_keyword.is_token(TokenKind::XHP),
4139 module: self.alloc(module),
4150 support_dynamic_type,
4161 self.add_class(name, cls);
4163 self.classish_name_builder.parsed_classish_declaration();
4165 Node::Ignored(SK::ClassishDeclaration)
4168 fn make_property_declaration(
4173 declarators: Self::R,
4174 _semicolon: Self::R,
4176 let (attrs, modifiers, hint) = (attrs, modifiers, hint);
4177 let modifiers = read_member_modifiers(modifiers.iter());
4178 let declarators = self.slice(declarators.iter().filter_map(
4179 |declarator| match declarator {
4180 Node::ListItem(&(name, initializer)) => {
4181 let attributes = self.to_attributes(attrs);
4182 let Id(pos, name) = name.as_variable()?;
4183 let name = if modifiers.is_static {
4186 strip_dollar_prefix(name)
4188 let ty = self.rewrite_ty_for_global_inference(
4189 self.node_to_non_ret_ty(hint),
4190 Reason::RglobalClassProp(pos),
4192 let ty = if self.opts.interpret_soft_types_as_like_types {
4193 if attributes.soft {
4196 self.alloc(Reason::hint(self.get_pos(hint))),
4206 let needs_init = if self.file_mode == Mode::Mhhi {
4209 initializer.is_ignored()
4211 let mut flags = PropFlags::empty();
4212 flags.set(PropFlags::CONST, attributes.const_);
4213 flags.set(PropFlags::LATEINIT, attributes.late_init);
4214 flags.set(PropFlags::LSB, attributes.lsb);
4215 flags.set(PropFlags::NEEDS_INIT, needs_init);
4216 flags.set(PropFlags::ABSTRACT, modifiers.is_abstract);
4217 flags.set(PropFlags::READONLY, modifiers.is_readonly);
4218 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
4223 visibility: if attributes.internal
4224 && modifiers.visibility == aast::Visibility::Public
4226 aast::Visibility::Internal
4228 modifiers.visibility
4236 Node::Property(self.alloc(PropertyNode {
4238 is_static: modifiers.is_static,
4242 fn make_xhp_class_attribute_declaration(
4245 attributes: Self::R,
4246 _semicolon: Self::R,
4248 let mut xhp_attr_enum_values = Vec::new_in(self.arena);
4250 let xhp_attr_decls = self.slice(attributes.iter().filter_map(|node| {
4251 let node = match node {
4252 Node::XhpClassAttribute(x) => x,
4255 let Id(pos, name) = node.name;
4256 let name = prefix_colon(self.arena, name);
4258 let (type_, enum_values) = match node.hint {
4259 Node::XhpEnumTy((ty, values)) => (Some(*ty), Some(values)),
4260 _ => (self.node_to_ty(node.hint), None),
4262 if let Some(enum_values) = enum_values {
4263 xhp_attr_enum_values.push((name, *enum_values));
4266 let type_ = if node.nullable && node.tag.is_none() {
4267 type_.and_then(|x| match x {
4269 Ty(_, Ty_::Toption(_)) | Ty(_, Ty_::Tmixed) => type_,
4271 _ => self.node_to_ty(self.hint_ty(x.get_pos()?, Ty_::Toption(x))),
4277 let mut flags = PropFlags::empty();
4278 flags.set(PropFlags::NEEDS_INIT, node.needs_init);
4281 visibility: aast::Visibility::Public,
4283 xhp_attr: Some(shallow_decl_defs::XhpAttr {
4285 has_default: !node.needs_init,
4291 let xhp_attr_uses_decls = self.slice(attributes.iter().filter_map(|x| match x {
4292 Node::XhpAttributeUse(&name) => Some(name),
4296 Node::XhpClassAttributeDeclaration(self.alloc(XhpClassAttributeDeclarationNode {
4297 xhp_attr_enum_values: xhp_attr_enum_values.into_bump_slice(),
4299 xhp_attr_uses_decls,
4303 /// Handle XHP attribute enum declarations.
4305 /// class :foo implements XHPChild {
4307 /// enum {'big', 'small'} size; // this line
4309 fn make_xhp_enum_type(
4311 enum_keyword: Self::R,
4312 _left_brace: Self::R,
4313 xhp_enum_values: Self::R,
4314 right_brace: Self::R,
4316 // Infer the type hint from the first value.
4317 // TODO: T88207956 consider all the values.
4318 let ty = xhp_enum_values
4321 .and_then(|node| self.node_to_ty(*node))
4322 .and_then(|node_ty| {
4323 let pos = self.merge_positions(enum_keyword, right_brace);
4324 let ty_ = node_ty.1;
4325 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
4328 let mut values = Vec::new_in(self.arena);
4329 for node in xhp_enum_values.iter() {
4330 // XHP enum values may only be string or int literals.
4332 Node::IntLiteral(&(s, _)) => {
4333 let i = s.parse::<isize>().unwrap_or(0);
4334 values.push(XhpEnumValue::XEVInt(i));
4336 Node::StringLiteral(&(s, _)) => {
4337 let owned_str = std::string::String::from_utf8_lossy(s);
4338 values.push(XhpEnumValue::XEVString(self.arena.alloc_str(&owned_str)));
4345 Some(ty) => Node::XhpEnumTy(self.alloc((&ty, values.into_bump_slice()))),
4346 None => Node::Ignored(SK::XHPEnumType),
4350 fn make_xhp_class_attribute(
4354 initializer: Self::R,
4357 let name = match name.as_id() {
4359 None => return Node::Ignored(SK::XHPClassAttribute),
4361 Node::XhpClassAttribute(self.alloc(XhpClassAttributeNode {
4364 needs_init: !initializer.is_present(),
4365 tag: match tag.token_kind() {
4366 Some(TokenKind::Required) => Some(XhpAttrTag::Required),
4367 Some(TokenKind::Lateinit) => Some(XhpAttrTag::Lateinit),
4370 nullable: initializer.is_token(TokenKind::NullLiteral) || !initializer.is_present(),
4374 fn make_xhp_simple_class_attribute(&mut self, name: Self::R) -> Self::R {
4375 Node::XhpAttributeUse(self.alloc(name))
4378 fn make_property_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
4379 Node::ListItem(self.alloc((name, initializer)))
4382 fn make_methodish_declaration(
4389 let header = match header {
4390 Node::FunctionHeader(header) => header,
4391 _ => return Node::Ignored(SK::MethodishDeclaration),
4393 // If we don't have a body, use the closing token. A closing token of
4394 // '}' indicates a regular function, while a closing token of ';'
4395 // indicates an abstract function.
4396 let body = if body.is_ignored() { closer } else { body };
4397 let modifiers = read_member_modifiers(header.modifiers.iter());
4398 let is_constructor = header.name.is_token(TokenKind::Construct);
4399 let is_method = true;
4400 let (id, ty, properties) = match self.function_to_ty(is_method, attrs, header, body) {
4401 Some(tuple) => tuple,
4402 None => return Node::Ignored(SK::MethodishDeclaration),
4404 let attributes = self.to_attributes(attrs);
4405 let deprecated = attributes.deprecated.map(|msg| {
4406 let mut s = String::new_in(self.arena);
4407 s.push_str("The method ");
4409 s.push_str(" is deprecated: ");
4413 let mut flags = MethodFlags::empty();
4415 MethodFlags::ABSTRACT,
4416 self.classish_name_builder.in_interface() || modifiers.is_abstract,
4418 flags.set(MethodFlags::FINAL, modifiers.is_final);
4419 flags.set(MethodFlags::OVERRIDE, attributes.override_);
4421 MethodFlags::DYNAMICALLYCALLABLE,
4422 attributes.dynamically_callable,
4424 flags.set(MethodFlags::PHP_STD_LIB, attributes.php_std_lib);
4426 MethodFlags::SUPPORT_DYNAMIC_TYPE,
4427 !is_constructor && attributes.support_dynamic_type,
4429 let visibility = match modifiers.visibility {
4430 aast::Visibility::Public => {
4431 if attributes.internal {
4432 aast::Visibility::Internal
4434 aast::Visibility::Public
4437 _ => modifiers.visibility,
4440 let mut user_attributes = Vec::new_in(self.arena);
4441 if self.retain_or_omit_user_attributes_for_facts {
4442 for attribute in attrs.iter() {
4444 Node::Attribute(attr) => {
4445 user_attributes.push(self.user_attribute_to_decl(attr))
4450 // Match ordering of attributes produced by the OCaml decl parser (even
4451 // though it's the reverse of the syntactic ordering).
4452 user_attributes.reverse();
4454 let user_attributes = user_attributes.into_bump_slice();
4456 let method = self.alloc(ShallowMethod {
4462 attributes: user_attributes,
4465 Node::Constructor(self.alloc(ConstructorNode { method, properties }))
4467 Node::Method(self.alloc(MethodNode {
4469 is_static: modifiers.is_static,
4474 fn make_classish_body(
4476 _left_brace: Self::R,
4478 _right_brace: Self::R,
4480 Node::ClassishBody(self.alloc(elements.as_slice(self.arena)))
4483 fn make_enum_declaration(
4485 attributes: Self::R,
4490 constraint: Self::R,
4491 _left_brace: Self::R,
4492 use_clauses: Self::R,
4493 enumerators: Self::R,
4494 _right_brace: Self::R,
4496 let id = match self.elaborate_defined_id(name) {
4498 None => return Node::Ignored(SK::EnumDeclaration),
4500 let hint = match self.node_to_ty(extends) {
4502 None => return Node::Ignored(SK::EnumDeclaration),
4504 let extends = match self.node_to_ty(self.make_apply(
4505 (self.get_pos(name), "\\HH\\BuiltinEnum"),
4510 None => return Node::Ignored(SK::EnumDeclaration),
4513 let consts = self.slice(enumerators.iter().filter_map(|node| match node {
4514 &Node::Const(const_) => Some(const_),
4517 let mut user_attributes = Vec::with_capacity_in(attributes.len(), self.arena);
4518 for attribute in attributes.iter() {
4520 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4524 // Match ordering of attributes produced by the OCaml decl parser (even
4525 // though it's the reverse of the syntactic ordering).
4526 user_attributes.reverse();
4527 let user_attributes = user_attributes.into_bump_slice();
4529 let constraint = match constraint {
4530 Node::TypeConstraint(&(_kind, ty)) => self.node_to_ty(ty),
4534 let mut includes_len = 0;
4535 for element in use_clauses.iter() {
4537 Node::EnumUse(names) => includes_len += names.len(),
4541 let mut includes = Vec::with_capacity_in(includes_len, self.arena);
4542 for element in use_clauses.iter() {
4544 Node::EnumUse(names) => {
4545 includes.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
4550 let includes = includes.into_bump_slice();
4552 let parsed_attributes = self.to_attributes(attributes);
4554 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4555 mode: self.file_mode,
4559 has_xhp_keyword: false,
4560 kind: ClassishKind::Cenum,
4561 module: self.alloc(parsed_attributes.module),
4564 where_constraints: &[],
4565 extends: bumpalo::vec![in self.arena; extends].into_bump_slice(),
4568 xhp_enum_values: SMap::empty(),
4570 req_implements: &[],
4572 support_dynamic_type: parsed_attributes.support_dynamic_type,
4578 static_methods: &[],
4581 enum_type: Some(self.alloc(EnumType {
4587 self.add_class(key, cls);
4589 self.classish_name_builder.parsed_classish_declaration();
4591 Node::Ignored(SK::EnumDeclaration)
4594 fn make_enum_use(&mut self, _keyword: Self::R, names: Self::R, _semicolon: Self::R) -> Self::R {
4595 Node::EnumUse(self.alloc(names))
4598 fn begin_enumerator(&mut self) {
4599 self.start_accumulating_const_refs();
4607 _semicolon: Self::R,
4609 let refs = self.stop_accumulating_const_refs();
4610 let id = match self.expect_name(name) {
4612 None => return Node::Ignored(SyntaxKind::Enumerator),
4616 self.alloc(ShallowClassConst {
4617 abstract_: ClassConstKind::CCConcrete,
4620 .infer_const(name, value)
4621 .unwrap_or_else(|| self.tany_with_pos(id.0)),
4627 fn make_enum_class_declaration(
4629 attributes: Self::R,
4631 _enum_keyword: Self::R,
4632 _class_keyword: Self::R,
4636 _extends_keyword: Self::R,
4637 extends_list: Self::R,
4638 _left_brace: Self::R,
4640 _right_brace: Self::R,
4642 let name = match self.elaborate_defined_id(name) {
4644 None => return Node::Ignored(SyntaxKind::EnumClassDeclaration),
4648 .unwrap_or_else(|| self.tany_with_pos(name.0));
4650 let mut is_abstract = false;
4651 let mut is_final = false;
4652 for modifier in modifiers.iter() {
4653 match modifier.token_kind() {
4654 Some(TokenKind::Abstract) => is_abstract = true,
4655 Some(TokenKind::Final) => is_final = true,
4660 let class_kind = if is_abstract {
4661 ClassishKind::CenumClass(&Abstraction::Abstract)
4663 ClassishKind::CenumClass(&Abstraction::Concrete)
4666 let builtin_enum_class_ty = {
4668 let enum_class_ty_ = Ty_::Tapply(self.alloc((name.into(), &[])));
4669 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4670 let elt_ty_ = Ty_::Tapply(self.alloc((
4671 (pos, "\\HH\\MemberOf"),
4672 bumpalo::vec![in self.arena; enum_class_ty, base].into_bump_slice(),
4674 let elt_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), elt_ty_));
4675 let builtin_enum_ty_ = if is_abstract {
4676 Ty_::Tapply(self.alloc(((pos, "\\HH\\BuiltinAbstractEnumClass"), &[])))
4678 Ty_::Tapply(self.alloc((
4679 (pos, "\\HH\\BuiltinEnumClass"),
4680 std::slice::from_ref(self.alloc(elt_ty)),
4683 self.alloc(Ty(self.alloc(Reason::hint(pos)), builtin_enum_ty_))
4686 let consts = self.slice(elements.iter().filter_map(|node| match node {
4687 &Node::Const(const_) => Some(const_),
4691 let mut extends = Vec::with_capacity_in(extends_list.len() + 1, self.arena);
4692 extends.push(builtin_enum_class_ty);
4693 extends.extend(extends_list.iter().filter_map(|&n| self.node_to_ty(n)));
4694 let extends = extends.into_bump_slice();
4695 let includes = &extends[1..];
4697 let mut user_attributes = Vec::with_capacity_in(attributes.len() + 1, self.arena);
4698 for attribute in attributes.iter() {
4700 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4704 user_attributes.push(self.alloc(shallow_decl_defs::UserAttribute {
4705 name: (name.0, "__EnumClass"),
4706 classname_params: &[],
4708 // Match ordering of attributes produced by the OCaml decl parser (even
4709 // though it's the reverse of the syntactic ordering).
4710 user_attributes.reverse();
4711 let user_attributes = user_attributes.into_bump_slice();
4713 let parsed_attributes = self.to_attributes(attributes);
4715 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4716 mode: self.file_mode,
4718 abstract_: is_abstract,
4720 has_xhp_keyword: false,
4722 module: &None, // TODO: grab module from attributes
4725 where_constraints: &[],
4729 xhp_enum_values: SMap::empty(),
4731 req_implements: &[],
4733 support_dynamic_type: parsed_attributes.support_dynamic_type,
4739 static_methods: &[],
4742 enum_type: Some(self.alloc(EnumType {
4748 self.add_class(name.1, cls);
4750 self.classish_name_builder.parsed_classish_declaration();
4752 Node::Ignored(SyntaxKind::EnumClassDeclaration)
4755 fn begin_enum_class_enumerator(&mut self) {
4756 self.start_accumulating_const_refs();
4759 fn make_enum_class_enumerator(
4764 _initializer: Self::R,
4765 _semicolon: Self::R,
4767 let refs = self.stop_accumulating_const_refs();
4768 let name = match self.expect_name(name) {
4770 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4773 let has_abstract_keyword = modifiers
4775 .any(|node| node.is_token(TokenKind::Abstract));
4776 let abstract_ = if has_abstract_keyword {
4777 /* default values not allowed atm */
4778 ClassConstKind::CCAbstract(false)
4780 ClassConstKind::CCConcrete
4784 .unwrap_or_else(|| self.tany_with_pos(name.0));
4785 let class_name = match self.classish_name_builder.get_current_classish_name() {
4787 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4789 let enum_class_ty_ = Ty_::Tapply(self.alloc(((pos, class_name.0), &[])));
4790 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4791 let type_ = Ty_::Tapply(self.alloc((
4792 (pos, "\\HH\\MemberOf"),
4793 bumpalo::vec![in self.arena; enum_class_ty, type_].into_bump_slice(),
4795 let type_ = self.alloc(Ty(self.alloc(Reason::hint(pos)), type_));
4796 Node::Const(self.alloc(ShallowClassConst {
4804 fn make_tuple_type_specifier(
4806 left_paren: Self::R,
4808 right_paren: Self::R,
4810 // We don't need to include the tys list in this position merging
4811 // because by definition it's already contained by the two brackets.
4812 let pos = self.merge_positions(left_paren, right_paren);
4813 let tys = self.slice(tys.iter().filter_map(|&node| self.node_to_ty(node)));
4814 self.hint_ty(pos, Ty_::Ttuple(tys))
4817 fn make_tuple_type_explicit_specifier(
4820 _left_angle: Self::R,
4822 right_angle: Self::R,
4824 let id = (self.get_pos(keyword), "\\tuple");
4825 // This is an error--tuple syntax is (A, B), not tuple<A, B>.
4826 // OCaml decl makes a Tapply rather than a Ttuple here.
4827 self.make_apply(id, types, self.get_pos(right_angle))
4830 fn make_intersection_type_specifier(
4832 left_paren: Self::R,
4834 right_paren: Self::R,
4836 let pos = self.merge_positions(left_paren, right_paren);
4837 let tys = self.slice(tys.iter().filter_map(|x| match x {
4838 Node::ListItem(&(ty, _ampersand)) => self.node_to_ty(ty),
4839 &x => self.node_to_ty(x),
4841 self.hint_ty(pos, Ty_::Tintersection(tys))
4844 fn make_union_type_specifier(
4846 left_paren: Self::R,
4848 right_paren: Self::R,
4850 let pos = self.merge_positions(left_paren, right_paren);
4851 let tys = self.slice(tys.iter().filter_map(|x| match x {
4852 Node::ListItem(&(ty, _bar)) => self.node_to_ty(ty),
4853 &x => self.node_to_ty(x),
4855 self.hint_ty(pos, Ty_::Tunion(tys))
4858 fn make_shape_type_specifier(
4866 let fields = fields;
4867 let fields_iter = fields.iter();
4868 let mut fields = AssocListMut::new_in(self.arena);
4869 for node in fields_iter {
4870 if let &Node::ShapeFieldSpecifier(&ShapeFieldNode { name, type_ }) = node {
4871 fields.insert(self.make_t_shape_field_name(name), type_)
4874 let kind = match open.token_kind() {
4875 Some(TokenKind::DotDotDot) => ShapeKind::OpenShape,
4876 _ => ShapeKind::ClosedShape,
4878 let pos = self.merge_positions(shape, rparen);
4879 self.hint_ty(pos, Ty_::Tshape(self.alloc((kind, fields.into()))))
4882 fn make_classname_type_specifier(
4887 _trailing_comma: Self::R,
4890 let id = match classname.as_id() {
4892 None => return Node::Ignored(SK::ClassnameTypeSpecifier),
4894 if gt.is_ignored() {
4895 self.prim_ty(aast::Tprim::Tstring, id.0)
4898 (id.0, self.elaborate_raw_id(id.1)),
4900 self.merge_positions(classname, gt),
4905 fn make_scope_resolution_expression(
4907 class_name: Self::R,
4911 let pos = self.merge_positions(class_name, value);
4912 let Id(class_name_pos, class_name_str) = match self.expect_name(class_name) {
4913 Some(id) => self.elaborate_id(id),
4914 None => return Node::Ignored(SK::ScopeResolutionExpression),
4916 let class_id = self.alloc(aast::ClassId(
4920 Node::Name(("self", _)) => aast::ClassId_::CIself,
4921 _ => aast::ClassId_::CI(self.alloc(Id(class_name_pos, class_name_str))),
4924 let value_id = match self.expect_name(value) {
4926 None => return Node::Ignored(SK::ScopeResolutionExpression),
4928 self.accumulate_const_ref(class_id, &value_id);
4929 Node::Expr(self.alloc(aast::Expr(
4932 nast::Expr_::ClassConst(self.alloc((class_id, self.alloc((value_id.0, value_id.1))))),
4936 fn make_field_specifier(
4938 question_token: Self::R,
4943 let optional = question_token.is_present();
4944 let ty = match self.node_to_ty(type_) {
4946 None => return Node::Ignored(SK::FieldSpecifier),
4948 let name = match self.make_shape_field_name(name) {
4950 None => return Node::Ignored(SK::FieldSpecifier),
4952 Node::ShapeFieldSpecifier(self.alloc(ShapeFieldNode {
4953 name: self.alloc(ShapeField(name)),
4954 type_: self.alloc(ShapeFieldType { optional, ty }),
4958 fn make_field_initializer(&mut self, key: Self::R, _arrow: Self::R, value: Self::R) -> Self::R {
4959 Node::ListItem(self.alloc((key, value)))
4962 fn make_varray_type_specifier(
4964 varray_keyword: Self::R,
4965 _less_than: Self::R,
4967 _trailing_comma: Self::R,
4968 greater_than: Self::R,
4970 let tparam = match self.node_to_ty(tparam) {
4972 None => self.tany_with_pos(self.get_pos(varray_keyword)),
4975 self.merge_positions(varray_keyword, greater_than),
4976 Ty_::Tapply(self.alloc((
4978 self.get_pos(varray_keyword),
4979 naming_special_names::collections::VEC,
4981 self.alloc([tparam]),
4986 fn make_darray_type_specifier(
4989 _less_than: Self::R,
4992 value_type: Self::R,
4993 _trailing_comma: Self::R,
4994 greater_than: Self::R,
4996 let pos = self.merge_positions(darray, greater_than);
4997 let key_type = self.node_to_ty(key_type).unwrap_or(TANY);
4998 let value_type = self.node_to_ty(value_type).unwrap_or(TANY);
5001 Ty_::Tapply(self.alloc((
5003 self.get_pos(darray),
5004 naming_special_names::collections::DICT,
5006 self.alloc([key_type, value_type]),
5011 fn make_old_attribute_specification(
5018 Node::List(nodes) => {
5019 Node::BracketedList(self.alloc((self.get_pos(ltlt), nodes, self.get_pos(gtgt))))
5021 _ => Node::Ignored(SK::OldAttributeSpecification),
5025 fn make_constructor_call(
5028 _left_paren: Self::R,
5030 _right_paren: Self::R,
5032 let unqualified_name = match self.expect_name(name) {
5034 None => return Node::Ignored(SK::ConstructorCall),
5036 let name = if unqualified_name.1.starts_with("__") {
5037 if self.simplify_naming_for_facts
5038 && !naming_special_names::user_attributes::AS_SET.contains(unqualified_name.1)
5040 // only elaborate non-builtins
5041 self.elaborate_id(unqualified_name)
5046 match self.expect_name(name) {
5047 Some(name) => self.elaborate_id(name),
5048 None => return Node::Ignored(SK::ConstructorCall),
5051 let classname_params = self.slice(args.iter().filter_map(|node| match node {
5052 Node::Expr(aast::Expr(
5055 aast::Expr_::ClassConst(&(
5056 aast::ClassId(_, _, aast::ClassId_::CI(&Id(pos, class_name))),
5060 let name = self.elaborate_id(Id(pos, class_name));
5061 Some(ClassNameParam { name, full_pos })
5063 Node::StringLiteral((name, full_pos))
5064 if self.retain_or_omit_user_attributes_for_facts =>
5066 Some(ClassNameParam {
5067 name: Id(Pos::none(), self.str_from_utf8_for_bytes_in_arena(*name)),
5071 Node::IntLiteral((name, full_pos)) if self.retain_or_omit_user_attributes_for_facts => {
5072 Some(ClassNameParam {
5073 name: Id(Pos::none(), name),
5080 let string_literal_params = if match name.1 {
5081 "__Deprecated" | "__Cipp" | "__CippLocal" | "__Policied" | "__Module" => true,
5084 fn fold_string_concat<'a>(expr: &nast::Expr<'a>, acc: &mut Vec<'a, u8>) {
5086 &aast::Expr(_, _, aast::Expr_::String(val)) => acc.extend_from_slice(val),
5087 &aast::Expr(_, _, aast::Expr_::Binop(&(Bop::Dot, e1, e2))) => {
5088 fold_string_concat(&e1, acc);
5089 fold_string_concat(&e2, acc);
5095 self.slice(args.iter().filter_map(|expr| match expr {
5096 Node::StringLiteral((x, _)) => Some(*x),
5097 Node::Expr(e @ aast::Expr(_, _, aast::Expr_::Binop(_))) => {
5098 let mut acc = Vec::new_in(self.arena);
5099 fold_string_concat(e, &mut acc);
5100 Some(acc.into_bump_slice().into())
5108 Node::Attribute(self.alloc(UserAttributeNode {
5111 string_literal_params,
5119 _semicolon: Self::R,
5121 Node::TraitUse(self.alloc(names))
5124 fn make_trait_use_conflict_resolution(
5128 _left_brace: Self::R,
5130 _right_brace: Self::R,
5132 Node::TraitUse(self.alloc(names))
5135 fn make_require_clause(
5138 require_type: Self::R,
5140 _semicolon: Self::R,
5142 Node::RequireClause(self.alloc(RequireClause { require_type, name }))
5145 fn make_nullable_type_specifier(&mut self, question_mark: Self::R, hint: Self::R) -> Self::R {
5146 let pos = self.merge_positions(question_mark, hint);
5147 let ty = match self.node_to_ty(hint) {
5149 None => return Node::Ignored(SK::NullableTypeSpecifier),
5151 self.hint_ty(pos, Ty_::Toption(ty))
5154 fn make_like_type_specifier(&mut self, tilde: Self::R, hint: Self::R) -> Self::R {
5155 let pos = self.merge_positions(tilde, hint);
5156 let ty = match self.node_to_ty(hint) {
5158 None => return Node::Ignored(SK::LikeTypeSpecifier),
5160 self.hint_ty(pos, Ty_::Tlike(ty))
5163 fn make_closure_type_specifier(
5165 outer_left_paren: Self::R,
5166 readonly_keyword: Self::R,
5167 _function_keyword: Self::R,
5168 _inner_left_paren: Self::R,
5169 parameter_list: Self::R,
5170 _inner_right_paren: Self::R,
5171 capability: Self::R,
5173 readonly_ret: Self::R,
5174 return_type: Self::R,
5175 outer_right_paren: Self::R,
5177 let make_param = |fp: &'a FunParamDecl<'a>| -> &'a FunParam<'a> {
5178 let mut flags = FunParamFlags::empty();
5181 ParamMode::FPinout => {
5182 flags |= FunParamFlags::INOUT;
5184 ParamMode::FPnormal => {}
5188 flags |= FunParamFlags::READONLY;
5191 self.alloc(FunParam {
5192 pos: self.get_pos(fp.hint),
5194 type_: self.alloc(PossiblyEnforcedTy {
5195 enforced: Enforcement::Unenforced,
5196 type_: self.node_to_ty(fp.hint).unwrap_or_else(|| tany()),
5202 let arity = parameter_list
5204 .find_map(|&node| match node {
5205 Node::FunParam(fp) if fp.variadic => Some(FunArity::Fvariadic(make_param(fp))),
5208 .unwrap_or(FunArity::Fstandard);
5210 let params = self.slice(parameter_list.iter().filter_map(|&node| match node {
5211 Node::FunParam(fp) if !fp.variadic => Some(make_param(fp)),
5215 let ret = match self.node_to_ty(return_type) {
5217 None => return Node::Ignored(SK::ClosureTypeSpecifier),
5219 let pos = self.merge_positions(outer_left_paren, outer_right_paren);
5220 let implicit_params = self.as_fun_implicit_params(capability, pos);
5222 let mut flags = FunTypeFlags::empty();
5223 if readonly_ret.is_token(TokenKind::Readonly) {
5224 flags |= FunTypeFlags::RETURNS_READONLY;
5226 if readonly_keyword.is_token(TokenKind::Readonly) {
5227 flags |= FunTypeFlags::READONLY_THIS;
5232 Ty_::Tfun(self.alloc(FunType {
5235 where_constraints: &[],
5238 ret: self.alloc(PossiblyEnforcedTy {
5239 enforced: Enforcement::Unenforced,
5243 ifc_decl: default_ifc_fun_decl(),
5248 fn make_closure_parameter_type_specifier(
5254 let kind = if inout.is_token(TokenKind::Inout) {
5259 Node::FunParam(self.alloc(FunParamDecl {
5260 attributes: Node::Ignored(SK::Missing),
5261 visibility: Node::Ignored(SK::Missing),
5264 readonly: readonly.is_token(TokenKind::Readonly),
5265 pos: self.get_pos(hint),
5268 initializer: Node::Ignored(SK::Missing),
5272 fn make_type_const_declaration(
5274 attributes: Self::R,
5276 _const_keyword: Self::R,
5277 _type_keyword: Self::R,
5279 _type_parameters: Self::R,
5280 as_constraint: Self::R,
5283 _semicolon: Self::R,
5285 let attributes = self.to_attributes(attributes);
5286 let has_abstract_keyword = modifiers
5288 .any(|node| node.is_token(TokenKind::Abstract));
5289 let as_constraint = match as_constraint {
5290 Node::TypeConstraint(innards) => self.node_to_ty(innards.1),
5293 let type_ = self.node_to_ty(type_);
5294 let kind = if has_abstract_keyword {
5295 // Abstract type constant:
5296 // abstract const type T [as X] [super Y] [= Z];
5297 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5299 super_constraint: None,
5303 if let Some(t) = type_ {
5304 // Concrete type constant:
5305 // const type T = Z;
5306 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type: t }))
5308 // concrete or type constant requires a value
5309 return Node::Ignored(SK::TypeConstDeclaration);
5312 let name = match name.as_id() {
5314 None => return Node::Ignored(SK::TypeConstDeclaration),
5316 Node::TypeConstant(self.alloc(ShallowTypeconst {
5319 enforceable: match attributes.enforceable {
5320 Some(pos) => (pos, true),
5321 None => (Pos::none(), false),
5323 reifiable: attributes.reifiable,
5328 fn make_context_const_declaration(
5331 _const_keyword: Self::R,
5332 _ctx_keyword: Self::R,
5334 _type_parameters: Self::R,
5335 constraints: Self::R,
5338 _semicolon: Self::R,
5340 let name = match name.as_id() {
5342 None => return Node::Ignored(SK::TypeConstDeclaration),
5344 let has_abstract_keyword = modifiers
5346 .any(|node| node.is_token(TokenKind::Abstract));
5347 let context = self.node_to_ty(ctx_list);
5349 // note: lowerer ensures that there's at most 1 constraint of each kind
5350 let mut as_constraint = None;
5351 let mut super_constraint = None;
5352 for c in constraints.iter() {
5353 if let Node::ContextConstraint(&(kind, hint)) = c {
5354 let ty = self.node_to_ty(hint);
5356 ConstraintKind::ConstraintSuper => super_constraint = ty,
5357 ConstraintKind::ConstraintAs => as_constraint = ty,
5362 let kind = if has_abstract_keyword {
5363 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5369 if let Some(tc_type) = context {
5370 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type }))
5372 /* Concrete type const must have a value */
5373 return Node::Ignored(SK::TypeConstDeclaration);
5376 Node::TypeConstant(self.alloc(ShallowTypeconst {
5379 enforceable: (Pos::none(), false),
5385 fn make_decorated_expression(&mut self, decorator: Self::R, expr: Self::R) -> Self::R {
5386 Node::ListItem(self.alloc((decorator, expr)))
5389 fn make_type_constant(
5392 _coloncolon: Self::R,
5393 constant_name: Self::R,
5395 let id = match self.expect_name(constant_name) {
5397 None => return Node::Ignored(SK::TypeConstant),
5399 let pos = self.merge_positions(ty, constant_name);
5400 let ty = match (ty, self.classish_name_builder.get_current_classish_name()) {
5401 (Node::Name(("self", self_pos)), Some((name, class_name_pos))) => {
5402 // In classes, we modify the position when rewriting the
5403 // `self` keyword to point to the class name. In traits,
5404 // we don't (because traits are not types). We indicate
5405 // that the position shouldn't be rewritten with the
5407 let id_pos = if class_name_pos.is_none() {
5412 let reason = self.alloc(Reason::hint(self_pos));
5413 let ty_ = Ty_::Tapply(self.alloc(((id_pos, name), &[][..])));
5414 self.alloc(Ty(reason, ty_))
5416 _ => match self.node_to_ty(ty) {
5418 None => return Node::Ignored(SK::TypeConstant),
5421 let reason = self.alloc(Reason::hint(pos));
5422 // The reason-rewriting here is only necessary to match the
5423 // behavior of OCaml decl (which flattens and then unflattens
5424 // Haccess hints, losing some position information).
5425 let ty = self.rewrite_taccess_reasons(ty, reason);
5426 Node::Ty(self.alloc(Ty(
5428 Ty_::Taccess(self.alloc(TaccessType(ty, id.into()))),
5432 fn make_soft_type_specifier(&mut self, at_token: Self::R, hint: Self::R) -> Self::R {
5433 let pos = self.merge_positions(at_token, hint);
5434 let hint = match self.node_to_ty(hint) {
5436 None => return Node::Ignored(SK::SoftTypeSpecifier),
5438 // Use the type of the hint as-is (i.e., throw away the knowledge that
5439 // we had a soft type specifier here--the typechecker does not use it).
5440 // Replace its Reason with one including the position of the `@` token.
5443 if self.opts.interpret_soft_types_as_like_types {
5451 fn make_attribute_specification(&mut self, attributes: Self::R) -> Self::R {
5452 if self.retain_or_omit_user_attributes_for_facts {
5455 Node::Ignored(SK::AttributeSpecification)
5459 fn make_attribute(&mut self, _at: Self::R, attribute: Self::R) -> Self::R {
5460 if self.retain_or_omit_user_attributes_for_facts {
5463 Node::Ignored(SK::Attribute)
5467 // A type specifier preceded by an attribute list. At the time of writing,
5468 // only the <<__Soft>> attribute is permitted here.
5469 fn make_attributized_specifier(&mut self, attributes: Self::R, hint: Self::R) -> Self::R {
5471 Node::BracketedList((
5473 [Node::Attribute(UserAttributeNode {
5474 name: Id(_, "__Soft"),
5479 let attributes_pos = self.merge(*ltlt_pos, *gtgt_pos);
5480 let hint_pos = self.get_pos(hint);
5481 // Use the type of the hint as-is (i.e., throw away the
5482 // knowledge that we had a soft type specifier here--the
5483 // typechecker does not use it). Replace its Reason with one
5484 // including the position of the attribute list.
5485 let hint = match self.node_to_ty(hint) {
5487 None => return Node::Ignored(SK::AttributizedSpecifier),
5491 self.merge(attributes_pos, hint_pos),
5492 if self.opts.interpret_soft_types_as_like_types {
5503 fn make_vector_type_specifier(
5506 _left_angle: Self::R,
5508 _trailing_comma: Self::R,
5509 right_angle: Self::R,
5511 let id = match self.expect_name(vec) {
5513 None => return Node::Ignored(SK::VectorTypeSpecifier),
5515 let id = (id.0, self.elaborate_raw_id(id.1));
5516 self.make_apply(id, hint, self.get_pos(right_angle))
5519 fn make_dictionary_type_specifier(
5522 _left_angle: Self::R,
5523 type_arguments: Self::R,
5524 right_angle: Self::R,
5526 let id = match self.expect_name(dict) {
5528 None => return Node::Ignored(SK::DictionaryTypeSpecifier),
5530 let id = (id.0, self.elaborate_raw_id(id.1));
5531 self.make_apply(id, type_arguments, self.get_pos(right_angle))
5534 fn make_keyset_type_specifier(
5537 _left_angle: Self::R,
5539 _trailing_comma: Self::R,
5540 right_angle: Self::R,
5542 let id = match self.expect_name(keyset) {
5544 None => return Node::Ignored(SK::KeysetTypeSpecifier),
5546 let id = (id.0, self.elaborate_raw_id(id.1));
5547 self.make_apply(id, hint, self.get_pos(right_angle))
5550 fn make_variable_expression(&mut self, _expression: Self::R) -> Self::R {
5551 Node::Ignored(SK::VariableExpression)
5554 fn make_file_attribute_specification(
5556 _left_double_angle: Self::R,
5559 attributes: Self::R,
5560 _right_double_angle: Self::R,
5562 if self.retain_or_omit_user_attributes_for_facts {
5563 self.file_attributes = List::empty();
5564 for attr in attributes.iter() {
5566 Node::Attribute(attr) => self
5568 .push_front(self.user_attribute_to_decl(attr), self.arena),
5573 Node::Ignored(SK::FileAttributeSpecification)
5576 fn make_subscript_expression(
5579 _left_bracket: Self::R,
5581 _right_bracket: Self::R,
5583 Node::Ignored(SK::SubscriptExpression)
5586 fn make_member_selection_expression(
5592 Node::Ignored(SK::MemberSelectionExpression)
5595 fn make_object_creation_expression(
5597 _new_keyword: Self::R,
5600 Node::Ignored(SK::ObjectCreationExpression)
5603 fn make_safe_member_selection_expression(
5609 Node::Ignored(SK::SafeMemberSelectionExpression)
5612 fn make_function_call_expression(
5615 _type_args: Self::R,
5616 _enum_class_label: Self::R,
5617 _left_paren: Self::R,
5618 _argument_list: Self::R,
5619 _right_paren: Self::R,
5621 Node::Ignored(SK::FunctionCallExpression)
5624 fn make_list_expression(
5627 _left_paren: Self::R,
5629 _right_paren: Self::R,
5631 Node::Ignored(SK::ListExpression)