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, FunElt, FunImplicitParams, FunParam, FunParams, FunType, IfcFunDecl,
46 ParamMode, PosByteString, PosId, PosString, PossiblyEnforcedTy, ShapeFieldType, ShapeKind,
47 TaccessType, Tparam, TshapeFieldName, Ty, Ty_, Typeconst, TypedefType, WhereConstraint,
49 typing_defs_flags::{FunParamFlags, FunTypeFlags},
50 typing_reason::Reason,
53 use parser_core_types::{
54 compact_token::CompactToken, indexed_source_text::IndexedSourceText, source_text::SourceText,
55 syntax_kind::SyntaxKind, token_factory::SimpleTokenFactoryImpl, token_kind::TokenKind,
58 mod direct_decl_smart_constructors_generated;
62 type SSet<'a> = arena_collections::SortedSet<'a, &'a str>;
65 pub struct DirectDeclSmartConstructors<'a, 'text, S: SourceTextAllocator<'text, 'a>> {
66 pub token_factory: SimpleTokenFactoryImpl<CompactToken>,
68 pub source_text: IndexedSourceText<'text>,
69 pub arena: &'a bumpalo::Bump,
71 pub file_attributes: List<'a, &'a typing_defs::UserAttribute<'a>>,
72 // const_refs will accumulate all scope-resolution-expressions it enconuters while it's "Some"
73 const_refs: Option<arena_collections::set::Set<'a, typing_defs::ClassConstRef<'a>>>,
74 opts: &'a DeclParserOptions<'a>,
75 filename: &'a RelativePath<'a>,
77 namespace_builder: Rc<NamespaceBuilder<'a>>,
78 classish_name_builder: ClassishNameBuilder<'a>,
79 type_parameters: Rc<Vec<'a, SSet<'a>>>,
80 retain_or_omit_user_attributes_for_facts: bool,
81 previous_token_kind: TokenKind,
82 source_text_allocator: S,
83 module: Option<Id<'a>>,
86 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> DirectDeclSmartConstructors<'a, 'text, S> {
88 opts: &'a DeclParserOptions<'a>,
89 src: &SourceText<'text>,
92 source_text_allocator: S,
93 retain_or_omit_user_attributes_for_facts: bool,
94 elaborate_xhp_namespaces_for_facts: bool,
96 let source_text = IndexedSourceText::new(src.clone());
97 let path = source_text.source_text().file_path();
98 let prefix = path.prefix();
99 let path = String::from_str_in(path.path_str(), arena).into_bump_str();
100 let filename = RelativePath::make(prefix, path);
102 token_factory: SimpleTokenFactoryImpl::new(),
107 filename: arena.alloc(filename),
109 decls: Decls::empty(),
110 file_attributes: List::empty(),
112 namespace_builder: Rc::new(NamespaceBuilder::new_in(
113 opts.auto_namespace_map,
114 opts.disable_xhp_element_mangling,
115 elaborate_xhp_namespaces_for_facts,
118 classish_name_builder: ClassishNameBuilder::new(),
119 type_parameters: Rc::new(Vec::new_in(arena)),
120 // EndOfFile is used here as a None value (signifying "beginning of
121 // file") to save space. There is no legitimate circumstance where
122 // we would parse a token and the previous token kind would be
124 previous_token_kind: TokenKind::EndOfFile,
125 source_text_allocator,
126 retain_or_omit_user_attributes_for_facts,
132 pub fn alloc<T>(&self, val: T) -> &'a T {
133 self.arena.alloc(val)
136 fn qualified_name_from_parts(&self, parts: &'a [Node<'a>], pos: &'a Pos<'a>) -> Id<'a> {
137 // Count the length of the qualified name, so that we can allocate
138 // exactly the right amount of space for it in our arena.
142 Node::Name(&(name, _)) => len += name.len(),
143 Node::Token(t) if t.kind() == TokenKind::Backslash => len += 1,
144 Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => len += name.len() + 1,
145 Node::ListItem(&(Node::Token(t), _backslash))
146 if t.kind() == TokenKind::Namespace =>
148 len += t.width() + 1;
153 // If there's no internal trivia, then we can just reference the
154 // qualified name in the original source text instead of copying it.
155 let source_len = pos.end_offset() - pos.start_offset();
156 if source_len == len {
157 let qualified_name: &'a str = self.str_from_utf8(self.source_text_at_pos(pos));
158 return Id(pos, qualified_name);
160 // Allocate `len` bytes and fill them with the fully qualified name.
161 let mut qualified_name = String::with_capacity_in(len, self.arena);
164 Node::Name(&(name, _pos)) => qualified_name.push_str(name),
165 Node::Token(t) if t.kind() == TokenKind::Backslash => qualified_name.push('\\'),
166 &Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => {
167 qualified_name.push_str(name);
168 qualified_name.push_str("\\");
170 &Node::ListItem(&(Node::Token(t), _backslash))
171 if t.kind() == TokenKind::Namespace =>
173 qualified_name.push_str("namespace\\");
178 debug_assert_eq!(len, qualified_name.len());
179 debug_assert_eq!(len, qualified_name.capacity());
180 Id(pos, qualified_name.into_bump_str())
183 /// If the given node is an identifier, XHP name, or qualified name,
184 /// elaborate it in the current namespace and return Some. To be used for
185 /// the name of a decl in its definition (e.g., "C" in `class C {}` or "f"
186 /// in `function f() {}`).
187 fn elaborate_defined_id(&self, name: Node<'a>) -> Option<Id<'a>> {
188 let id = match name {
189 Node::Name(&(name, pos)) => Id(pos, name),
190 Node::XhpName(&(name, pos)) => Id(pos, name),
191 Node::QualifiedName(&(parts, pos)) => self.qualified_name_from_parts(parts, pos),
192 // This is always an error; e.g. using a reserved word where a name
194 Node::Token(t) | Node::IgnoredToken(t) => {
195 let pos = self.token_pos(t);
196 let text = self.str_from_utf8(self.source_text_at_pos(pos));
201 Some(self.namespace_builder.elaborate_defined_id(id))
204 /// If the given node is a name (i.e., an identifier or a qualified name),
205 /// return Some. No namespace elaboration is performed.
206 fn expect_name(&self, name: Node<'a>) -> Option<Id<'a>> {
207 // If it's a simple identifier, return it.
208 if let id @ Some(_) = name.as_id() {
212 Node::QualifiedName(&(parts, pos)) => Some(self.qualified_name_from_parts(parts, pos)),
213 // The IgnoredToken case is always an error; e.g. using a reserved
214 // word where a name is expected. The Token case is not an error if
215 // the token is TokenKind::XHP (which is legal to use as a name),
216 // but an error otherwise (since we expect a Name or QualifiedName
217 // here, and the Name case would have been handled in `as_id`
219 Node::Token(t) | Node::IgnoredToken(t) => {
220 let pos = self.token_pos(t);
221 let text = self.str_from_utf8(self.source_text_at_pos(pos));
228 /// Fully qualify the given identifier as a type name (with consideration
229 /// to `use` statements in scope).
230 fn elaborate_id(&self, id: Id<'a>) -> Id<'a> {
231 let Id(pos, name) = id;
232 Id(pos, self.elaborate_raw_id(name))
235 /// Fully qualify the given identifier as a type name (with consideration
236 /// to `use` statements in scope).
237 fn elaborate_raw_id(&self, id: &'a str) -> &'a str {
238 self.namespace_builder
239 .elaborate_raw_id(ElaborateKind::Class, id)
242 /// Fully qualify the given identifier as a constant name (with
243 /// consideration to `use` statements in scope).
244 fn elaborate_const_id(&self, id: Id<'a>) -> Id<'a> {
245 let Id(pos, name) = id;
248 self.namespace_builder
249 .elaborate_raw_id(ElaborateKind::Const, name),
253 fn slice<T>(&self, iter: impl Iterator<Item = T>) -> &'a [T] {
254 let mut result = match iter.size_hint().1 {
255 Some(upper_bound) => Vec::with_capacity_in(upper_bound, self.arena),
256 None => Vec::new_in(self.arena),
261 result.into_bump_slice()
264 fn start_accumulating_const_refs(&mut self) {
265 self.const_refs = Some(arena_collections::set::Set::empty());
268 fn accumulate_const_ref(&mut self, class_id: &'a aast::ClassId<'_, (), ()>, value_id: &Id<'a>) {
269 // The decl for a class constant stores a list of all the scope-resolution expressions
270 // it contains. For example "const C=A::X" stores A::X, and "const D=self::Y" stores self::Y.
271 // (This is so we can detect cross-type circularity in constant initializers).
272 // TODO: Hack is the wrong place to detect circularity (because we can never do it completely soundly,
273 // and because it's a cross-body problem). The right place to do it is in a linter. All this should be
274 // removed from here and put into a linter.
275 if let Some(const_refs) = self.const_refs {
277 nast::ClassId_::CI(sid) => {
278 self.const_refs = Some(const_refs.add(
280 typing_defs::ClassConstRef(
281 typing_defs::ClassConstFrom::From(sid.1),
286 nast::ClassId_::CIself => {
287 self.const_refs = Some(const_refs.add(
289 typing_defs::ClassConstRef(typing_defs::ClassConstFrom::Self_, value_id.1),
293 nast::ClassId_::CIparent
294 | nast::ClassId_::CIstatic
295 | nast::ClassId_::CIexpr(_) => {}
300 fn stop_accumulating_const_refs(&mut self) -> &'a [typing_defs::ClassConstRef<'a>] {
301 let const_refs = self.const_refs;
302 self.const_refs = None;
304 Some(const_refs) => {
305 let mut elements: Vec<'_, typing_defs::ClassConstRef<'_>> =
306 bumpalo::collections::Vec::with_capacity_in(const_refs.count(), self.arena);
307 elements.extend(const_refs.into_iter());
308 elements.into_bump_slice()
315 pub trait SourceTextAllocator<'text, 'target>: Clone {
316 fn alloc(&self, text: &'text str) -> &'target str;
320 pub struct NoSourceTextAllocator;
322 impl<'text> SourceTextAllocator<'text, 'text> for NoSourceTextAllocator {
324 fn alloc(&self, text: &'text str) -> &'text str {
330 pub struct ArenaSourceTextAllocator<'arena>(pub &'arena bumpalo::Bump);
332 impl<'text, 'arena> SourceTextAllocator<'text, 'arena> for ArenaSourceTextAllocator<'arena> {
334 fn alloc(&self, text: &'text str) -> &'arena str {
335 self.0.alloc_str(text)
339 fn prefix_slash<'a>(arena: &'a Bump, name: &str) -> &'a str {
340 let mut s = String::with_capacity_in(1 + name.len(), arena);
346 fn prefix_colon<'a>(arena: &'a Bump, name: &str) -> &'a str {
347 let mut s = String::with_capacity_in(1 + name.len(), arena);
353 fn concat<'a>(arena: &'a Bump, str1: &str, str2: &str) -> &'a str {
354 let mut result = String::with_capacity_in(str1.len() + str2.len(), arena);
355 result.push_str(str1);
356 result.push_str(str2);
357 result.into_bump_str()
360 fn strip_dollar_prefix<'a>(name: &'a str) -> &'a str {
361 name.trim_start_matches('$')
364 const TANY_: Ty_<'_> = Ty_::Tany(oxidized_by_ref::tany_sentinel::TanySentinel);
365 const TANY: &Ty<'_> = &Ty(Reason::none(), TANY_);
367 const NO_POS: &Pos<'_> = Pos::none();
369 fn default_ifc_fun_decl<'a>() -> IfcFunDecl<'a> {
370 IfcFunDecl::FDPolicied(Some("PUBLIC"))
376 visibility: aast::Visibility,
382 fn read_member_modifiers<'a: 'b, 'b>(modifiers: impl Iterator<Item = &'b Node<'a>>) -> Modifiers {
383 let mut ret = Modifiers {
385 visibility: aast::Visibility::Public,
390 for modifier in modifiers {
391 if let Some(vis) = modifier.as_visibility() {
392 ret.visibility = vis;
394 match modifier.token_kind() {
395 Some(TokenKind::Static) => ret.is_static = true,
396 Some(TokenKind::Abstract) => ret.is_abstract = true,
397 Some(TokenKind::Final) => ret.is_final = true,
398 Some(TokenKind::Readonly) => ret.is_readonly = true,
405 #[derive(Clone, Debug)]
406 struct NamespaceBuilder<'a> {
408 stack: Vec<'a, NamespaceEnv<'a>>,
410 auto_ns_map: &'a [(&'a str, &'a str)],
411 elaborate_xhp_namespaces_for_facts: bool,
414 impl<'a> NamespaceBuilder<'a> {
416 auto_ns_map: &'a [(&'a str, &'a str)],
417 disable_xhp_element_mangling: bool,
418 elaborate_xhp_namespaces_for_facts: bool,
421 let mut ns_uses = SMap::empty();
422 for &alias in hh_autoimport::NAMESPACES {
423 ns_uses = ns_uses.add(arena, alias, concat(arena, "HH\\", alias));
425 for (alias, ns) in auto_ns_map.iter() {
426 ns_uses = ns_uses.add(arena, alias, ns);
429 let mut class_uses = SMap::empty();
430 for &alias in hh_autoimport::TYPES {
431 class_uses = class_uses.add(arena, alias, concat(arena, "HH\\", alias));
436 stack: bumpalo::vec![in arena; NamespaceEnv {
439 fun_uses: SMap::empty(),
440 const_uses: SMap::empty(),
444 disable_xhp_element_mangling,
447 elaborate_xhp_namespaces_for_facts,
451 fn push_namespace(&mut self, name: Option<&str>) {
452 let current = self.current_namespace();
453 let nsenv = self.stack.last().unwrap().clone(); // shallow clone
454 if let Some(name) = name {
455 let mut fully_qualified = match current {
456 None => String::with_capacity_in(name.len(), self.arena),
458 let mut fully_qualified =
459 String::with_capacity_in(current.len() + name.len() + 1, self.arena);
460 fully_qualified.push_str(current);
461 fully_qualified.push('\\');
465 fully_qualified.push_str(name);
466 self.stack.push(NamespaceEnv {
467 name: Some(fully_qualified.into_bump_str()),
471 self.stack.push(NamespaceEnv {
478 fn pop_namespace(&mut self) {
479 // We'll never push a namespace for a declaration of items in the global
480 // namespace (e.g., `namespace { ... }`), so only pop if we are in some
481 // namespace other than the global one.
482 if self.stack.len() > 1 {
483 self.stack.pop().unwrap();
487 // push_namespace(Y) + pop_namespace() + push_namespace(X) should be equivalent to
488 // push_namespace(Y) + push_namespace(X) + pop_previous_namespace()
489 fn pop_previous_namespace(&mut self) {
490 if self.stack.len() > 2 {
491 let last = self.stack.pop().unwrap().name.unwrap_or("\\");
492 let previous = self.stack.pop().unwrap().name.unwrap_or("\\");
493 assert!(last.starts_with(previous));
494 let name = &last[previous.len() + 1..last.len()];
495 self.push_namespace(Some(name));
499 fn current_namespace(&self) -> Option<&'a str> {
500 self.stack.last().and_then(|nsenv| nsenv.name)
503 fn add_import(&mut self, kind: NamespaceUseKind, name: &'a str, aliased_name: Option<&'a str>) {
504 let stack_top = &mut self
507 .expect("Attempted to get the current import map, but namespace stack was empty");
508 let aliased_name = aliased_name.unwrap_or_else(|| {
509 name.rsplit_terminator('\\')
511 .expect("Expected at least one entry in import name")
513 let name = name.trim_end_matches('\\');
514 let name = if name.starts_with('\\') {
517 prefix_slash(self.arena, name)
520 NamespaceUseKind::Type => {
521 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
523 NamespaceUseKind::Namespace => {
524 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
526 NamespaceUseKind::Mixed => {
527 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
528 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
533 fn elaborate_raw_id(&self, kind: ElaborateKind, name: &'a str) -> &'a str {
534 if name.starts_with('\\') {
537 let env = self.stack.last().unwrap();
538 namespaces::elaborate_raw_id_in(
543 self.elaborate_xhp_namespaces_for_facts,
547 fn elaborate_defined_id(&self, id: Id<'a>) -> Id<'a> {
548 let Id(pos, name) = id;
549 let env = self.stack.last().unwrap();
550 let name = if env.disable_xhp_element_mangling && name.contains(':') {
551 let xhp_name_opt = namespaces::elaborate_xhp_namespace(name);
552 let name = xhp_name_opt.map_or(name, |s| self.arena.alloc_str(&s));
553 if !name.starts_with('\\') {
554 namespaces::elaborate_into_current_ns_in(self.arena, env, name)
555 } else if self.elaborate_xhp_namespaces_for_facts {
556 // allow :foo:bar to be elaborated into \currentnamespace\foo\bar
557 namespaces::elaborate_into_current_ns_in(self.arena, env, &name[1..])
562 namespaces::elaborate_into_current_ns_in(self.arena, env, name)
568 #[derive(Clone, Debug)]
569 enum ClassishNameBuilder<'a> {
570 /// We are not in a classish declaration.
573 /// We saw a classish keyword token followed by a Name, so we make it
574 /// available as the name of the containing class declaration.
575 InClassish(&'a (&'a str, &'a Pos<'a>, TokenKind)),
578 impl<'a> ClassishNameBuilder<'a> {
580 ClassishNameBuilder::NotInClassish
583 fn lexed_name_after_classish_keyword(
588 token_kind: TokenKind,
590 use ClassishNameBuilder::*;
593 let name = if name.starts_with(':') {
594 prefix_slash(arena, name)
598 *self = InClassish(arena.alloc((name, pos, token_kind)))
604 fn parsed_classish_declaration(&mut self) {
605 *self = ClassishNameBuilder::NotInClassish;
608 fn get_current_classish_name(&self) -> Option<(&'a str, &'a Pos<'a>)> {
609 use ClassishNameBuilder::*;
611 NotInClassish => None,
612 InClassish((name, pos, _)) => Some((name, pos)),
616 fn in_interface(&self) -> bool {
617 use ClassishNameBuilder::*;
619 InClassish((_, _, TokenKind::Interface)) => true,
620 InClassish((_, _, _)) | NotInClassish => false,
626 pub struct FunParamDecl<'a> {
627 attributes: Node<'a>,
628 visibility: Node<'a>,
633 name: Option<&'a str>,
635 initializer: Node<'a>,
639 pub struct FunctionHeader<'a> {
642 type_params: Node<'a>,
643 param_list: Node<'a>,
644 capability: Node<'a>,
646 readonly_return: Node<'a>,
647 where_constraints: Node<'a>,
651 pub struct RequireClause<'a> {
652 require_type: Node<'a>,
657 pub struct TypeParameterDecl<'a> {
659 reified: aast::ReifyKind,
661 constraints: &'a [(ConstraintKind, Node<'a>)],
662 tparam_params: &'a [&'a Tparam<'a>],
663 user_attributes: &'a [&'a UserAttributeNode<'a>],
667 pub struct ClosureTypeHint<'a> {
675 pub struct NamespaceUseClause<'a> {
676 kind: NamespaceUseKind,
678 as_: Option<&'a str>,
681 #[derive(Copy, Clone, Debug)]
682 enum NamespaceUseKind {
689 pub struct ConstructorNode<'a> {
690 method: &'a ShallowMethod<'a>,
691 properties: &'a [ShallowProp<'a>],
695 pub struct MethodNode<'a> {
696 method: &'a ShallowMethod<'a>,
701 pub struct PropertyNode<'a> {
702 decls: &'a [ShallowProp<'a>],
707 pub struct XhpClassAttributeDeclarationNode<'a> {
708 xhp_attr_enum_values: &'a [(&'a str, &'a [XhpEnumValue<'a>])],
709 xhp_attr_decls: &'a [ShallowProp<'a>],
710 xhp_attr_uses_decls: &'a [Node<'a>],
714 pub struct XhpClassAttributeNode<'a> {
716 tag: Option<xhp_attribute::Tag>,
723 pub struct ShapeFieldNode<'a> {
724 name: &'a ShapeField<'a>,
725 type_: &'a ShapeFieldType<'a>,
728 #[derive(Copy, Clone, Debug)]
729 struct ClassNameParam<'a> {
732 full_pos: &'a Pos<'a>, // Position of the full expression `Foo::class`
736 pub struct UserAttributeNode<'a> {
738 classname_params: &'a [ClassNameParam<'a>],
739 /// This is only used for __Deprecated attribute message and CIPP parameters
740 string_literal_params: &'a [(&'a Pos<'a>, &'a BStr)],
743 mod fixed_width_token {
744 use parser_core_types::token_kind::TokenKind;
746 #[derive(Copy, Clone)]
747 pub struct FixedWidthToken(u64); // { offset: u56, kind: TokenKind }
749 const KIND_BITS: u8 = 8;
750 const KIND_MASK: u64 = u8::MAX as u64;
751 const MAX_OFFSET: u64 = !(KIND_MASK << (64 - KIND_BITS));
753 impl FixedWidthToken {
754 pub fn new(kind: TokenKind, offset: usize) -> Self {
755 // We don't want to spend bits tracking the width of fixed-width
756 // tokens. Since we don't track width, verify that this token kind
757 // is in fact a fixed-width kind.
758 debug_assert!(kind.fixed_width().is_some());
760 let offset: u64 = offset.try_into().unwrap();
761 if offset > MAX_OFFSET {
762 panic!("FixedWidthToken: offset too large: {}", offset);
764 Self(offset << KIND_BITS | kind as u8 as u64)
767 pub fn offset(self) -> usize {
768 (self.0 >> KIND_BITS).try_into().unwrap()
771 pub fn kind(self) -> TokenKind {
772 TokenKind::try_from_u8(self.0 as u8).unwrap()
775 pub fn width(self) -> usize {
776 self.kind().fixed_width().unwrap().get()
780 impl std::fmt::Debug for FixedWidthToken {
781 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
782 fmt.debug_struct("FixedWidthToken")
783 .field("kind", &self.kind())
784 .field("offset", &self.offset())
789 use fixed_width_token::FixedWidthToken;
791 #[derive(Copy, Clone, Debug)]
793 // Nodes which are not useful in constructing a decl are ignored. We keep
794 // track of the SyntaxKind for two reasons.
796 // One is that the parser needs to know the SyntaxKind of a parsed node in
797 // some circumstances (this information is exposed to the parser via an
798 // implementation of `smart_constructors::NodeType`). An adapter called
799 // WithKind exists to provide a `NodeType` implementation for arbitrary
800 // nodes by pairing each node with a SyntaxKind, but in the direct decl
801 // parser, we want to avoid the extra 8 bytes of overhead on each node.
803 // The second reason is that debugging is difficult when nodes are silently
804 // ignored, and providing at least the SyntaxKind of an ignored node helps
805 // in tracking down the reason it was ignored.
808 // For tokens with a fixed width (like `using`), we also keep its offset in
809 // the source text, so that we can reference the text of the token if it's
810 // (erroneously) used as an identifier (e.g., `function using() {}`).
811 IgnoredToken(FixedWidthToken),
813 List(&'a &'a [Node<'a>]),
814 BracketedList(&'a (&'a Pos<'a>, &'a [Node<'a>], &'a Pos<'a>)),
815 Name(&'a (&'a str, &'a Pos<'a>)),
816 XhpName(&'a (&'a str, &'a Pos<'a>)),
817 Variable(&'a (&'a str, &'a Pos<'a>)),
818 QualifiedName(&'a (&'a [Node<'a>], &'a Pos<'a>)),
819 StringLiteral(&'a (&'a BStr, &'a Pos<'a>)), // For shape keys and const expressions.
820 IntLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
821 FloatingLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
822 BooleanLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
824 XhpEnumTy(&'a (Option<&'a Pos<'a>>, &'a Ty<'a>, &'a [XhpEnumValue<'a>])),
825 ListItem(&'a (Node<'a>, Node<'a>)),
826 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
827 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".
828 FunParam(&'a FunParamDecl<'a>),
829 Attribute(&'a UserAttributeNode<'a>),
830 FunctionHeader(&'a FunctionHeader<'a>),
831 Constructor(&'a ConstructorNode<'a>),
832 Method(&'a MethodNode<'a>),
833 Property(&'a PropertyNode<'a>),
834 EnumUse(&'a Node<'a>),
835 TraitUse(&'a Node<'a>),
836 XhpClassAttributeDeclaration(&'a XhpClassAttributeDeclarationNode<'a>),
837 XhpClassAttribute(&'a XhpClassAttributeNode<'a>),
838 XhpAttributeUse(&'a Node<'a>),
839 TypeConstant(&'a ShallowTypeconst<'a>),
840 ContextConstraint(&'a (ConstraintKind, Node<'a>)),
841 RequireClause(&'a RequireClause<'a>),
842 ClassishBody(&'a &'a [Node<'a>]),
843 TypeParameter(&'a TypeParameterDecl<'a>),
844 TypeConstraint(&'a (ConstraintKind, Node<'a>)),
845 ShapeFieldSpecifier(&'a ShapeFieldNode<'a>),
846 NamespaceUseClause(&'a NamespaceUseClause<'a>),
847 Expr(&'a nast::Expr<'a>),
848 TypeParameters(&'a &'a [&'a Tparam<'a>]),
849 WhereConstraint(&'a WhereConstraint<'a>),
850 // Non-ignored, fixed-width tokens (e.g., keywords, operators, braces, etc.).
851 Token(FixedWidthToken),
854 impl<'a> smart_constructors::NodeType for Node<'a> {
857 fn extract(self) -> Self::R {
861 fn is_abstract(&self) -> bool {
862 self.is_token(TokenKind::Abstract) || self.is_ignored_token_with_kind(TokenKind::Abstract)
864 fn is_name(&self) -> bool {
865 matches!(self, Node::Name(..)) || self.is_ignored_token_with_kind(TokenKind::Name)
867 fn is_qualified_name(&self) -> bool {
868 matches!(self, Node::QualifiedName(..)) || matches!(self, Node::Ignored(SK::QualifiedName))
870 fn is_prefix_unary_expression(&self) -> bool {
871 matches!(self, Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(..))))
872 || matches!(self, Node::Ignored(SK::PrefixUnaryExpression))
874 fn is_scope_resolution_expression(&self) -> bool {
877 Node::Expr(aast::Expr(_, _, aast::Expr_::ClassConst(..)))
878 ) || matches!(self, Node::Ignored(SK::ScopeResolutionExpression))
880 fn is_missing(&self) -> bool {
881 matches!(self, Node::Ignored(SK::Missing))
883 fn is_variable_expression(&self) -> bool {
884 matches!(self, Node::Ignored(SK::VariableExpression))
886 fn is_subscript_expression(&self) -> bool {
887 matches!(self, Node::Ignored(SK::SubscriptExpression))
889 fn is_member_selection_expression(&self) -> bool {
890 matches!(self, Node::Ignored(SK::MemberSelectionExpression))
892 fn is_object_creation_expression(&self) -> bool {
893 matches!(self, Node::Ignored(SK::ObjectCreationExpression))
895 fn is_safe_member_selection_expression(&self) -> bool {
896 matches!(self, Node::Ignored(SK::SafeMemberSelectionExpression))
898 fn is_function_call_expression(&self) -> bool {
899 matches!(self, Node::Ignored(SK::FunctionCallExpression))
901 fn is_list_expression(&self) -> bool {
902 matches!(self, Node::Ignored(SK::ListExpression))
907 fn is_token(self, kind: TokenKind) -> bool {
908 self.token_kind() == Some(kind)
911 fn token_kind(self) -> Option<TokenKind> {
913 Node::Token(token) => Some(token.kind()),
918 fn is_ignored_token_with_kind(self, kind: TokenKind) -> bool {
920 Node::IgnoredToken(token) => token.kind() == kind,
925 fn as_slice(self, b: &'a Bump) -> &'a [Self] {
927 Node::List(&items) | Node::BracketedList(&(_, items, _)) => items,
928 n if n.is_ignored() => &[],
929 n => std::slice::from_ref(b.alloc(n)),
933 fn iter<'b>(&'b self) -> NodeIterHelper<'a, 'b>
938 &Node::List(&items) | Node::BracketedList(&(_, items, _)) => {
939 NodeIterHelper::Vec(items.iter())
941 n if n.is_ignored() => NodeIterHelper::Empty,
942 n => NodeIterHelper::Single(n),
946 // The number of elements which would be yielded by `self.iter()`.
947 // Must return the upper bound returned by NodeIterHelper::size_hint.
948 fn len(&self) -> usize {
950 &Node::List(&items) | Node::BracketedList(&(_, items, _)) => items.len(),
951 n if n.is_ignored() => 0,
956 fn as_visibility(&self) -> Option<aast::Visibility> {
957 match self.token_kind() {
958 Some(TokenKind::Private) => Some(aast::Visibility::Private),
959 Some(TokenKind::Protected) => Some(aast::Visibility::Protected),
960 Some(TokenKind::Public) => Some(aast::Visibility::Public),
965 // If this node is a simple unqualified identifier, return its position and text.
966 fn as_id(&self) -> Option<Id<'a>> {
968 Node::Name(&(name, pos)) | Node::XhpName(&(name, pos)) => Some(Id(pos, name)),
973 // If this node is a Variable token, return its position and text.
974 // As an attempt at error recovery (when the dollar sign is omitted), also
975 // return other unqualified identifiers (i.e., the Name token kind).
976 fn as_variable(&self) -> Option<Id<'a>> {
978 Node::Variable(&(name, pos)) | Node::Name(&(name, pos)) => Some(Id(pos, name)),
983 fn is_ignored(&self) -> bool {
984 matches!(self, Node::Ignored(..) | Node::IgnoredToken(..))
987 fn is_present(&self) -> bool {
993 struct Attributes<'a> {
994 deprecated: Option<&'a str>,
995 reifiable: Option<&'a Pos<'a>>,
1002 enforceable: Option<&'a Pos<'a>>,
1003 accept_disposable: bool,
1004 dynamically_callable: bool,
1005 returns_disposable: bool,
1007 ifc_attribute: IfcFunDecl<'a>,
1011 support_dynamic_type: bool,
1012 module: Option<Id<'a>>,
1016 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> DirectDeclSmartConstructors<'a, 'text, S> {
1017 fn add_class(&mut self, name: &'a str, decl: &'a shallow_decl_defs::ShallowClass<'a>) {
1018 self.decls.add(name, Decl::Class(decl), self.arena);
1020 fn add_fun(&mut self, name: &'a str, decl: &'a typing_defs::FunElt<'a>) {
1021 self.decls.add(name, Decl::Fun(decl), self.arena);
1023 fn add_typedef(&mut self, name: &'a str, decl: &'a typing_defs::TypedefType<'a>) {
1024 self.decls.add(name, Decl::Typedef(decl), self.arena);
1026 fn add_const(&mut self, name: &'a str, decl: &'a typing_defs::ConstDecl<'a>) {
1027 self.decls.add(name, Decl::Const(decl), self.arena);
1029 fn add_module(&mut self, name: &'a str, decl: &'a typing_defs::ModuleDefType<'a>) {
1030 self.decls.add(name, Decl::Module(decl), self.arena)
1034 fn concat(&self, str1: &str, str2: &str) -> &'a str {
1035 concat(self.arena, str1, str2)
1038 fn token_bytes(&self, token: &CompactToken) -> &'text [u8] {
1041 .sub(token.start_offset(), token.width())
1044 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1045 // the same data. Otherwise, copy the slice into our arena using
1046 // String::from_utf8_lossy_in, and return a reference to the arena str.
1047 fn str_from_utf8(&self, slice: &'text [u8]) -> &'a str {
1048 if let Ok(s) = std::str::from_utf8(slice) {
1049 self.source_text_allocator.alloc(s)
1051 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1055 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1056 // the same data. Otherwise, copy the slice into our arena using
1057 // String::from_utf8_lossy_in, and return a reference to the arena str.
1058 fn str_from_utf8_for_bytes_in_arena(&self, slice: &'a [u8]) -> &'a str {
1059 if let Ok(s) = std::str::from_utf8(slice) {
1062 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1068 pos1: impl Into<Option<&'a Pos<'a>>>,
1069 pos2: impl Into<Option<&'a Pos<'a>>>,
1071 match (pos1.into(), pos2.into()) {
1072 (None, None) => NO_POS,
1073 (Some(pos), None) | (None, Some(pos)) => pos,
1074 (Some(pos1), Some(pos2)) => match (pos1.is_none(), pos2.is_none()) {
1075 (true, true) => NO_POS,
1076 (true, false) => pos2,
1077 (false, true) => pos1,
1078 (false, false) => Pos::merge_without_checking_filename(self.arena, pos1, pos2),
1083 fn merge_positions(&self, node1: Node<'a>, node2: Node<'a>) -> &'a Pos<'a> {
1084 self.merge(self.get_pos(node1), self.get_pos(node2))
1087 fn pos_from_slice(&self, nodes: &[Node<'a>]) -> &'a Pos<'a> {
1090 .fold(NO_POS, |acc, &node| self.merge(acc, self.get_pos(node)))
1093 fn get_pos(&self, node: Node<'a>) -> &'a Pos<'a> {
1094 self.get_pos_opt(node).unwrap_or(NO_POS)
1097 fn get_pos_opt(&self, node: Node<'a>) -> Option<&'a Pos<'a>> {
1098 let pos = match node {
1099 Node::Name(&(_, pos)) | Node::Variable(&(_, pos)) => pos,
1100 Node::Ty(ty) => return ty.get_pos(),
1101 Node::XhpName(&(_, pos)) => pos,
1102 Node::QualifiedName(&(_, pos)) => pos,
1103 Node::IntLiteral(&(_, pos))
1104 | Node::FloatingLiteral(&(_, pos))
1105 | Node::StringLiteral(&(_, pos))
1106 | Node::BooleanLiteral(&(_, pos)) => pos,
1107 Node::ListItem(&(fst, snd)) => self.merge_positions(fst, snd),
1108 Node::List(items) => self.pos_from_slice(items),
1109 Node::BracketedList(&(first_pos, inner_list, second_pos)) => self.merge(
1111 self.merge(self.pos_from_slice(inner_list), second_pos),
1113 Node::Expr(&aast::Expr(_, pos, _)) => pos,
1114 Node::Token(token) => self.token_pos(token),
1117 if pos.is_none() { None } else { Some(pos) }
1120 fn token_pos(&self, token: FixedWidthToken) -> &'a Pos<'a> {
1121 let start = token.offset();
1122 let end = start + token.width();
1123 let start = self.source_text.offset_to_file_pos_triple(start);
1124 let end = self.source_text.offset_to_file_pos_triple(end);
1125 Pos::from_lnum_bol_offset(self.arena, self.filename, start, end)
1128 fn node_to_expr(&self, node: Node<'a>) -> Option<&'a nast::Expr<'a>> {
1129 let expr_ = match node {
1130 Node::Expr(expr) => return Some(expr),
1131 Node::IntLiteral(&(s, _)) => aast::Expr_::Int(s),
1132 Node::FloatingLiteral(&(s, _)) => aast::Expr_::Float(s),
1133 Node::StringLiteral(&(s, _)) => aast::Expr_::String(s),
1134 Node::BooleanLiteral((s, _)) => {
1135 if s.eq_ignore_ascii_case("true") {
1141 Node::Token(t) if t.kind() == TokenKind::NullLiteral => aast::Expr_::Null,
1142 Node::Name(..) | Node::QualifiedName(..) => {
1143 aast::Expr_::Id(self.alloc(self.elaborate_const_id(self.expect_name(node)?)))
1147 let pos = self.get_pos(node);
1148 Some(self.alloc(aast::Expr((), pos, expr_)))
1151 fn node_to_non_ret_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1152 self.node_to_ty_(node, false)
1155 fn node_to_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1156 self.node_to_ty_(node, true)
1159 fn make_supportdynamic(&self, pos: &'a Pos<'a>) -> Ty_<'a> {
1160 Ty_::Tapply(self.alloc((
1161 (pos, naming_special_names::typehints::HH_SUPPORTDYN),
1162 self.alloc([self.alloc(Ty(
1163 self.alloc(Reason::witness_from_decl(pos)),
1168 fn node_to_ty_(&self, node: Node<'a>, allow_non_ret_ty: bool) -> Option<&'a Ty<'a>> {
1170 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tvoid))) if !allow_non_ret_ty => {
1171 Some(self.alloc(Ty(reason, Ty_::Terr)))
1173 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tnoreturn))) if !allow_non_ret_ty => {
1174 Some(self.alloc(Ty(reason, Ty_::Terr)))
1176 Node::Ty(ty) => Some(ty),
1177 Node::Expr(expr) => {
1178 fn expr_to_ty<'a>(arena: &'a Bump, expr: &'a nast::Expr<'a>) -> Option<Ty_<'a>> {
1181 Null => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tnull))),
1182 This => Some(Ty_::Tthis),
1183 True | False => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tbool))),
1184 Int(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tint))),
1185 Float(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tfloat))),
1186 String(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1187 String2(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1188 PrefixedString(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1189 Unop(&(_op, expr)) => expr_to_ty(arena, expr),
1190 Hole(&(expr, _, _, _)) => expr_to_ty(arena, expr),
1192 ArrayGet(_) | As(_) | Await(_) | Binop(_) | Call(_) | Cast(_)
1193 | ClassConst(_) | ClassGet(_) | Clone(_) | Collection(_) | Darray(_)
1194 | Dollardollar(_) | Efun(_) | Eif(_) | EnumClassLabel(_) | ETSplice(_)
1195 | ExpressionTree(_) | FunctionPointer(_) | FunId(_) | Id(_) | Import(_)
1196 | Is(_) | KeyValCollection(_) | Lfun(_) | List(_) | Lplaceholder(_)
1197 | Lvar(_) | MethodCaller(_) | MethodId(_) | New(_) | ObjGet(_)
1198 | Omitted | Pair(_) | Pipe(_) | ReadonlyExpr(_) | Shape(_)
1199 | SmethodId(_) | Tuple(_) | Upcast(_) | ValCollection(_) | Varray(_)
1200 | Xml(_) | Yield(_) => None,
1204 self.alloc(Reason::witness_from_decl(expr.1)),
1205 expr_to_ty(self.arena, expr)?,
1208 Node::IntLiteral((_, pos)) => Some(self.alloc(Ty(
1209 self.alloc(Reason::witness_from_decl(pos)),
1210 Ty_::Tprim(self.alloc(aast::Tprim::Tint)),
1212 Node::FloatingLiteral((_, pos)) => Some(self.alloc(Ty(
1213 self.alloc(Reason::witness_from_decl(pos)),
1214 Ty_::Tprim(self.alloc(aast::Tprim::Tfloat)),
1216 Node::StringLiteral((_, pos)) => Some(self.alloc(Ty(
1217 self.alloc(Reason::witness_from_decl(pos)),
1218 Ty_::Tprim(self.alloc(aast::Tprim::Tstring)),
1220 Node::BooleanLiteral((_, pos)) => Some(self.alloc(Ty(
1221 self.alloc(Reason::witness_from_decl(pos)),
1222 Ty_::Tprim(self.alloc(aast::Tprim::Tbool)),
1224 Node::Token(t) if t.kind() == TokenKind::Varray => {
1225 let pos = self.token_pos(t);
1226 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1227 let ty_ = Ty_::Tapply(self.alloc((
1228 (self.token_pos(t), naming_special_names::collections::VEC),
1231 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1233 Node::Token(t) if t.kind() == TokenKind::Darray => {
1234 let pos = self.token_pos(t);
1235 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1236 let ty_ = Ty_::Tapply(self.alloc((
1237 (self.token_pos(t), naming_special_names::collections::DICT),
1238 self.alloc([tany, tany]),
1240 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1242 Node::Token(t) if t.kind() == TokenKind::This => {
1243 Some(self.alloc(Ty(self.alloc(Reason::hint(self.token_pos(t))), Ty_::Tthis)))
1245 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1246 let pos = self.token_pos(t);
1248 self.alloc(Reason::hint(pos)),
1249 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1252 // In coeffects contexts, we get types like `ctx $f` or `$v::C`.
1253 // Node::Variable is used for the `$f` and `$v`, so that we don't
1254 // incorrectly attempt to elaborate them as names.
1255 Node::Variable(&(name, pos)) => Some(self.alloc(Ty(
1256 self.alloc(Reason::hint(pos)),
1257 Ty_::Tapply(self.alloc(((pos, name), &[][..]))),
1260 let Id(pos, name) = self.expect_name(node)?;
1261 let reason = self.alloc(Reason::hint(pos));
1262 let ty_ = if self.is_type_param_in_scope(name) {
1263 // TODO (T69662957) must fill type args of Tgeneric
1264 Ty_::Tgeneric(self.alloc((name, &[])))
1267 "nothing" => Ty_::Tunion(&[]),
1269 if self.opts.everything_sdt {
1270 self.make_supportdynamic(pos)
1275 "dynamic" => Ty_::Tdynamic,
1276 "supportdynamic" => self.make_supportdynamic(pos),
1277 "varray_or_darray" | "vec_or_dict" => {
1278 let key_type = self.vec_or_dict_key(pos);
1279 let value_type = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1280 Ty_::TvecOrDict(self.alloc((key_type, value_type)))
1284 let name = self.elaborate_raw_id(name);
1285 Ty_::Tapply(self.alloc(((pos, name), &[][..])))
1289 Some(self.alloc(Ty(reason, ty_)))
1294 fn rewrite_ty_for_global_inference(
1296 ty: Option<&'a Ty<'a>>,
1298 ) -> Option<&'a Ty<'a>> {
1299 if !self.opts.global_inference {
1302 let tvar = self.alloc(Ty(self.alloc(reason), Ty_::Tvar(0)));
1306 fn cut_namespace<'a>(id: &'a str) -> &'a str {
1307 id.rsplit_terminator('\\').next().unwrap()
1309 fn reinfer_type_to_string_opt<'a>(arena: &'a Bump, ty: Ty_<'a>) -> Option<&'a str> {
1311 Ty_::Tmixed => Some("mixed"),
1312 Ty_::Tnonnull => Some("nonnull"),
1313 Ty_::Tdynamic => Some("dynamic"),
1314 Ty_::Tunion([]) => Some("nothing"),
1315 Ty_::Tthis => Some("this"),
1316 Ty_::Tprim(prim) => Some(match prim {
1317 aast::Tprim::Tnull => "null",
1318 aast::Tprim::Tvoid => "void",
1319 aast::Tprim::Tint => "int",
1320 aast::Tprim::Tnum => "num",
1321 aast::Tprim::Tfloat => "float",
1322 aast::Tprim::Tstring => "string",
1323 aast::Tprim::Tarraykey => "arraykey",
1324 aast::Tprim::Tresource => "resource",
1325 aast::Tprim::Tnoreturn => "noreturn",
1326 aast::Tprim::Tbool => "bool",
1328 Ty_::Tapply(((_p, id), _tyl)) => Some(cut_namespace(id)),
1329 Ty_::Taccess(TaccessType(ty, id)) => {
1330 reinfer_type_to_string_opt(arena, ty.1).map(|s| {
1331 bumpalo::format!(in arena, "{}::{}", s, id.1).into_bump_str()
1337 fn create_vars_for_reinfer_types<'a, 'text, S: SourceTextAllocator<'text, 'a>>(
1338 this: &DirectDeclSmartConstructors<'a, 'text, S>,
1342 let mk = |r, ty_| this.alloc(Ty(r, ty_));
1343 let must_reinfer_type = |ty| match reinfer_type_to_string_opt(this.arena, ty) {
1345 Some(ty_str) => this.opts.gi_reinfer_types.contains(&ty_str),
1348 Ty(r, Ty_::Tapply(&(id, [ty1]))) if id.1 == "\\HH\\Awaitable" => {
1349 let ty1 = this.alloc(create_vars_for_reinfer_types(this, *ty1, tvar));
1350 mk(r, Ty_::Tapply(this.alloc((id, std::slice::from_ref(ty1)))))
1352 Ty(r, Ty_::Toption(ty1)) => {
1353 let ty1 = create_vars_for_reinfer_types(this, ty1, tvar);
1354 mk(r, Ty_::Toption(ty1))
1356 Ty(r, Ty_::Tapply(((_p, id), [])))
1357 if cut_namespace(id) == "PHPism_FIXME_Array" =>
1359 if must_reinfer_type(ty.1) {
1360 let tvar = mk(r, Ty_::Tvar(0));
1361 mk(r, Ty_::TvecOrDict(this.alloc((tvar, tvar))))
1367 if must_reinfer_type(ty_) {
1375 create_vars_for_reinfer_types(self, ty, tvar)
1381 fn to_attributes(&self, node: Node<'a>) -> Attributes<'a> {
1382 let mut attributes = Attributes {
1392 accept_disposable: false,
1393 dynamically_callable: false,
1394 returns_disposable: false,
1396 ifc_attribute: default_ifc_fun_decl(),
1400 support_dynamic_type: false,
1401 module: self.module,
1405 let nodes = match node {
1406 Node::List(&nodes) | Node::BracketedList(&(_, nodes, _)) => nodes,
1407 _ => return attributes,
1410 let mut ifc_already_policied = false;
1412 // Iterate in reverse, to match the behavior of OCaml decl in error conditions.
1413 for attribute in nodes.iter().rev() {
1414 if let Node::Attribute(attribute) = attribute {
1415 match attribute.name.1 {
1417 attributes.deprecated = attribute
1418 .string_literal_params
1420 .map(|&(_, x)| self.str_from_utf8_for_bytes_in_arena(x));
1422 "__Reifiable" => attributes.reifiable = Some(attribute.name.0),
1424 attributes.late_init = true;
1427 attributes.const_ = true;
1430 attributes.lsb = true;
1433 attributes.memoize = true;
1436 attributes.memoizelsb = true;
1439 attributes.override_ = true;
1441 "__Enforceable" => {
1442 attributes.enforceable = Some(attribute.name.0);
1444 "__AcceptDisposable" => {
1445 attributes.accept_disposable = true;
1447 "__DynamicallyCallable" => {
1448 attributes.dynamically_callable = true;
1450 "__ReturnDisposable" => {
1451 attributes.returns_disposable = true;
1454 attributes.php_std_lib = true;
1457 let string_literal_params = || {
1459 .string_literal_params
1461 .map(|&(_, x)| self.str_from_utf8_for_bytes_in_arena(x))
1463 // Take the classname param by default
1464 attributes.ifc_attribute =
1465 IfcFunDecl::FDPolicied(attribute.classname_params.first().map_or_else(
1466 string_literal_params, // default
1467 |&x| Some(x.name.1), // f
1469 ifc_already_policied = true;
1472 if !ifc_already_policied {
1473 attributes.ifc_attribute = IfcFunDecl::FDInferFlows;
1477 attributes.external = true;
1480 attributes.can_call = true;
1483 attributes.soft = true;
1485 "__SupportDynamicType" => {
1486 attributes.support_dynamic_type = true;
1489 attributes.internal = true;
1499 // Limited version of node_to_ty that matches behavior of Decl_utils.infer_const
1500 fn infer_const(&self, name: Node<'a>, node: Node<'a>) -> Option<&'a Ty<'a>> {
1502 Node::StringLiteral(_)
1503 | Node::BooleanLiteral(_)
1504 | Node::IntLiteral(_)
1505 | Node::FloatingLiteral(_)
1506 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uminus, _))))
1507 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uplus, _))))
1508 | Node::Expr(aast::Expr(_, _, aast::Expr_::String(..))) => self.node_to_ty(node),
1509 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1510 let pos = self.token_pos(t);
1512 self.alloc(Reason::witness_from_decl(pos)),
1513 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1516 _ => Some(self.tany_with_pos(self.get_pos(name))),
1520 fn pop_type_params(&mut self, node: Node<'a>) -> &'a [&'a Tparam<'a>] {
1522 Node::TypeParameters(tparams) => {
1523 Rc::make_mut(&mut self.type_parameters).pop().unwrap();
1530 fn ret_from_fun_kind(&self, kind: FunKind, type_: &'a Ty<'a>) -> &'a Ty<'a> {
1531 let pos = type_.get_pos().unwrap_or(NO_POS);
1533 FunKind::FAsyncGenerator => self.alloc(Ty(
1534 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1535 Ty_::Tapply(self.alloc((
1536 (pos, naming_special_names::classes::ASYNC_GENERATOR),
1537 self.alloc([type_, type_, type_]),
1540 FunKind::FGenerator => self.alloc(Ty(
1541 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1542 Ty_::Tapply(self.alloc((
1543 (pos, naming_special_names::classes::GENERATOR),
1544 self.alloc([type_, type_, type_]),
1547 FunKind::FAsync => self.alloc(Ty(
1548 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1549 Ty_::Tapply(self.alloc((
1550 (pos, naming_special_names::classes::AWAITABLE),
1551 self.alloc([type_]),
1558 fn is_type_param_in_scope(&self, name: &str) -> bool {
1559 self.type_parameters.iter().any(|tps| tps.contains(name))
1562 fn as_fun_implicit_params(
1564 capability: Node<'a>,
1565 default_pos: &'a Pos<'a>,
1566 ) -> &'a FunImplicitParams<'a> {
1567 /* Note: do not simplify intersections, keep empty / singleton intersections
1568 * for coeffect contexts
1570 let capability = match self.node_to_ty(capability) {
1571 Some(ty) => CapTy(ty),
1572 None => CapDefaults(default_pos),
1574 self.alloc(FunImplicitParams { capability })
1580 attributes: Node<'a>,
1581 header: &'a FunctionHeader<'a>,
1583 ) -> Option<(PosId<'a>, &'a Ty<'a>, &'a [ShallowProp<'a>])> {
1584 let id_opt = match (is_method, header.name) {
1585 // If the name is missing, we use the left paren here, just to get a
1586 // position to point to.
1587 (_, Node::Token(t)) if t.kind() == TokenKind::LeftParen => {
1588 let pos = self.token_pos(t);
1591 (true, Node::Token(t)) if t.kind() == TokenKind::Construct => {
1592 let pos = self.token_pos(t);
1593 Some(Id(pos, naming_special_names::members::__CONSTRUCT))
1595 (true, _) => self.expect_name(header.name),
1596 (false, _) => self.elaborate_defined_id(header.name),
1598 let id = id_opt.unwrap_or_else(|| Id(self.get_pos(header.name), ""));
1599 let (params, properties, variadic) = self.as_fun_params(header.param_list)?;
1600 let f_pos = self.get_pos(header.name);
1601 let implicit_params = self.as_fun_implicit_params(header.capability, f_pos);
1603 let type_ = match header.name {
1604 Node::Token(t) if t.kind() == TokenKind::Construct => {
1605 let pos = self.token_pos(t);
1607 self.alloc(Reason::witness_from_decl(pos)),
1608 Ty_::Tprim(self.alloc(aast::Tprim::Tvoid)),
1612 .rewrite_ty_for_global_inference(
1613 self.node_to_ty(header.ret_hint),
1614 Reason::RglobalFunRet(f_pos),
1616 .unwrap_or_else(|| self.tany_with_pos(f_pos)),
1621 .any(|n| n.is_token(TokenKind::Async));
1622 let readonly = header
1625 .any(|n| n.is_token(TokenKind::Readonly));
1627 let fun_kind = if body.iter().any(|node| node.is_token(TokenKind::Yield)) {
1629 FunKind::FAsyncGenerator
1638 let type_ = if !header.ret_hint.is_present() {
1639 self.ret_from_fun_kind(fun_kind, type_)
1643 let attributes = self.to_attributes(attributes);
1644 // TODO(hrust) Put this in a helper. Possibly do this for all flags.
1645 let mut flags = match fun_kind {
1646 FunKind::FSync => FunTypeFlags::empty(),
1647 FunKind::FAsync => FunTypeFlags::ASYNC,
1648 FunKind::FGenerator => FunTypeFlags::GENERATOR,
1649 FunKind::FAsyncGenerator => FunTypeFlags::ASYNC | FunTypeFlags::GENERATOR,
1652 if attributes.returns_disposable {
1653 flags |= FunTypeFlags::RETURN_DISPOSABLE;
1655 if attributes.support_dynamic_type {
1656 flags |= FunTypeFlags::SUPPORT_DYNAMIC_TYPE;
1658 if header.readonly_return.is_token(TokenKind::Readonly) {
1659 flags |= FunTypeFlags::RETURNS_READONLY;
1661 if attributes.memoize || attributes.memoizelsb {
1662 flags |= FunTypeFlags::IS_MEMOIZED;
1665 flags |= FunTypeFlags::READONLY_THIS
1668 flags |= FunTypeFlags::VARIADIC
1671 let ifc_decl = attributes.ifc_attribute;
1673 // Pop the type params stack only after creating all inner types.
1674 let tparams = self.pop_type_params(header.type_params);
1676 let where_constraints =
1677 self.slice(header.where_constraints.iter().filter_map(|&x| match x {
1678 Node::WhereConstraint(x) => Some(x),
1682 let (params, tparams, implicit_params, where_constraints) =
1683 self.rewrite_effect_polymorphism(params, tparams, implicit_params, where_constraints);
1685 let ft = self.alloc(FunType {
1690 ret: self.alloc(PossiblyEnforcedTy {
1691 enforced: Enforcement::Unenforced,
1698 let ty = self.alloc(Ty(
1699 self.alloc(Reason::witness_from_decl(id.0)),
1702 Some((id.into(), ty, properties))
1708 ) -> Option<(&'a FunParams<'a>, &'a [ShallowProp<'a>], bool)> {
1710 Node::List(nodes) => {
1711 let mut params = Vec::with_capacity_in(nodes.len(), self.arena);
1712 let mut properties = Vec::new_in(self.arena);
1713 let mut ft_variadic = false;
1714 for node in nodes.iter() {
1716 Node::FunParam(&FunParamDecl {
1727 let attributes = self.to_attributes(attributes);
1729 if let Some(visibility) = visibility.as_visibility() {
1730 let name = name.unwrap_or("");
1731 let name = strip_dollar_prefix(name);
1732 let mut flags = PropFlags::empty();
1733 flags.set(PropFlags::CONST, attributes.const_);
1734 flags.set(PropFlags::NEEDS_INIT, self.file_mode != Mode::Mhhi);
1735 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
1736 flags.set(PropFlags::READONLY, readonly);
1737 properties.push(ShallowProp {
1740 type_: self.rewrite_ty_for_global_inference(
1741 self.node_to_ty(hint),
1742 Reason::RglobalFunParam(pos),
1744 visibility: if attributes.internal
1745 && visibility == aast::Visibility::Public
1747 aast::Visibility::Internal
1756 .rewrite_ty_for_global_inference(
1757 self.node_to_ty(hint),
1758 Reason::RglobalFunParam(pos),
1760 .unwrap_or_else(|| self.tany_with_pos(pos));
1761 // These are illegal here--they can only be used on
1762 // parameters in a function type hint (see
1763 // make_closure_type_specifier and unwrap_mutability).
1764 // Unwrap them here anyway for better error recovery.
1765 let type_ = match type_ {
1766 Ty(_, Ty_::Tapply(((_, "\\Mutable"), [t]))) => t,
1767 Ty(_, Ty_::Tapply(((_, "\\OwnedMutable"), [t]))) => t,
1768 Ty(_, Ty_::Tapply(((_, "\\MaybeMutable"), [t]))) => t,
1771 let mut flags = FunParamFlags::empty();
1772 if attributes.accept_disposable {
1773 flags |= FunParamFlags::ACCEPT_DISPOSABLE
1775 if attributes.external {
1776 flags |= FunParamFlags::IFC_EXTERNAL
1778 if attributes.can_call {
1779 flags |= FunParamFlags::IFC_CAN_CALL
1782 flags |= FunParamFlags::READONLY
1785 ParamMode::FPinout => {
1786 flags |= FunParamFlags::INOUT;
1788 ParamMode::FPnormal => {}
1791 if initializer.is_present() {
1792 flags |= FunParamFlags::HAS_DEFAULT;
1797 let variadic = initializer.is_ignored() && variadic;
1798 let type_ = if variadic {
1800 self.alloc(if name.is_some() {
1801 Reason::RvarParamFromDecl(pos)
1803 Reason::witness_from_decl(pos)
1810 let param = self.alloc(FunParam {
1813 type_: self.alloc(PossiblyEnforcedTy {
1814 enforced: Enforcement::Unenforced,
1825 params.into_bump_slice(),
1826 properties.into_bump_slice(),
1830 n if n.is_ignored() => Some((&[], &[], false)),
1835 fn make_shape_field_name(&self, name: Node<'a>) -> Option<ShapeFieldName<'a>> {
1837 Node::StringLiteral(&(s, pos)) => ShapeFieldName::SFlitStr(self.alloc((pos, s))),
1838 // TODO: OCaml decl produces SFlitStr here instead of SFlitInt, so
1839 // we must also. Looks like int literal keys have become a parse
1840 // error--perhaps that's why.
1841 Node::IntLiteral(&(s, pos)) => ShapeFieldName::SFlitStr(self.alloc((pos, s.into()))),
1842 Node::Expr(aast::Expr(
1845 aast::Expr_::ClassConst(&(
1846 aast::ClassId(_, _, aast::ClassId_::CI(&class_name)),
1849 )) => ShapeFieldName::SFclassConst(self.alloc((class_name, const_name))),
1850 Node::Expr(aast::Expr(
1853 aast::Expr_::ClassConst(&(
1854 aast::ClassId(_, pos, aast::ClassId_::CIself),
1857 )) => ShapeFieldName::SFclassConst(self.alloc((
1860 self.classish_name_builder.get_current_classish_name()?.0,
1870 fn make_t_shape_field_name(&mut self, ShapeField(field): &ShapeField<'a>) -> TShapeField<'a> {
1871 TShapeField(match field {
1872 ShapeFieldName::SFlitInt(&(pos, x)) => {
1873 TshapeFieldName::TSFlitInt(self.alloc(PosString(pos, x)))
1875 ShapeFieldName::SFlitStr(&(pos, x)) => {
1876 TshapeFieldName::TSFlitStr(self.alloc(PosByteString(pos, x)))
1878 ShapeFieldName::SFclassConst(&(id, &(pos, x))) => {
1879 TshapeFieldName::TSFclassConst(self.alloc((id.into(), PosString(pos, x))))
1887 type_arguments: Node<'a>,
1888 pos_to_merge: &'a Pos<'a>,
1890 let type_arguments = self.slice(
1893 .filter_map(|&node| self.node_to_ty(node)),
1896 let pos = self.merge(base_ty.0, pos_to_merge);
1898 // OCaml decl creates a capability with a hint pointing to the entire
1899 // type (i.e., pointing to `Rx<(function(): void)>` rather than just
1900 // `(function(): void)`), so we extend the hint position similarly here.
1901 let extend_capability_pos = |implicit_params: &'a FunImplicitParams<'_>| {
1902 let capability = match implicit_params.capability {
1904 let ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), ty.1));
1907 CapDefaults(_) => CapDefaults(pos),
1909 self.alloc(FunImplicitParams {
1915 let ty_ = match (base_ty, type_arguments) {
1916 ((_, name), &[&Ty(_, Ty_::Tfun(f))]) if name == "\\Pure" => {
1917 Ty_::Tfun(self.alloc(FunType {
1918 implicit_params: extend_capability_pos(f.implicit_params),
1922 _ => Ty_::Tapply(self.alloc((base_ty, type_arguments))),
1925 self.hint_ty(pos, ty_)
1928 fn hint_ty(&self, pos: &'a Pos<'a>, ty_: Ty_<'a>) -> Node<'a> {
1929 Node::Ty(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1932 fn prim_ty(&self, tprim: aast::Tprim, pos: &'a Pos<'a>) -> Node<'a> {
1933 self.hint_ty(pos, Ty_::Tprim(self.alloc(tprim)))
1936 fn tany_with_pos(&self, pos: &'a Pos<'a>) -> &'a Ty<'a> {
1937 self.alloc(Ty(self.alloc(Reason::witness_from_decl(pos)), TANY_))
1940 /// The type used when a `vec_or_dict` typehint is missing its key type argument.
1941 fn vec_or_dict_key(&self, pos: &'a Pos<'a>) -> &'a Ty<'a> {
1943 self.alloc(Reason::RvecOrDictKey(pos)),
1944 Ty_::Tprim(self.alloc(aast::Tprim::Tarraykey)),
1948 fn source_text_at_pos(&self, pos: &'a Pos<'a>) -> &'text [u8] {
1949 let start = pos.start_offset();
1950 let end = pos.end_offset();
1951 self.source_text.source_text().sub(start, end - start)
1954 // While we usually can tell whether to allocate a Tapply or Tgeneric based
1955 // on our type_parameters stack, *constraints* on type parameters may
1956 // reference type parameters which we have not parsed yet. When constructing
1957 // a type parameter list, we use this function to rewrite the type of each
1958 // constraint, considering the full list of type parameters to be in scope.
1959 fn convert_tapply_to_tgeneric(&self, ty: &'a Ty<'a>) -> &'a Ty<'a> {
1960 let ty_ = match ty.1 {
1961 Ty_::Tapply(&(id, targs)) => {
1962 let converted_targs = self.slice(
1965 .map(|&targ| self.convert_tapply_to_tgeneric(targ)),
1967 match self.tapply_should_be_tgeneric(ty.0, id) {
1968 Some(name) => Ty_::Tgeneric(self.alloc((name, converted_targs))),
1969 None => Ty_::Tapply(self.alloc((id, converted_targs))),
1972 Ty_::Tlike(ty) => Ty_::Tlike(self.convert_tapply_to_tgeneric(ty)),
1973 Ty_::Toption(ty) => Ty_::Toption(self.convert_tapply_to_tgeneric(ty)),
1974 Ty_::Tfun(fun_type) => {
1975 let convert_param = |param: &'a FunParam<'a>| {
1976 self.alloc(FunParam {
1977 type_: self.alloc(PossiblyEnforcedTy {
1978 enforced: param.type_.enforced,
1979 type_: self.convert_tapply_to_tgeneric(param.type_.type_),
1984 let params = self.slice(fun_type.params.iter().copied().map(convert_param));
1985 let implicit_params = fun_type.implicit_params;
1986 let ret = self.alloc(PossiblyEnforcedTy {
1987 enforced: fun_type.ret.enforced,
1988 type_: self.convert_tapply_to_tgeneric(fun_type.ret.type_),
1990 Ty_::Tfun(self.alloc(FunType {
1997 Ty_::Tshape(&(kind, fields)) => {
1998 let mut converted_fields = AssocListMut::with_capacity_in(fields.len(), self.arena);
1999 for (&name, ty) in fields.iter() {
2000 converted_fields.insert(
2002 self.alloc(ShapeFieldType {
2003 optional: ty.optional,
2004 ty: self.convert_tapply_to_tgeneric(ty.ty),
2008 Ty_::Tshape(self.alloc((kind, converted_fields.into())))
2010 Ty_::TvecOrDict(&(tk, tv)) => Ty_::TvecOrDict(self.alloc((
2011 self.convert_tapply_to_tgeneric(tk),
2012 self.convert_tapply_to_tgeneric(tv),
2014 Ty_::Ttuple(tys) => Ty_::Ttuple(
2017 .map(|&targ| self.convert_tapply_to_tgeneric(targ)),
2022 self.alloc(Ty(ty.0, ty_))
2025 // This is the logic for determining if convert_tapply_to_tgeneric should turn
2026 // a Tapply into a Tgeneric
2027 fn tapply_should_be_tgeneric(&self, reason: &'a Reason<'a>, id: PosId<'a>) -> Option<&'a str> {
2028 match reason.pos() {
2029 // If the name contained a namespace delimiter in the original
2030 // source text, then it can't have referred to a type parameter
2031 // (since type parameters cannot be namespaced).
2033 if self.source_text_at_pos(pos).contains(&b'\\') {
2037 None => return None,
2039 // However, the direct decl parser will unconditionally prefix
2040 // the name with the current namespace (as it does for any
2041 // Tapply). We need to remove it.
2042 match id.1.rsplit('\\').next() {
2043 Some(name) if self.is_type_param_in_scope(name) => Some(name),
2048 fn rewrite_taccess_reasons(&self, ty: &'a Ty<'a>, r: &'a Reason<'a>) -> &'a Ty<'a> {
2049 let ty_ = match ty.1 {
2050 Ty_::Taccess(&TaccessType(ty, id)) => {
2051 Ty_::Taccess(self.alloc(TaccessType(self.rewrite_taccess_reasons(ty, r), id)))
2055 self.alloc(Ty(r, ty_))
2058 fn user_attribute_to_decl(
2060 attr: &UserAttributeNode<'a>,
2061 ) -> &'a shallow_decl_defs::UserAttribute<'a> {
2062 self.alloc(shallow_decl_defs::UserAttribute {
2063 name: attr.name.into(),
2064 classname_params: self.slice(attr.classname_params.iter().map(|p| p.name.1)),
2068 fn set_module(&mut self, attributes: &Node<'a>) {
2069 for attr in attributes.iter() {
2070 if let Node::Attribute(attr) = attr {
2071 if attr.name.1 == "__Module" {
2072 self.module = attr.string_literal_params.first().map(|&(p, m)| {
2073 oxidized_by_ref::ast::Id(p, self.str_from_utf8_for_bytes_in_arena(m))
2081 fn namespace_use_kind(use_kind: &Node<'_>) -> Option<NamespaceUseKind> {
2082 match use_kind.token_kind() {
2083 Some(TokenKind::Const) => None,
2084 Some(TokenKind::Function) => None,
2085 Some(TokenKind::Type) => Some(NamespaceUseKind::Type),
2086 Some(TokenKind::Namespace) => Some(NamespaceUseKind::Namespace),
2087 _ if !use_kind.is_present() => Some(NamespaceUseKind::Mixed),
2092 fn has_polymorphic_context(contexts: &[&Ty<'_>]) -> bool {
2093 contexts.iter().any(|&ty| match ty.1 {
2094 Ty_::Tapply((root, &[])) // Hfun_context in the AST
2095 | Ty_::Taccess(TaccessType(Ty(_, Ty_::Tapply((root, &[]))), _)) => root.1.contains('$'),
2096 | Ty_::Taccess(TaccessType(t, _)) => Self::taccess_root_is_generic(t),
2101 fn ctx_generic_for_fun(&self, name: &str) -> &'a str {
2102 bumpalo::format!(in self.arena, "T/[ctx {}]", name).into_bump_str()
2105 fn ctx_generic_for_dependent(&self, name: &str, cst: &str) -> &'a str {
2106 bumpalo::format!(in self.arena, "T/[{}::{}]", name, cst).into_bump_str()
2109 // Note: the reason for the divergence between this and the lowerer is that
2110 // hint Haccess is a flat list, whereas decl ty Taccess is a tree.
2111 fn taccess_root_is_generic(ty: &Ty<'_>) -> bool {
2113 Ty(_, Ty_::Tgeneric((_, &[]))) => true,
2114 Ty(_, Ty_::Taccess(&TaccessType(t, _))) => Self::taccess_root_is_generic(t),
2119 fn ctx_generic_for_generic_taccess_inner(&self, ty: &Ty<'_>, cst: &str) -> std::string::String {
2120 let left = match ty {
2121 Ty(_, Ty_::Tgeneric((name, &[]))) => name.to_string(),
2122 Ty(_, Ty_::Taccess(&TaccessType(ty, cst))) => {
2123 self.ctx_generic_for_generic_taccess_inner(ty, cst.1)
2125 _ => panic!("Unexpected element in Taccess"),
2127 format!("{}::{}", left, cst)
2129 fn ctx_generic_for_generic_taccess(&self, ty: &Ty<'_>, cst: &str) -> &'a str {
2130 bumpalo::format!(in self.arena, "T/[{}]", self.ctx_generic_for_generic_taccess_inner(ty, cst))
2134 // For a polymorphic context with form `ctx $f` (represented here as
2135 // `Tapply "$f"`), add a type parameter named `Tctx$f`, and rewrite the
2136 // parameter `(function (ts)[_]: t) $f` as `(function (ts)[Tctx$f]: t) $f`
2139 tparams: &mut Vec<'_, &'a Tparam<'a>>,
2145 let cap_ty = match ft.implicit_params.capability {
2146 CapTy(&Ty(_, Ty_::Tintersection(&[ty]))) | CapTy(ty) => ty,
2147 _ => return ty.clone(),
2149 let pos = match cap_ty.1 {
2150 Ty_::Tapply(((pos, "_"), _)) => pos,
2151 _ => return ty.clone(),
2153 let name = self.ctx_generic_for_fun(param_name);
2154 let tparam = self.alloc(Tparam {
2155 variance: Variance::Invariant,
2159 reified: aast::ReifyKind::Erased,
2160 user_attributes: &[],
2162 tparams.push(tparam);
2163 let cap_ty = self.alloc(Ty(cap_ty.0, Ty_::Tgeneric(self.alloc((name, &[])))));
2164 let ft = self.alloc(FunType {
2165 implicit_params: self.alloc(FunImplicitParams {
2166 capability: CapTy(cap_ty),
2170 Ty(ty.0, Ty_::Tfun(ft))
2172 Ty_::Tlike(t) => Ty(
2174 Ty_::Tlike(self.alloc(self.rewrite_fun_ctx(tparams, t, param_name))),
2176 Ty_::Toption(t) => Ty(
2178 Ty_::Toption(self.alloc(self.rewrite_fun_ctx(tparams, t, param_name))),
2180 Ty_::Tapply(((p, name), targs))
2181 if *name == naming_special_names::typehints::HH_SUPPORTDYN =>
2183 if let Some(t) = targs.first() {
2186 Ty_::Tapply(self.alloc((
2188 self.alloc([self.alloc(self.rewrite_fun_ctx(tparams, t, param_name))]),
2199 fn rewrite_effect_polymorphism(
2201 params: &'a [&'a FunParam<'a>],
2202 tparams: &'a [&'a Tparam<'a>],
2203 implicit_params: &'a FunImplicitParams<'a>,
2204 where_constraints: &'a [&'a WhereConstraint<'a>],
2206 &'a [&'a FunParam<'a>],
2207 &'a [&'a Tparam<'a>],
2208 &'a FunImplicitParams<'a>,
2209 &'a [&'a WhereConstraint<'a>],
2211 let (cap_reason, context_tys) = match implicit_params.capability {
2212 CapTy(&Ty(r, Ty_::Tintersection(tys))) if Self::has_polymorphic_context(tys) => {
2215 CapTy(ty) if Self::has_polymorphic_context(&[ty]) => {
2216 (ty.0, std::slice::from_ref(self.alloc(ty)))
2218 _ => return (params, tparams, implicit_params, where_constraints),
2220 let tp = |name, constraints| {
2222 variance: Variance::Invariant,
2226 reified: aast::ReifyKind::Erased,
2227 user_attributes: &[],
2231 // For a polymorphic context with form `$g::C`, if we have a function
2232 // parameter `$g` with type `G` (where `G` is not a type parameter),
2233 // - add a type parameter constrained by $g's type: `T/$g as G`
2234 // - replace $g's type hint (`G`) with the new type parameter `T/$g`
2235 // Then, for each polymorphic context with form `$g::C`,
2236 // - add a type parameter `T/[$g::C]`
2237 // - add a where constraint `T/[$g::C] = T$g :: C`
2238 let rewrite_arg_ctx = |
2239 tparams: &mut Vec<'_, &'a Tparam<'a>>,
2240 where_constraints: &mut Vec<'_, &'a WhereConstraint<'a>>,
2242 param_pos: &'a Pos<'a>,
2244 context_reason: &'a Reason<'a>,
2247 let rewritten_ty = match ty.1 {
2248 // If the type hint for this function parameter is a type
2249 // parameter introduced in this function declaration, don't add
2250 // a new type parameter.
2251 Ty_::Tgeneric(&(type_name, _))
2252 if tparams.iter().any(|tp| tp.name.1 == type_name) =>
2256 // Otherwise, if the parameter is `G $g`, create tparam
2257 // `T$g as G` and replace $g's type hint
2259 let id = (param_pos, self.concat("T/", name));
2262 std::slice::from_ref(
2263 self.alloc((ConstraintKind::ConstraintAs, self.alloc(ty.clone()))),
2267 self.alloc(Reason::hint(param_pos)),
2268 Ty_::Tgeneric(self.alloc((id.1, &[]))),
2272 let ty = self.alloc(Ty(context_reason, rewritten_ty.1));
2273 let right = self.alloc(Ty(
2275 Ty_::Taccess(self.alloc(TaccessType(ty, cst))),
2278 context_reason.pos().unwrap_or(NO_POS),
2279 self.ctx_generic_for_dependent(name, cst.1),
2281 tparams.push(tp(left_id, &[]));
2282 let left = self.alloc(Ty(
2284 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2286 where_constraints.push(self.alloc(WhereConstraint(
2288 ConstraintKind::ConstraintEq,
2294 let mut tparams = Vec::from_iter_in(tparams.iter().copied(), self.arena);
2295 let mut where_constraints =
2296 Vec::from_iter_in(where_constraints.iter().copied(), self.arena);
2298 // The divergence here from the lowerer comes from using oxidized_by_ref instead of oxidized
2299 let mut ty_by_param: BTreeMap<&str, (Ty<'a>, &'a Pos<'a>)> = params
2301 .filter_map(|param| Some((param.name?, (param.type_.type_.clone(), param.pos))))
2304 for context_ty in context_tys {
2305 match context_ty.1 {
2306 // Hfun_context in the AST.
2307 Ty_::Tapply(((_, name), _)) if name.starts_with('$') => {
2308 if let Some((param_ty, _)) = ty_by_param.get_mut(name) {
2309 *param_ty = self.rewrite_fun_ctx(&mut tparams, param_ty, name);
2312 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), _))), cst)) => {
2313 if let Some((param_ty, param_pos)) = ty_by_param.get_mut(name) {
2314 let mut rewrite = |t| {
2317 &mut where_constraints,
2326 Ty_::Tlike(ref mut ty) => match ty {
2327 Ty(r, Ty_::Toption(tinner)) => {
2329 self.alloc(Ty(r, Ty_::Toption(self.alloc(rewrite(tinner)))))
2332 *ty = self.alloc(rewrite(ty));
2335 Ty_::Toption(ref mut ty) => {
2336 *ty = self.alloc(rewrite(ty));
2339 *param_ty = rewrite(param_ty);
2344 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2346 context_ty.0.pos().unwrap_or(NO_POS),
2347 self.ctx_generic_for_generic_taccess(t, cst.1),
2349 tparams.push(tp(left_id, &[]));
2350 let left = self.alloc(Ty(
2352 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2354 where_constraints.push(self.alloc(WhereConstraint(
2356 ConstraintKind::ConstraintEq,
2364 let params = self.slice(params.iter().copied().map(|param| match param.name {
2366 Some(name) => match ty_by_param.get(name) {
2367 Some((type_, _)) if param.type_.type_ != type_ => self.alloc(FunParam {
2368 type_: self.alloc(PossiblyEnforcedTy {
2369 type_: self.alloc(type_.clone()),
2378 let context_tys = self.slice(context_tys.iter().copied().map(|ty| {
2379 let ty_ = match ty.1 {
2380 Ty_::Tapply(((_, name), &[])) if name.starts_with('$') => {
2381 Ty_::Tgeneric(self.alloc((self.ctx_generic_for_fun(name), &[])))
2383 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), &[]))), cst))
2384 if name.starts_with('$') =>
2386 let name = self.ctx_generic_for_dependent(name, cst.1);
2387 Ty_::Tgeneric(self.alloc((name, &[])))
2389 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2390 let name = self.ctx_generic_for_generic_taccess(t, cst.1);
2391 Ty_::Tgeneric(self.alloc((name, &[])))
2395 self.alloc(Ty(ty.0, ty_))
2397 let cap_ty = match context_tys {
2399 _ => self.alloc(Ty(cap_reason, Ty_::Tintersection(context_tys))),
2401 let implicit_params = self.alloc(FunImplicitParams {
2402 capability: CapTy(cap_ty),
2407 tparams.into_bump_slice(),
2409 where_constraints.into_bump_slice(),
2414 enum NodeIterHelper<'a, 'b> {
2416 Single(&'b Node<'a>),
2417 Vec(std::slice::Iter<'b, Node<'a>>),
2420 impl<'a, 'b> Iterator for NodeIterHelper<'a, 'b> {
2421 type Item = &'b Node<'a>;
2423 fn next(&mut self) -> Option<Self::Item> {
2425 NodeIterHelper::Empty => None,
2426 NodeIterHelper::Single(node) => {
2428 *self = NodeIterHelper::Empty;
2431 NodeIterHelper::Vec(ref mut iter) => iter.next(),
2435 // Must return the upper bound returned by Node::len.
2436 fn size_hint(&self) -> (usize, Option<usize>) {
2438 NodeIterHelper::Empty => (0, Some(0)),
2439 NodeIterHelper::Single(_) => (1, Some(1)),
2440 NodeIterHelper::Vec(iter) => iter.size_hint(),
2445 impl<'a, 'b> DoubleEndedIterator for NodeIterHelper<'a, 'b> {
2446 fn next_back(&mut self) -> Option<Self::Item> {
2448 NodeIterHelper::Empty => None,
2449 NodeIterHelper::Single(_) => self.next(),
2450 NodeIterHelper::Vec(ref mut iter) => iter.next_back(),
2455 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> FlattenOp
2456 for DirectDeclSmartConstructors<'a, 'text, S>
2460 fn flatten(&self, kind: SyntaxKind, lst: std::vec::Vec<Self::S>) -> Self::S {
2464 Node::List(children) => children.len(),
2466 if Self::is_zero(x) {
2474 let mut r = Vec::with_capacity_in(size, self.arena);
2475 for s in lst.into_iter() {
2477 Node::List(children) => r.extend(children.iter().copied()),
2479 if !Self::is_zero(&x) {
2485 match r.into_bump_slice() {
2486 [] => Node::Ignored(kind),
2488 slice => Node::List(self.alloc(slice)),
2492 fn zero(kind: SyntaxKind) -> Self::S {
2496 fn is_zero(s: &Self::S) -> bool {
2498 Node::Token(token) => match token.kind() {
2499 TokenKind::Yield | TokenKind::Required | TokenKind::Lateinit => false,
2502 Node::List(inner) => inner.iter().all(Self::is_zero),
2508 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>>
2509 FlattenSmartConstructors<'a, DirectDeclSmartConstructors<'a, 'text, S>>
2510 for DirectDeclSmartConstructors<'a, 'text, S>
2512 fn make_token(&mut self, token: CompactToken) -> Self::R {
2513 let token_text = |this: &Self| this.str_from_utf8(this.token_bytes(&token));
2514 let token_pos = |this: &Self| {
2517 .offset_to_file_pos_triple(token.start_offset());
2520 .offset_to_file_pos_triple(token.end_offset());
2521 Pos::from_lnum_bol_offset(this.arena, this.filename, start, end)
2523 let kind = token.kind();
2525 let result = match kind {
2526 TokenKind::Name | TokenKind::XHPClassName => {
2527 let text = token_text(self);
2528 let pos = token_pos(self);
2530 let name = if kind == TokenKind::XHPClassName {
2531 Node::XhpName(self.alloc((text, pos)))
2533 Node::Name(self.alloc((text, pos)))
2536 if self.previous_token_kind == TokenKind::Class
2537 || self.previous_token_kind == TokenKind::Trait
2538 || self.previous_token_kind == TokenKind::Interface
2540 if let Some(current_class_name) = self.elaborate_defined_id(name) {
2541 self.classish_name_builder
2542 .lexed_name_after_classish_keyword(
2544 current_class_name.1,
2546 self.previous_token_kind,
2552 TokenKind::Variable => Node::Variable(self.alloc((token_text(self), token_pos(self)))),
2553 // There are a few types whose string representations we have to
2554 // grab anyway, so just go ahead and treat them as generic names.
2559 | TokenKind::Classname
2560 | TokenKind::SelfToken => Node::Name(self.alloc((token_text(self), token_pos(self)))),
2561 TokenKind::XHPElementName => {
2562 Node::XhpName(self.alloc((token_text(self), token_pos(self))))
2564 TokenKind::SingleQuotedStringLiteral => match escaper::unescape_single_in(
2565 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2568 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2569 Err(_) => Node::Ignored(SK::Token(kind)),
2571 TokenKind::DoubleQuotedStringLiteral => match escaper::unescape_double_in(
2572 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2575 Ok(text) => Node::StringLiteral(self.alloc((text, token_pos(self)))),
2576 Err(_) => Node::Ignored(SK::Token(kind)),
2578 TokenKind::HeredocStringLiteral => match escaper::unescape_heredoc_in(
2579 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2582 Ok(text) if !self.retain_or_omit_user_attributes_for_facts => {
2583 Node::StringLiteral(self.alloc((text, token_pos(self))))
2585 _ => Node::Ignored(SK::Token(kind)),
2587 TokenKind::NowdocStringLiteral => match escaper::unescape_nowdoc_in(
2588 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2591 Ok(text) if !self.retain_or_omit_user_attributes_for_facts => {
2592 Node::StringLiteral(self.alloc((text.into(), token_pos(self))))
2594 _ => Node::Ignored(SK::Token(kind)),
2596 TokenKind::DecimalLiteral
2597 | TokenKind::OctalLiteral
2598 | TokenKind::HexadecimalLiteral
2599 | TokenKind::BinaryLiteral => {
2600 Node::IntLiteral(self.alloc((token_text(self), token_pos(self))))
2602 TokenKind::FloatingLiteral => {
2603 Node::FloatingLiteral(self.alloc((token_text(self), token_pos(self))))
2605 TokenKind::BooleanLiteral => {
2606 Node::BooleanLiteral(self.alloc((token_text(self), token_pos(self))))
2608 TokenKind::String => self.prim_ty(aast::Tprim::Tstring, token_pos(self)),
2609 TokenKind::Int => self.prim_ty(aast::Tprim::Tint, token_pos(self)),
2610 TokenKind::Float => self.prim_ty(aast::Tprim::Tfloat, token_pos(self)),
2611 // "double" and "boolean" are parse errors--they should be written
2612 // "float" and "bool". The decl-parser treats the incorrect names as
2613 // type names rather than primitives.
2614 TokenKind::Double | TokenKind::Boolean => self.hint_ty(
2616 Ty_::Tapply(self.alloc(((token_pos(self), token_text(self)), &[][..]))),
2618 TokenKind::Num => self.prim_ty(aast::Tprim::Tnum, token_pos(self)),
2619 TokenKind::Bool => self.prim_ty(aast::Tprim::Tbool, token_pos(self)),
2620 TokenKind::Mixed => {
2621 if self.opts.everything_sdt {
2622 Node::Ty(self.alloc(Ty(
2623 self.alloc(Reason::hint(token_pos(self))),
2624 Ty_::Toption(self.alloc(Ty(
2625 self.alloc(Reason::hint(token_pos(self))),
2626 self.make_supportdynamic(token_pos(self)),
2630 Node::Ty(self.alloc(Ty(self.alloc(Reason::hint(token_pos(self))), Ty_::Tmixed)))
2633 TokenKind::Void => self.prim_ty(aast::Tprim::Tvoid, token_pos(self)),
2634 TokenKind::Arraykey => self.prim_ty(aast::Tprim::Tarraykey, token_pos(self)),
2635 TokenKind::Noreturn => self.prim_ty(aast::Tprim::Tnoreturn, token_pos(self)),
2636 TokenKind::Resource => self.prim_ty(aast::Tprim::Tresource, token_pos(self)),
2637 TokenKind::NullLiteral
2640 | TokenKind::Backslash
2641 | TokenKind::Construct
2642 | TokenKind::LeftParen
2643 | TokenKind::RightParen
2644 | TokenKind::LeftBracket
2645 | TokenKind::RightBracket
2647 | TokenKind::Question
2650 | TokenKind::Exclamation
2653 | TokenKind::PlusPlus
2654 | TokenKind::MinusMinus
2658 | TokenKind::EqualEqual
2659 | TokenKind::EqualEqualEqual
2660 | TokenKind::StarStar
2661 | TokenKind::AmpersandAmpersand
2663 | TokenKind::LessThan
2664 | TokenKind::LessThanEqual
2665 | TokenKind::GreaterThan
2666 | TokenKind::GreaterThanEqual
2668 | TokenKind::Ampersand
2670 | TokenKind::LessThanLessThan
2671 | TokenKind::GreaterThanGreaterThan
2672 | TokenKind::Percent
2673 | TokenKind::QuestionQuestion
2675 | TokenKind::Abstract
2679 | TokenKind::DotDotDot
2680 | TokenKind::Extends
2682 | TokenKind::Implements
2684 | TokenKind::Interface
2686 | TokenKind::Newtype
2689 | TokenKind::Semicolon
2690 | TokenKind::Private
2691 | TokenKind::Protected
2697 | TokenKind::Lateinit
2698 | TokenKind::RightBrace
2701 | TokenKind::Function
2702 | TokenKind::Namespace
2704 | TokenKind::Required
2706 | TokenKind::Readonly => Node::Token(FixedWidthToken::new(kind, token.start_offset())),
2707 _ if kind.fixed_width().is_some() => {
2708 Node::IgnoredToken(FixedWidthToken::new(kind, token.start_offset()))
2710 _ => Node::Ignored(SK::Token(kind)),
2712 self.previous_token_kind = kind;
2716 fn make_error(&mut self, error: Self::R) -> Self::R {
2717 // If it's a Token or IgnoredToken, we can use it for error recovery.
2718 // For instance, in `function using() {}`, the `using` keyword will be a
2719 // token wrapped in an error CST node, since the keyword isn't legal in
2724 fn make_missing(&mut self, _: usize) -> Self::R {
2725 Node::Ignored(SK::Missing)
2728 fn make_list(&mut self, items: std::vec::Vec<Self::R>, _: usize) -> Self::R {
2729 if let Some(&yield_) = items
2731 .flat_map(|node| node.iter())
2732 .find(|node| node.is_token(TokenKind::Yield))
2736 let size = items.iter().filter(|node| node.is_present()).count();
2737 let items_iter = items.into_iter();
2738 let mut items = Vec::with_capacity_in(size, self.arena);
2739 for node in items_iter {
2740 if node.is_present() {
2744 let items = items.into_bump_slice();
2745 if items.is_empty() {
2746 Node::Ignored(SK::SyntaxList)
2748 Node::List(self.alloc(items))
2753 fn make_qualified_name(&mut self, parts: Self::R) -> Self::R {
2754 let pos = self.get_pos(parts);
2756 Node::List(nodes) => Node::QualifiedName(self.alloc((nodes, pos))),
2757 node if node.is_ignored() => Node::Ignored(SK::QualifiedName),
2758 node => Node::QualifiedName(
2759 self.alloc((bumpalo::vec![in self.arena; node].into_bump_slice(), pos)),
2764 fn make_simple_type_specifier(&mut self, specifier: Self::R) -> Self::R {
2765 // Return this explicitly because flatten filters out zero nodes, and
2766 // we treat most non-error nodes as zeroes.
2770 fn make_literal_expression(&mut self, expression: Self::R) -> Self::R {
2774 fn make_simple_initializer(&mut self, equals: Self::R, expr: Self::R) -> Self::R {
2775 // If the expr is Ignored, bubble up the assignment operator so that we
2776 // can tell that *some* initializer was here. Useful for class
2777 // properties, where we need to enforce that properties without default
2778 // values are initialized in the constructor.
2779 if expr.is_ignored() { equals } else { expr }
2782 fn make_anonymous_function(
2784 _attribute_spec: Self::R,
2785 _async_keyword: Self::R,
2786 _function_keyword: Self::R,
2787 _left_paren: Self::R,
2788 _parameters: Self::R,
2789 _right_paren: Self::R,
2792 _readonly_return: Self::R,
2797 // do not allow Yield to bubble up
2798 Node::Ignored(SK::AnonymousFunction)
2801 fn make_lambda_expression(
2803 _attribute_spec: Self::R,
2805 _signature: Self::R,
2809 // do not allow Yield to bubble up
2810 Node::Ignored(SK::LambdaExpression)
2813 fn make_awaitable_creation_expression(
2815 _attribute_spec: Self::R,
2817 _compound_statement: Self::R,
2819 // do not allow Yield to bubble up
2820 Node::Ignored(SK::AwaitableCreationExpression)
2823 fn make_element_initializer(
2829 Node::ListItem(self.alloc((key, value)))
2832 fn make_prefix_unary_expression(&mut self, op: Self::R, value: Self::R) -> Self::R {
2833 let pos = self.merge_positions(op, value);
2834 let op = match op.token_kind() {
2835 Some(TokenKind::Tilde) => Uop::Utild,
2836 Some(TokenKind::Exclamation) => Uop::Unot,
2837 Some(TokenKind::Plus) => Uop::Uplus,
2838 Some(TokenKind::Minus) => Uop::Uminus,
2839 Some(TokenKind::PlusPlus) => Uop::Uincr,
2840 Some(TokenKind::MinusMinus) => Uop::Udecr,
2841 Some(TokenKind::At) => Uop::Usilence,
2842 _ => return Node::Ignored(SK::PrefixUnaryExpression),
2844 let value = match self.node_to_expr(value) {
2845 Some(value) => value,
2846 None => return Node::Ignored(SK::PrefixUnaryExpression),
2848 Node::Expr(self.alloc(aast::Expr(
2851 aast::Expr_::Unop(self.alloc((op, value))),
2855 fn make_postfix_unary_expression(&mut self, value: Self::R, op: Self::R) -> Self::R {
2856 let pos = self.merge_positions(value, op);
2857 let op = match op.token_kind() {
2858 Some(TokenKind::PlusPlus) => Uop::Upincr,
2859 Some(TokenKind::MinusMinus) => Uop::Updecr,
2860 _ => return Node::Ignored(SK::PostfixUnaryExpression),
2862 let value = match self.node_to_expr(value) {
2863 Some(value) => value,
2864 None => return Node::Ignored(SK::PostfixUnaryExpression),
2866 Node::Expr(self.alloc(aast::Expr(
2869 aast::Expr_::Unop(self.alloc((op, value))),
2873 fn make_binary_expression(&mut self, lhs: Self::R, op_node: Self::R, rhs: Self::R) -> Self::R {
2874 let op = match op_node.token_kind() {
2875 Some(TokenKind::Plus) => Bop::Plus,
2876 Some(TokenKind::Minus) => Bop::Minus,
2877 Some(TokenKind::Star) => Bop::Star,
2878 Some(TokenKind::Slash) => Bop::Slash,
2879 Some(TokenKind::Equal) => Bop::Eq(None),
2880 Some(TokenKind::EqualEqual) => Bop::Eqeq,
2881 Some(TokenKind::EqualEqualEqual) => Bop::Eqeqeq,
2882 Some(TokenKind::StarStar) => Bop::Starstar,
2883 Some(TokenKind::AmpersandAmpersand) => Bop::Ampamp,
2884 Some(TokenKind::BarBar) => Bop::Barbar,
2885 Some(TokenKind::LessThan) => Bop::Lt,
2886 Some(TokenKind::LessThanEqual) => Bop::Lte,
2887 Some(TokenKind::LessThanLessThan) => Bop::Ltlt,
2888 Some(TokenKind::GreaterThan) => Bop::Gt,
2889 Some(TokenKind::GreaterThanEqual) => Bop::Gte,
2890 Some(TokenKind::GreaterThanGreaterThan) => Bop::Gtgt,
2891 Some(TokenKind::Dot) => Bop::Dot,
2892 Some(TokenKind::Ampersand) => Bop::Amp,
2893 Some(TokenKind::Bar) => Bop::Bar,
2894 Some(TokenKind::Percent) => Bop::Percent,
2895 Some(TokenKind::QuestionQuestion) => Bop::QuestionQuestion,
2896 _ => return Node::Ignored(SK::BinaryExpression),
2899 match (&op, rhs.is_token(TokenKind::Yield)) {
2900 (Bop::Eq(_), true) => return rhs,
2904 let pos = self.merge(self.merge_positions(lhs, op_node), self.get_pos(rhs));
2906 let lhs = match self.node_to_expr(lhs) {
2908 None => return Node::Ignored(SK::BinaryExpression),
2910 let rhs = match self.node_to_expr(rhs) {
2912 None => return Node::Ignored(SK::BinaryExpression),
2915 Node::Expr(self.alloc(aast::Expr(
2918 aast::Expr_::Binop(self.alloc((op, lhs, rhs))),
2922 fn make_parenthesized_expression(
2928 if self.retain_or_omit_user_attributes_for_facts {
2929 Node::Ignored(SK::ParenthesizedExpression)
2935 fn make_list_item(&mut self, item: Self::R, sep: Self::R) -> Self::R {
2936 match (item.is_ignored(), sep.is_ignored()) {
2937 (true, true) => Node::Ignored(SK::ListItem),
2938 (false, true) => item,
2939 (true, false) => sep,
2940 (false, false) => Node::ListItem(self.alloc((item, sep))),
2944 fn make_type_arguments(
2948 greater_than: Self::R,
2950 Node::BracketedList(self.alloc((
2951 self.get_pos(less_than),
2952 arguments.as_slice(self.arena),
2953 self.get_pos(greater_than),
2957 fn make_generic_type_specifier(
2959 class_type: Self::R,
2960 type_arguments: Self::R,
2962 let class_id = match self.expect_name(class_type) {
2964 None => return Node::Ignored(SK::GenericTypeSpecifier),
2966 match class_id.1.trim_start_matches('\\') {
2967 "varray_or_darray" | "vec_or_dict" => {
2968 let id_pos = class_id.0;
2969 let pos = self.merge(id_pos, self.get_pos(type_arguments));
2970 let type_arguments = type_arguments.as_slice(self.arena);
2971 let ty_ = match type_arguments {
2972 [tk, tv] => Ty_::TvecOrDict(
2974 self.node_to_ty(*tk)
2975 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
2976 self.node_to_ty(*tv)
2977 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
2980 [tv] => Ty_::TvecOrDict(
2982 self.vec_or_dict_key(pos),
2983 self.node_to_ty(*tv)
2984 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
2989 self.hint_ty(pos, ty_)
2992 let Id(pos, class_type) = class_id;
2993 match class_type.rsplit('\\').next() {
2994 Some(name) if self.is_type_param_in_scope(name) => {
2995 let pos = self.merge(pos, self.get_pos(type_arguments));
2996 let type_arguments = self.slice(
2999 .filter_map(|&node| self.node_to_ty(node)),
3001 let ty_ = Ty_::Tgeneric(self.alloc((name, type_arguments)));
3002 self.hint_ty(pos, ty_)
3005 let class_type = self.elaborate_raw_id(class_type);
3009 self.get_pos(type_arguments),
3017 fn make_alias_declaration(
3019 attributes: Self::R,
3022 generic_params: Self::R,
3023 constraint: Self::R,
3025 aliased_type: Self::R,
3026 _semicolon: Self::R,
3028 if name.is_ignored() {
3029 return Node::Ignored(SK::AliasDeclaration);
3031 let Id(pos, name) = match self.elaborate_defined_id(name) {
3033 None => return Node::Ignored(SK::AliasDeclaration),
3035 let ty = match self.node_to_ty(aliased_type) {
3037 None => return Node::Ignored(SK::AliasDeclaration),
3039 let constraint = match constraint {
3040 Node::TypeConstraint(&(_kind, hint)) => self.node_to_ty(hint),
3044 // Pop the type params stack only after creating all inner types.
3045 let tparams = self.pop_type_params(generic_params);
3046 let parsed_attributes = self.to_attributes(attributes);
3047 let user_attributes = if self.retain_or_omit_user_attributes_for_facts {
3048 self.slice(attributes.iter().rev().filter_map(|attribute| {
3049 if let Node::Attribute(attr) = attribute {
3050 Some(self.user_attribute_to_decl(attr))
3058 let typedef = self.alloc(TypedefType {
3059 module: parsed_attributes.module,
3061 vis: if parsed_attributes.internal {
3062 aast::TypedefVisibility::Tinternal
3064 match keyword.token_kind() {
3065 Some(TokenKind::Type) => aast::TypedefVisibility::Transparent,
3066 Some(TokenKind::Newtype) => aast::TypedefVisibility::Opaque,
3067 _ => aast::TypedefVisibility::Transparent,
3074 attributes: user_attributes,
3077 self.add_typedef(name, typedef);
3079 Node::Ignored(SK::AliasDeclaration)
3082 fn make_context_alias_declaration(
3084 attributes: Self::R,
3087 generic_params: Self::R,
3088 constraint: Self::R,
3091 _semicolon: Self::R,
3093 if name.is_ignored() {
3094 return Node::Ignored(SK::ContextAliasDeclaration);
3096 let Id(pos, name) = match self.elaborate_defined_id(name) {
3098 None => return Node::Ignored(SK::ContextAliasDeclaration),
3100 let ty = match self.node_to_ty(ctx_list) {
3102 None => self.alloc(Ty(
3103 self.alloc(Reason::hint(pos)),
3104 Ty_::Tapply(self.alloc(((pos, "\\HH\\Contexts\\defaults"), &[]))),
3108 // lowerer ensures there is only one as constraint
3109 let mut as_constraint = None;
3110 for c in constraint.iter() {
3111 if let Node::ContextConstraint(&(kind, hint)) = c {
3112 let ty = self.node_to_ty(hint);
3114 ConstraintKind::ConstraintAs => as_constraint = ty,
3119 // Pop the type params stack only after creating all inner types.
3120 let tparams = self.pop_type_params(generic_params);
3121 let parsed_attributes = self.to_attributes(attributes);
3122 let user_attributes = if self.retain_or_omit_user_attributes_for_facts {
3123 self.slice(attributes.iter().rev().filter_map(|attribute| {
3124 if let Node::Attribute(attr) = attribute {
3125 Some(self.user_attribute_to_decl(attr))
3133 let typedef = self.alloc(TypedefType {
3134 module: parsed_attributes.module,
3136 vis: if parsed_attributes.internal {
3137 aast::TypedefVisibility::Tinternal
3139 aast::TypedefVisibility::Opaque
3142 constraint: as_constraint,
3145 attributes: user_attributes,
3148 self.add_typedef(name, typedef);
3150 Node::Ignored(SK::ContextAliasDeclaration)
3153 fn make_type_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3154 let kind = match kind.token_kind() {
3155 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3156 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3157 _ => return Node::Ignored(SK::TypeConstraint),
3159 Node::TypeConstraint(self.alloc((kind, value)))
3162 fn make_context_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3163 let kind = match kind.token_kind() {
3164 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3165 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3166 _ => return Node::Ignored(SK::ContextConstraint),
3168 Node::ContextConstraint(self.alloc((kind, value)))
3171 fn make_type_parameter(
3173 user_attributes: Self::R,
3177 tparam_params: Self::R,
3178 constraints: Self::R,
3180 let user_attributes = match user_attributes {
3181 Node::BracketedList((_, attributes, _)) => {
3182 self.slice(attributes.iter().filter_map(|x| match x {
3183 Node::Attribute(a) => Some(*a),
3190 let constraints = self.slice(constraints.iter().filter_map(|node| match node {
3191 Node::TypeConstraint(&constraint) => Some(constraint),
3195 // TODO(T70068435) Once we add support for constraints on higher-kinded types
3196 // (in particular, constraints on nested type parameters), we need to ensure
3197 // that we correctly handle the scoping of nested type parameters.
3198 // This includes making sure that the call to convert_type_appl_to_generic
3199 // in make_type_parameters handles nested constraints.
3200 // For now, we just make sure that the nested type parameters that make_type_parameters
3201 // added to the global list of in-scope type parameters are removed immediately:
3202 self.pop_type_params(tparam_params);
3204 let tparam_params = match tparam_params {
3205 Node::TypeParameters(¶ms) => params,
3209 Node::TypeParameter(self.alloc(TypeParameterDecl {
3211 variance: match variance.token_kind() {
3212 Some(TokenKind::Minus) => Variance::Contravariant,
3213 Some(TokenKind::Plus) => Variance::Covariant,
3214 _ => Variance::Invariant,
3216 reified: if reify.is_token(TokenKind::Reify) {
3217 if user_attributes.iter().any(|node| node.name.1 == "__Soft") {
3218 aast::ReifyKind::SoftReified
3220 aast::ReifyKind::Reified
3223 aast::ReifyKind::Erased
3231 fn make_type_parameters(&mut self, _lt: Self::R, tparams: Self::R, _gt: Self::R) -> Self::R {
3232 let size = tparams.len();
3233 let mut tparams_with_name = Vec::with_capacity_in(size, self.arena);
3234 let mut tparam_names = MultiSetMut::with_capacity_in(size, self.arena);
3235 for node in tparams.iter() {
3237 Node::TypeParameter(decl) => {
3238 let name = match decl.name.as_id() {
3240 None => return Node::Ignored(SK::TypeParameters),
3242 tparam_names.insert(name.1);
3243 tparams_with_name.push((decl, name));
3248 Rc::make_mut(&mut self.type_parameters).push(tparam_names.into());
3249 let mut tparams = Vec::with_capacity_in(tparams_with_name.len(), self.arena);
3250 for (decl, name) in tparams_with_name.into_iter() {
3251 let &TypeParameterDecl {
3259 let constraints = self.slice(constraints.iter().filter_map(|constraint| {
3260 let &(kind, ty) = constraint;
3261 let ty = self.node_to_ty(ty)?;
3262 let ty = self.convert_tapply_to_tgeneric(ty);
3266 let user_attributes = self.slice(
3270 .map(|x| self.user_attribute_to_decl(x)),
3272 tparams.push(self.alloc(Tparam {
3278 tparams: tparam_params,
3281 Node::TypeParameters(self.alloc(tparams.into_bump_slice()))
3284 fn make_parameter_declaration(
3286 attributes: Self::R,
3287 visibility: Self::R,
3292 initializer: Self::R,
3294 let (variadic, pos, name) = match name {
3295 Node::ListItem(&(ellipsis, id)) => {
3296 let Id(pos, name) = match id.as_variable() {
3298 None => return Node::Ignored(SK::ParameterDeclaration),
3300 let variadic = ellipsis.is_token(TokenKind::DotDotDot);
3301 (variadic, pos, Some(name))
3304 let Id(pos, name) = match name.as_variable() {
3306 None => return Node::Ignored(SK::ParameterDeclaration),
3308 (false, pos, Some(name))
3311 let kind = if inout.is_token(TokenKind::Inout) {
3316 let is_readonly = readonly.is_token(TokenKind::Readonly);
3317 let hint = if self.opts.interpret_soft_types_as_like_types {
3318 let attributes = self.to_attributes(attributes);
3319 if attributes.soft {
3321 Node::Ty(ty) => self.hint_ty(self.get_pos(hint), Ty_::Tlike(ty)),
3330 Node::FunParam(self.alloc(FunParamDecl {
3334 readonly: is_readonly,
3343 fn make_variadic_parameter(&mut self, _: Self::R, hint: Self::R, ellipsis: Self::R) -> Self::R {
3345 self.alloc(FunParamDecl {
3346 attributes: Node::Ignored(SK::Missing),
3347 visibility: Node::Ignored(SK::Missing),
3348 kind: ParamMode::FPnormal,
3353 .unwrap_or_else(|| self.get_pos(ellipsis)),
3356 initializer: Node::Ignored(SK::Missing),
3361 fn make_function_declaration(
3363 attributes: Self::R,
3367 let parsed_attributes = self.to_attributes(attributes);
3369 Node::FunctionHeader(header) => {
3370 let is_method = false;
3371 let ((pos, name), type_, _) =
3372 match self.function_to_ty(is_method, attributes, header, body) {
3374 None => return Node::Ignored(SK::FunctionDeclaration),
3376 let deprecated = parsed_attributes.deprecated.map(|msg| {
3377 let mut s = String::new_in(self.arena);
3378 s.push_str("The function ");
3379 s.push_str(name.trim_start_matches('\\'));
3380 s.push_str(" is deprecated: ");
3384 let fun_elt = self.alloc(FunElt {
3385 module: parsed_attributes.module,
3386 internal: parsed_attributes.internal,
3390 php_std_lib: parsed_attributes.php_std_lib,
3391 support_dynamic_type: self.opts.everything_sdt
3392 || parsed_attributes.support_dynamic_type,
3394 self.add_fun(name, fun_elt);
3395 Node::Ignored(SK::FunctionDeclaration)
3397 _ => Node::Ignored(SK::FunctionDeclaration),
3403 left_bracket: Self::R,
3405 right_bracket: Self::R,
3407 let tys = self.slice(tys.iter().filter_map(|ty| match ty {
3408 Node::ListItem(&(ty, _)) | &ty => {
3409 // A wildcard is used for the context of a closure type on a
3410 // parameter of a function with a function context (e.g.,
3411 // `function f((function ()[_]: void) $f)[ctx $f]: void {}`).
3412 if let Some(Id(pos, "_")) = self.expect_name(ty) {
3413 return Some(self.alloc(Ty(
3414 self.alloc(Reason::hint(pos)),
3415 Ty_::Tapply(self.alloc(((pos, "_"), &[]))),
3418 let ty = self.node_to_ty(ty)?;
3420 // Only three forms of type can appear here in a valid program:
3421 // - function contexts (`ctx $f`)
3422 // - value-dependent paths (`$v::C`)
3423 // - built-in contexts (`rx`, `cipp_of<EntFoo>`)
3424 // The first and last will be represented with `Tapply`,
3425 // but function contexts will use a variable name
3426 // (containing a `$`). Built-in contexts are always in the
3427 // \HH\Contexts namespace, so we rewrite those names here.
3428 Ty_::Tapply(&((pos, name), targs)) if !name.starts_with('$') => {
3429 // The name will have been elaborated in the current
3430 // namespace, but we actually want it to be in the
3431 // \HH\Contexts namespace. Grab the last component of
3432 // the name, and rewrite it in the correct namespace.
3433 // Note that this makes it impossible to express names
3434 // in any sub-namespace of \HH\Contexts (e.g.,
3435 // "Unsafe\\cipp" will be rewritten as
3436 // "\\HH\\Contexts\\cipp" rather than
3437 // "\\HH\\Contexts\\Unsafe\\cipp").
3438 let name = match name.trim_end_matches('\\').split('\\').next_back() {
3439 Some(ctxname) => match ctxname.chars().next() {
3440 Some(first_char) if first_char.is_lowercase() => {
3441 self.concat("\\HH\\Contexts\\", ctxname)
3443 Some(_) | None => name,
3447 Some(self.alloc(Ty(ty.0, Ty_::Tapply(self.alloc(((pos, name), targs))))))
3453 /* Like in as_fun_implicit_params, we keep the intersection as is: we do not simplify
3454 * empty or singleton intersections.
3456 let pos = self.merge_positions(left_bracket, right_bracket);
3457 self.hint_ty(pos, Ty_::Tintersection(tys))
3460 fn make_function_ctx_type_specifier(
3462 ctx_keyword: Self::R,
3465 match variable.as_variable() {
3466 Some(Id(pos, name)) => {
3467 Node::Variable(self.alloc((name, self.merge(pos, self.get_pos(ctx_keyword)))))
3469 None => Node::Ignored(SK::FunctionCtxTypeSpecifier),
3473 fn make_function_declaration_header(
3478 type_params: Self::R,
3479 left_paren: Self::R,
3480 param_list: Self::R,
3481 _right_paren: Self::R,
3482 capability: Self::R,
3484 readonly_return: Self::R,
3486 where_constraints: Self::R,
3488 // Use the position of the left paren if the name is missing.
3489 // Keep the name if it's an IgnoredToken rather than an Ignored. An
3490 // IgnoredToken here should always be an error, but it's better to treat
3491 // a keyword as a name than to claim the function has no name at all.
3492 let name = if matches!(name, Node::Ignored(..)) {
3497 Node::FunctionHeader(self.alloc(FunctionHeader {
3509 fn make_yield_expression(&mut self, keyword: Self::R, _operand: Self::R) -> Self::R {
3510 assert!(keyword.token_kind() == Some(TokenKind::Yield));
3514 fn make_const_declaration(
3516 _attributes: Self::R,
3518 const_keyword: Self::R,
3527 .classish_name_builder
3528 .get_current_classish_name()
3531 let ty = self.node_to_ty(hint);
3533 self.alloc(self.slice(consts.iter().filter_map(|cst| match cst {
3534 Node::ConstInitializer(&(name, initializer, refs)) => {
3535 let id = name.as_id()?;
3536 let modifiers = read_member_modifiers(modifiers.iter());
3537 let abstract_ = if modifiers.is_abstract {
3538 ClassConstKind::CCAbstract(!initializer.is_ignored())
3540 ClassConstKind::CCConcrete
3543 .or_else(|| self.infer_const(name, initializer))
3545 Some(Node::Const(self.alloc(
3546 shallow_decl_defs::ShallowClassConst {
3559 Node::List(consts) => {
3560 // This case always returns Node::Ignored,
3561 // but has the side effect of calling self.add_const
3563 // Note: given "const int X=1,Y=2;", the legacy decl-parser
3564 // allows both decls, and it gives them both an identical text-span -
3565 // from start of "const" to end of semicolon. This is a bug but
3566 // the code here preserves it.
3567 let pos = self.merge_positions(const_keyword, semicolon);
3568 for cst in consts.iter() {
3570 Node::ConstInitializer(&(name, initializer, _refs)) => {
3571 if let Some(Id(id_pos, id)) = self.elaborate_defined_id(name) {
3574 .or_else(|| self.infer_const(name, initializer))
3575 .unwrap_or_else(|| self.tany_with_pos(id_pos));
3576 self.add_const(id, self.alloc(ConstDecl { pos, type_: ty }));
3582 Node::Ignored(SK::ConstDeclaration)
3584 _ => Node::Ignored(SK::ConstDeclaration),
3588 fn begin_constant_declarator(&mut self) {
3589 self.start_accumulating_const_refs();
3592 fn make_constant_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
3593 // The "X=1" part of either a member const "class C {const int X=1;}" or a top-level const "const int X=1;"
3594 // Note: the the declarator itself doesn't yet know whether a type was provided by the user;
3595 // that's only known in the parent, make_const_declaration
3596 let refs = self.stop_accumulating_const_refs();
3597 if name.is_ignored() {
3598 Node::Ignored(SK::ConstantDeclarator)
3600 Node::ConstInitializer(self.alloc((name, initializer, refs)))
3604 fn make_namespace_declaration(&mut self, _name: Self::R, body: Self::R) -> Self::R {
3605 if let Node::Ignored(SK::NamespaceBody) = body {
3606 Rc::make_mut(&mut self.namespace_builder).pop_namespace();
3608 Node::Ignored(SK::NamespaceDeclaration)
3611 fn make_namespace_declaration_header(&mut self, _keyword: Self::R, name: Self::R) -> Self::R {
3612 let name = self.expect_name(name).map(|Id(_, name)| name);
3613 // if this is header of semicolon-style (one with NamespaceEmptyBody) namespace, we should pop
3614 // the previous namespace first, but we don't have the body yet. We'll fix it retroactively in
3615 // make_namespace_empty_body
3616 Rc::make_mut(&mut self.namespace_builder).push_namespace(name);
3617 Node::Ignored(SK::NamespaceDeclarationHeader)
3620 fn make_namespace_body(
3622 _left_brace: Self::R,
3623 _declarations: Self::R,
3624 _right_brace: Self::R,
3626 Node::Ignored(SK::NamespaceBody)
3629 fn make_namespace_empty_body(&mut self, _semicolon: Self::R) -> Self::R {
3630 Rc::make_mut(&mut self.namespace_builder).pop_previous_namespace();
3631 Node::Ignored(SK::NamespaceEmptyBody)
3634 fn make_namespace_use_declaration(
3637 namespace_use_kind: Self::R,
3639 _semicolon: Self::R,
3641 if let Some(import_kind) = Self::namespace_use_kind(&namespace_use_kind) {
3642 for clause in clauses.iter() {
3643 if let Node::NamespaceUseClause(nuc) = clause {
3644 Rc::make_mut(&mut self.namespace_builder).add_import(
3652 Node::Ignored(SK::NamespaceUseDeclaration)
3655 fn make_namespace_group_use_declaration(
3660 _left_brace: Self::R,
3662 _right_brace: Self::R,
3663 _semicolon: Self::R,
3665 let Id(_, prefix) = match self.expect_name(prefix) {
3667 None => return Node::Ignored(SK::NamespaceGroupUseDeclaration),
3669 for clause in clauses.iter() {
3670 if let Node::NamespaceUseClause(nuc) = clause {
3671 let mut id = String::new_in(self.arena);
3672 id.push_str(prefix);
3673 id.push_str(nuc.id.1);
3674 Rc::make_mut(&mut self.namespace_builder).add_import(
3681 Node::Ignored(SK::NamespaceGroupUseDeclaration)
3684 fn make_namespace_use_clause(
3686 clause_kind: Self::R,
3689 aliased_name: Self::R,
3691 let id = match self.expect_name(name) {
3693 None => return Node::Ignored(SK::NamespaceUseClause),
3695 let as_ = if as_.is_token(TokenKind::As) {
3696 match aliased_name.as_id() {
3697 Some(name) => Some(name.1),
3698 None => return Node::Ignored(SK::NamespaceUseClause),
3703 if let Some(kind) = Self::namespace_use_kind(&clause_kind) {
3704 Node::NamespaceUseClause(self.alloc(NamespaceUseClause { kind, id, as_ }))
3706 Node::Ignored(SK::NamespaceUseClause)
3710 fn make_where_clause(&mut self, _: Self::R, where_constraints: Self::R) -> Self::R {
3714 fn make_where_constraint(
3718 right_type: Self::R,
3720 Node::WhereConstraint(self.alloc(WhereConstraint(
3721 self.node_to_ty(left_type).unwrap_or(TANY),
3722 match operator.token_kind() {
3723 Some(TokenKind::Equal) => ConstraintKind::ConstraintEq,
3724 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3725 _ => ConstraintKind::ConstraintAs,
3727 self.node_to_ty(right_type).unwrap_or(TANY),
3731 fn make_classish_declaration(
3733 attributes: Self::R,
3735 xhp_keyword: Self::R,
3736 class_keyword: Self::R,
3739 _extends_keyword: Self::R,
3741 _implements_keyword: Self::R,
3742 implements: Self::R,
3743 where_clause: Self::R,
3746 let raw_name = match self.expect_name(name) {
3747 Some(Id(_, name)) => name,
3748 None => return Node::Ignored(SK::ClassishDeclaration),
3750 let Id(pos, name) = match self.elaborate_defined_id(name) {
3752 None => return Node::Ignored(SK::ClassishDeclaration),
3754 let is_xhp = raw_name.starts_with(':') || xhp_keyword.is_present();
3756 let mut final_ = false;
3757 let mut abstract_ = false;
3759 for modifier in modifiers.iter() {
3760 match modifier.token_kind() {
3761 Some(TokenKind::Abstract) => {
3764 Some(TokenKind::Final) => final_ = true,
3768 let class_kind = match class_keyword.token_kind() {
3769 Some(TokenKind::Interface) => ClassishKind::Cinterface,
3770 Some(TokenKind::Trait) => ClassishKind::Ctrait,
3771 _ => ClassishKind::Cclass(if abstract_ {
3772 Abstraction::Abstract
3774 Abstraction::Concrete
3778 let where_constraints = self.slice(where_clause.iter().filter_map(|&x| match x {
3779 Node::WhereConstraint(x) => Some(x),
3783 let body = match body {
3784 Node::ClassishBody(body) => body,
3785 _ => return Node::Ignored(SK::ClassishDeclaration),
3788 let mut uses_len = 0;
3789 let mut xhp_attr_uses_len = 0;
3790 let mut xhp_enum_values = SMap::empty();
3791 let mut req_extends_len = 0;
3792 let mut req_implements_len = 0;
3793 let mut req_class_len = 0;
3794 let mut consts_len = 0;
3795 let mut typeconsts_len = 0;
3796 let mut props_len = 0;
3797 let mut sprops_len = 0;
3798 let mut static_methods_len = 0;
3799 let mut methods_len = 0;
3801 let mut user_attributes_len = 0;
3802 for attribute in attributes.iter() {
3804 Node::Attribute(..) => user_attributes_len += 1,
3809 for element in body.iter().copied() {
3811 Node::TraitUse(names) => uses_len += names.len(),
3812 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
3814 xhp_attr_uses_decls,
3815 xhp_attr_enum_values,
3817 props_len += xhp_attr_decls.len();
3818 xhp_attr_uses_len += xhp_attr_uses_decls.len();
3820 for (name, values) in xhp_attr_enum_values {
3821 xhp_enum_values = xhp_enum_values.add(self.arena, name, *values);
3824 Node::TypeConstant(..) => typeconsts_len += 1,
3825 Node::RequireClause(require) => match require.require_type.token_kind() {
3826 Some(TokenKind::Extends) => req_extends_len += 1,
3827 Some(TokenKind::Implements) => req_implements_len += 1,
3828 Some(TokenKind::Class) => req_class_len += 1,
3831 Node::List(consts @ [Node::Const(..), ..]) => consts_len += consts.len(),
3832 Node::Property(&PropertyNode { decls, is_static }) => {
3834 sprops_len += decls.len()
3836 props_len += decls.len()
3839 Node::Constructor(&ConstructorNode { properties, .. }) => {
3840 props_len += properties.len()
3842 Node::Method(&MethodNode { is_static, .. }) => {
3844 static_methods_len += 1
3853 let mut constructor = None;
3855 let mut uses = Vec::with_capacity_in(uses_len, self.arena);
3856 let mut xhp_attr_uses = Vec::with_capacity_in(xhp_attr_uses_len, self.arena);
3857 let mut req_extends = Vec::with_capacity_in(req_extends_len, self.arena);
3858 let mut req_implements = Vec::with_capacity_in(req_implements_len, self.arena);
3859 let mut req_class = Vec::with_capacity_in(req_class_len, self.arena);
3860 let mut consts = Vec::with_capacity_in(consts_len, self.arena);
3861 let mut typeconsts = Vec::with_capacity_in(typeconsts_len, self.arena);
3862 let mut props = Vec::with_capacity_in(props_len, self.arena);
3863 let mut sprops = Vec::with_capacity_in(sprops_len, self.arena);
3864 let mut static_methods = Vec::with_capacity_in(static_methods_len, self.arena);
3865 let mut methods = Vec::with_capacity_in(methods_len, self.arena);
3867 let mut user_attributes = Vec::with_capacity_in(user_attributes_len, self.arena);
3868 for attribute in attributes.iter() {
3870 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
3874 // Match ordering of attributes produced by the OCaml decl parser (even
3875 // though it's the reverse of the syntactic ordering).
3876 user_attributes.reverse();
3878 let class_attributes = self.to_attributes(attributes);
3880 // xhp props go after regular props, regardless of their order in file
3881 let mut xhp_props = vec![];
3883 for element in body.iter().copied() {
3885 Node::TraitUse(names) => {
3886 uses.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
3888 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
3890 xhp_attr_uses_decls,
3893 xhp_props.extend(xhp_attr_decls);
3894 xhp_attr_uses.extend(
3897 .filter_map(|&node| self.node_to_ty(node)),
3900 Node::TypeConstant(constant) => typeconsts.push(constant),
3901 Node::RequireClause(require) => match require.require_type.token_kind() {
3902 Some(TokenKind::Extends) => {
3903 req_extends.extend(self.node_to_ty(require.name).iter())
3905 Some(TokenKind::Implements) => {
3906 req_implements.extend(self.node_to_ty(require.name).iter())
3908 Some(TokenKind::Class) => {
3909 req_class.extend(self.node_to_ty(require.name).iter())
3913 Node::List(&const_nodes @ [Node::Const(..), ..]) => {
3914 for node in const_nodes {
3915 if let Node::Const(decl) = *node {
3920 Node::Property(&PropertyNode { decls, is_static }) => {
3921 for property in decls {
3923 sprops.push(property)
3925 props.push(property)
3929 Node::Constructor(&ConstructorNode { method, properties }) => {
3930 constructor = Some(method);
3931 for property in properties {
3932 props.push(property)
3935 Node::Method(&MethodNode { method, is_static }) => {
3936 // Annoyingly, the <<__SupportDynamicType>> annotation on a
3937 // class implicitly changes the decls of every method inside
3938 // it, so we have to reallocate them here.
3939 let method = if class_attributes.support_dynamic_type
3940 && !method.flags.contains(MethodFlags::SUPPORT_DYNAMIC_TYPE)
3942 let type_ = match method.type_.1 {
3944 let flags = ft.flags | FunTypeFlags::SUPPORT_DYNAMIC_TYPE;
3945 let ft = self.alloc(FunType { flags, ..*ft });
3946 self.alloc(Ty(method.type_.0, Ty_::Tfun(ft)))
3950 let flags = method.flags | MethodFlags::SUPPORT_DYNAMIC_TYPE;
3951 self.alloc(ShallowMethod {
3960 static_methods.push(method);
3962 methods.push(method);
3965 _ => {} // It's not our job to report errors here.
3969 props.extend(xhp_props.into_iter());
3971 if class_attributes.const_ {
3972 for prop in props.iter_mut() {
3973 if !prop.flags.contains(PropFlags::CONST) {
3974 *prop = self.alloc(ShallowProp {
3975 flags: prop.flags | PropFlags::CONST,
3982 let uses = uses.into_bump_slice();
3983 let xhp_attr_uses = xhp_attr_uses.into_bump_slice();
3984 let req_extends = req_extends.into_bump_slice();
3985 let req_implements = req_implements.into_bump_slice();
3986 let req_class = req_class.into_bump_slice();
3987 let consts = consts.into_bump_slice();
3988 let typeconsts = typeconsts.into_bump_slice();
3989 let props = props.into_bump_slice();
3990 let sprops = sprops.into_bump_slice();
3991 let static_methods = static_methods.into_bump_slice();
3992 let methods = methods.into_bump_slice();
3993 let user_attributes = user_attributes.into_bump_slice();
3994 let extends = self.slice(extends.iter().filter_map(|&node| self.node_to_ty(node)));
3995 let implements = self.slice(implements.iter().filter_map(|&node| self.node_to_ty(node)));
3996 let support_dynamic_type =
3997 self.opts.everything_sdt || class_attributes.support_dynamic_type;
3998 // Pop the type params stack only after creating all inner types.
3999 let tparams = self.pop_type_params(tparams);
4000 let module = class_attributes.module;
4002 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4003 mode: self.file_mode,
4007 has_xhp_keyword: xhp_keyword.is_token(TokenKind::XHP),
4021 support_dynamic_type,
4032 self.add_class(name, cls);
4034 self.classish_name_builder.parsed_classish_declaration();
4036 Node::Ignored(SK::ClassishDeclaration)
4039 fn make_property_declaration(
4044 declarators: Self::R,
4045 _semicolon: Self::R,
4047 let (attrs, modifiers, hint) = (attrs, modifiers, hint);
4048 let modifiers = read_member_modifiers(modifiers.iter());
4049 let declarators = self.slice(declarators.iter().filter_map(
4050 |declarator| match declarator {
4051 Node::ListItem(&(name, initializer)) => {
4052 let attributes = self.to_attributes(attrs);
4053 let Id(pos, name) = name.as_variable()?;
4054 let name = if modifiers.is_static {
4057 strip_dollar_prefix(name)
4059 let ty = self.rewrite_ty_for_global_inference(
4060 self.node_to_non_ret_ty(hint),
4061 Reason::RglobalClassProp(pos),
4063 let ty = if self.opts.interpret_soft_types_as_like_types {
4064 if attributes.soft {
4067 self.alloc(Reason::hint(self.get_pos(hint))),
4077 let needs_init = if self.file_mode == Mode::Mhhi {
4080 initializer.is_ignored()
4082 let mut flags = PropFlags::empty();
4083 flags.set(PropFlags::CONST, attributes.const_);
4084 flags.set(PropFlags::LATEINIT, attributes.late_init);
4085 flags.set(PropFlags::LSB, attributes.lsb);
4086 flags.set(PropFlags::NEEDS_INIT, needs_init);
4087 flags.set(PropFlags::ABSTRACT, modifiers.is_abstract);
4088 flags.set(PropFlags::READONLY, modifiers.is_readonly);
4089 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
4094 visibility: if attributes.internal
4095 && modifiers.visibility == aast::Visibility::Public
4097 aast::Visibility::Internal
4099 modifiers.visibility
4107 Node::Property(self.alloc(PropertyNode {
4109 is_static: modifiers.is_static,
4113 fn make_xhp_class_attribute_declaration(
4116 attributes: Self::R,
4117 _semicolon: Self::R,
4119 let mut xhp_attr_enum_values = Vec::new_in(self.arena);
4121 let xhp_attr_decls = self.slice(attributes.iter().filter_map(|node| {
4122 let node = match node {
4123 Node::XhpClassAttribute(x) => x,
4126 let Id(pos, name) = node.name;
4127 let name = prefix_colon(self.arena, name);
4129 let (like, type_, enum_values) = match node.hint {
4130 Node::XhpEnumTy((like, ty, values)) => (*like, Some(*ty), Some(values)),
4131 _ => (None, self.node_to_ty(node.hint), None),
4133 if let Some(enum_values) = enum_values {
4134 xhp_attr_enum_values.push((name, *enum_values));
4137 let type_ = if node.nullable && node.tag.is_none() {
4138 type_.and_then(|x| match x {
4140 Ty(_, Ty_::Toption(_)) | Ty(_, Ty_::Tmixed) => type_,
4142 _ => self.node_to_ty(self.hint_ty(x.get_pos()?, Ty_::Toption(x))),
4147 let type_ = type_.map(|t| match like {
4148 Some(p) => self.alloc(Ty(self.alloc(Reason::hint(p)), Ty_::Tlike(t))),
4152 let mut flags = PropFlags::empty();
4153 flags.set(PropFlags::NEEDS_INIT, node.needs_init);
4156 visibility: aast::Visibility::Public,
4158 xhp_attr: Some(xhp_attribute::XhpAttribute {
4160 has_default: !node.needs_init,
4166 let xhp_attr_uses_decls = self.slice(attributes.iter().filter_map(|x| match x {
4167 Node::XhpAttributeUse(&name) => Some(name),
4171 Node::XhpClassAttributeDeclaration(self.alloc(XhpClassAttributeDeclarationNode {
4172 xhp_attr_enum_values: xhp_attr_enum_values.into_bump_slice(),
4174 xhp_attr_uses_decls,
4178 /// Handle XHP attribute enum declarations.
4180 /// class :foo implements XHPChild {
4182 /// enum {'big', 'small'} size; // this line
4184 fn make_xhp_enum_type(
4187 enum_keyword: Self::R,
4188 _left_brace: Self::R,
4189 xhp_enum_values: Self::R,
4190 right_brace: Self::R,
4192 // Infer the type hint from the first value.
4193 // TODO: T88207956 consider all the values.
4194 let ty = xhp_enum_values
4197 .and_then(|node| self.node_to_ty(*node))
4199 let pos = self.merge_positions(enum_keyword, right_brace);
4200 let ty_ = node_ty.1;
4201 self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_))
4203 let mut values = Vec::new_in(self.arena);
4204 for node in xhp_enum_values.iter() {
4205 // XHP enum values may only be string or int literals.
4207 Node::IntLiteral(&(s, _)) => {
4208 let i = s.parse::<isize>().unwrap_or(0);
4209 values.push(XhpEnumValue::XEVInt(i));
4211 Node::StringLiteral(&(s, _)) => {
4212 let owned_str = std::string::String::from_utf8_lossy(s);
4213 values.push(XhpEnumValue::XEVString(self.arena.alloc_str(&owned_str)));
4221 Node::XhpEnumTy(self.alloc((self.get_pos_opt(like), ty, values.into_bump_slice())))
4223 None => Node::Ignored(SK::XHPEnumType),
4227 fn make_xhp_class_attribute(
4231 initializer: Self::R,
4234 let name = match name.as_id() {
4236 None => return Node::Ignored(SK::XHPClassAttribute),
4238 Node::XhpClassAttribute(self.alloc(XhpClassAttributeNode {
4241 needs_init: !initializer.is_present(),
4242 tag: match tag.token_kind() {
4243 Some(TokenKind::Required) => Some(xhp_attribute::Tag::Required),
4244 Some(TokenKind::Lateinit) => Some(xhp_attribute::Tag::LateInit),
4247 nullable: initializer.is_token(TokenKind::NullLiteral) || !initializer.is_present(),
4251 fn make_xhp_simple_class_attribute(&mut self, name: Self::R) -> Self::R {
4252 Node::XhpAttributeUse(self.alloc(name))
4255 fn make_property_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
4256 Node::ListItem(self.alloc((name, initializer)))
4259 fn make_methodish_declaration(
4266 let header = match header {
4267 Node::FunctionHeader(header) => header,
4268 _ => return Node::Ignored(SK::MethodishDeclaration),
4270 // If we don't have a body, use the closing token. A closing token of
4271 // '}' indicates a regular function, while a closing token of ';'
4272 // indicates an abstract function.
4273 let body = if body.is_ignored() { closer } else { body };
4274 let modifiers = read_member_modifiers(header.modifiers.iter());
4275 let is_constructor = header.name.is_token(TokenKind::Construct);
4276 let is_method = true;
4277 let (id, ty, properties) = match self.function_to_ty(is_method, attrs, header, body) {
4278 Some(tuple) => tuple,
4279 None => return Node::Ignored(SK::MethodishDeclaration),
4281 let attributes = self.to_attributes(attrs);
4282 let deprecated = attributes.deprecated.map(|msg| {
4283 let mut s = String::new_in(self.arena);
4284 s.push_str("The method ");
4286 s.push_str(" is deprecated: ");
4290 let mut flags = MethodFlags::empty();
4292 MethodFlags::ABSTRACT,
4293 self.classish_name_builder.in_interface() || modifiers.is_abstract,
4295 flags.set(MethodFlags::FINAL, modifiers.is_final);
4296 flags.set(MethodFlags::OVERRIDE, attributes.override_);
4298 MethodFlags::DYNAMICALLYCALLABLE,
4299 attributes.dynamically_callable,
4301 flags.set(MethodFlags::PHP_STD_LIB, attributes.php_std_lib);
4303 MethodFlags::SUPPORT_DYNAMIC_TYPE,
4304 !is_constructor && attributes.support_dynamic_type,
4306 let visibility = match modifiers.visibility {
4307 aast::Visibility::Public => {
4308 if attributes.internal {
4309 aast::Visibility::Internal
4311 aast::Visibility::Public
4314 _ => modifiers.visibility,
4317 let mut user_attributes = Vec::new_in(self.arena);
4318 if self.retain_or_omit_user_attributes_for_facts {
4319 for attribute in attrs.iter() {
4321 Node::Attribute(attr) => {
4322 user_attributes.push(self.user_attribute_to_decl(attr))
4327 // Match ordering of attributes produced by the OCaml decl parser (even
4328 // though it's the reverse of the syntactic ordering).
4329 user_attributes.reverse();
4331 let user_attributes = user_attributes.into_bump_slice();
4333 let method = self.alloc(ShallowMethod {
4339 attributes: user_attributes,
4342 Node::Constructor(self.alloc(ConstructorNode { method, properties }))
4344 Node::Method(self.alloc(MethodNode {
4346 is_static: modifiers.is_static,
4351 fn make_classish_body(
4353 _left_brace: Self::R,
4355 _right_brace: Self::R,
4357 Node::ClassishBody(self.alloc(elements.as_slice(self.arena)))
4360 fn make_enum_declaration(
4362 attributes: Self::R,
4367 constraint: Self::R,
4368 _left_brace: Self::R,
4369 use_clauses: Self::R,
4370 enumerators: Self::R,
4371 _right_brace: Self::R,
4373 let id = match self.elaborate_defined_id(name) {
4375 None => return Node::Ignored(SK::EnumDeclaration),
4377 let hint = match self.node_to_ty(extends) {
4379 None => return Node::Ignored(SK::EnumDeclaration),
4381 let extends = match self.node_to_ty(self.make_apply(
4382 (self.get_pos(name), "\\HH\\BuiltinEnum"),
4387 None => return Node::Ignored(SK::EnumDeclaration),
4390 let consts = self.slice(enumerators.iter().filter_map(|node| match *node {
4391 Node::Const(const_) => Some(const_),
4394 let mut user_attributes = Vec::with_capacity_in(attributes.len(), self.arena);
4395 for attribute in attributes.iter() {
4397 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4401 // Match ordering of attributes produced by the OCaml decl parser (even
4402 // though it's the reverse of the syntactic ordering).
4403 user_attributes.reverse();
4404 let user_attributes = user_attributes.into_bump_slice();
4406 let constraint = match constraint {
4407 Node::TypeConstraint(&(_kind, ty)) => self.node_to_ty(ty),
4411 let mut includes_len = 0;
4412 for element in use_clauses.iter() {
4414 Node::EnumUse(names) => includes_len += names.len(),
4418 let mut includes = Vec::with_capacity_in(includes_len, self.arena);
4419 for element in use_clauses.iter() {
4421 Node::EnumUse(names) => {
4422 includes.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
4427 let includes = includes.into_bump_slice();
4429 let parsed_attributes = self.to_attributes(attributes);
4431 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4432 mode: self.file_mode,
4436 has_xhp_keyword: false,
4437 kind: ClassishKind::Cenum,
4438 module: parsed_attributes.module,
4441 where_constraints: &[],
4442 extends: bumpalo::vec![in self.arena; extends].into_bump_slice(),
4445 xhp_enum_values: SMap::empty(),
4447 req_implements: &[],
4450 support_dynamic_type: parsed_attributes.support_dynamic_type,
4456 static_methods: &[],
4459 enum_type: Some(self.alloc(EnumType {
4465 self.add_class(key, cls);
4467 self.classish_name_builder.parsed_classish_declaration();
4469 Node::Ignored(SK::EnumDeclaration)
4472 fn make_enum_use(&mut self, _keyword: Self::R, names: Self::R, _semicolon: Self::R) -> Self::R {
4473 Node::EnumUse(self.alloc(names))
4476 fn begin_enumerator(&mut self) {
4477 self.start_accumulating_const_refs();
4485 _semicolon: Self::R,
4487 let refs = self.stop_accumulating_const_refs();
4488 let id = match self.expect_name(name) {
4490 None => return Node::Ignored(SyntaxKind::Enumerator),
4494 self.alloc(ShallowClassConst {
4495 abstract_: ClassConstKind::CCConcrete,
4498 .infer_const(name, value)
4499 .unwrap_or_else(|| self.tany_with_pos(id.0)),
4505 fn make_enum_class_declaration(
4507 attributes: Self::R,
4509 _enum_keyword: Self::R,
4510 _class_keyword: Self::R,
4514 _extends_keyword: Self::R,
4515 extends_list: Self::R,
4516 _left_brace: Self::R,
4518 _right_brace: Self::R,
4520 let name = match self.elaborate_defined_id(name) {
4522 None => return Node::Ignored(SyntaxKind::EnumClassDeclaration),
4526 .unwrap_or_else(|| self.tany_with_pos(name.0));
4528 let mut is_abstract = false;
4529 let mut is_final = false;
4530 for modifier in modifiers.iter() {
4531 match modifier.token_kind() {
4532 Some(TokenKind::Abstract) => is_abstract = true,
4533 Some(TokenKind::Final) => is_final = true,
4538 let class_kind = if is_abstract {
4539 ClassishKind::CenumClass(Abstraction::Abstract)
4541 ClassishKind::CenumClass(Abstraction::Concrete)
4544 let builtin_enum_class_ty = {
4546 let enum_class_ty_ = Ty_::Tapply(self.alloc((name.into(), &[])));
4547 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4548 let elt_ty_ = Ty_::Tapply(self.alloc((
4549 (pos, "\\HH\\MemberOf"),
4550 bumpalo::vec![in self.arena; enum_class_ty, base].into_bump_slice(),
4552 let elt_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), elt_ty_));
4553 let builtin_enum_ty_ = if is_abstract {
4554 Ty_::Tapply(self.alloc(((pos, "\\HH\\BuiltinAbstractEnumClass"), &[])))
4556 Ty_::Tapply(self.alloc((
4557 (pos, "\\HH\\BuiltinEnumClass"),
4558 std::slice::from_ref(self.alloc(elt_ty)),
4561 self.alloc(Ty(self.alloc(Reason::hint(pos)), builtin_enum_ty_))
4564 let consts = self.slice(elements.iter().filter_map(|node| match *node {
4565 Node::Const(const_) => Some(const_),
4569 let mut extends = Vec::with_capacity_in(extends_list.len() + 1, self.arena);
4570 extends.push(builtin_enum_class_ty);
4571 extends.extend(extends_list.iter().filter_map(|&n| self.node_to_ty(n)));
4572 let extends = extends.into_bump_slice();
4573 let includes = &extends[1..];
4575 let mut user_attributes = Vec::with_capacity_in(attributes.len() + 1, self.arena);
4576 for attribute in attributes.iter() {
4578 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4582 user_attributes.push(self.alloc(shallow_decl_defs::UserAttribute {
4583 name: (name.0, "__EnumClass"),
4584 classname_params: &[],
4586 // Match ordering of attributes produced by the OCaml decl parser (even
4587 // though it's the reverse of the syntactic ordering).
4588 user_attributes.reverse();
4589 let user_attributes = user_attributes.into_bump_slice();
4591 let parsed_attributes = self.to_attributes(attributes);
4593 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4594 mode: self.file_mode,
4596 abstract_: is_abstract,
4598 has_xhp_keyword: false,
4600 module: None, // TODO: grab module from attributes
4603 where_constraints: &[],
4607 xhp_enum_values: SMap::empty(),
4609 req_implements: &[],
4612 support_dynamic_type: parsed_attributes.support_dynamic_type,
4618 static_methods: &[],
4621 enum_type: Some(self.alloc(EnumType {
4627 self.add_class(name.1, cls);
4629 self.classish_name_builder.parsed_classish_declaration();
4631 Node::Ignored(SyntaxKind::EnumClassDeclaration)
4634 fn begin_enum_class_enumerator(&mut self) {
4635 self.start_accumulating_const_refs();
4638 fn make_enum_class_enumerator(
4643 _initializer: Self::R,
4644 _semicolon: Self::R,
4646 let refs = self.stop_accumulating_const_refs();
4647 let name = match self.expect_name(name) {
4649 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4652 let has_abstract_keyword = modifiers
4654 .any(|node| node.is_token(TokenKind::Abstract));
4655 let abstract_ = if has_abstract_keyword {
4656 /* default values not allowed atm */
4657 ClassConstKind::CCAbstract(false)
4659 ClassConstKind::CCConcrete
4663 .unwrap_or_else(|| self.tany_with_pos(name.0));
4664 let class_name = match self.classish_name_builder.get_current_classish_name() {
4666 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4668 let enum_class_ty_ = Ty_::Tapply(self.alloc(((pos, class_name.0), &[])));
4669 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4670 let type_ = Ty_::Tapply(self.alloc((
4671 (pos, "\\HH\\MemberOf"),
4672 bumpalo::vec![in self.arena; enum_class_ty, type_].into_bump_slice(),
4674 let type_ = self.alloc(Ty(self.alloc(Reason::hint(pos)), type_));
4675 Node::Const(self.alloc(ShallowClassConst {
4683 fn make_tuple_type_specifier(
4685 left_paren: Self::R,
4687 right_paren: Self::R,
4689 // We don't need to include the tys list in this position merging
4690 // because by definition it's already contained by the two brackets.
4691 let pos = self.merge_positions(left_paren, right_paren);
4692 let tys = self.slice(tys.iter().filter_map(|&node| self.node_to_ty(node)));
4693 self.hint_ty(pos, Ty_::Ttuple(tys))
4696 fn make_tuple_type_explicit_specifier(
4699 _left_angle: Self::R,
4701 right_angle: Self::R,
4703 let id = (self.get_pos(keyword), "\\tuple");
4704 // This is an error--tuple syntax is (A, B), not tuple<A, B>.
4705 // OCaml decl makes a Tapply rather than a Ttuple here.
4706 self.make_apply(id, types, self.get_pos(right_angle))
4709 fn make_intersection_type_specifier(
4711 left_paren: Self::R,
4713 right_paren: Self::R,
4715 let pos = self.merge_positions(left_paren, right_paren);
4716 let tys = self.slice(tys.iter().filter_map(|x| match x {
4717 Node::ListItem(&(ty, _ampersand)) => self.node_to_ty(ty),
4718 &x => self.node_to_ty(x),
4720 self.hint_ty(pos, Ty_::Tintersection(tys))
4723 fn make_union_type_specifier(
4725 left_paren: Self::R,
4727 right_paren: Self::R,
4729 let pos = self.merge_positions(left_paren, right_paren);
4730 let tys = self.slice(tys.iter().filter_map(|x| match x {
4731 Node::ListItem(&(ty, _bar)) => self.node_to_ty(ty),
4732 &x => self.node_to_ty(x),
4734 self.hint_ty(pos, Ty_::Tunion(tys))
4737 fn make_shape_type_specifier(
4745 let fields = fields;
4746 let fields_iter = fields.iter();
4747 let mut fields = AssocListMut::new_in(self.arena);
4748 for node in fields_iter {
4749 if let Node::ShapeFieldSpecifier(&ShapeFieldNode { name, type_ }) = *node {
4750 fields.insert(self.make_t_shape_field_name(name), type_)
4753 let kind = match open.token_kind() {
4754 Some(TokenKind::DotDotDot) => ShapeKind::OpenShape,
4755 _ => ShapeKind::ClosedShape,
4757 let pos = self.merge_positions(shape, rparen);
4758 self.hint_ty(pos, Ty_::Tshape(self.alloc((kind, fields.into()))))
4761 fn make_classname_type_specifier(
4766 _trailing_comma: Self::R,
4769 let id = match classname.as_id() {
4771 None => return Node::Ignored(SK::ClassnameTypeSpecifier),
4773 if gt.is_ignored() {
4774 self.prim_ty(aast::Tprim::Tstring, id.0)
4777 (id.0, self.elaborate_raw_id(id.1)),
4779 self.merge_positions(classname, gt),
4784 fn make_scope_resolution_expression(
4786 class_name: Self::R,
4790 let pos = self.merge_positions(class_name, value);
4791 let Id(class_name_pos, class_name_str) = match self.expect_name(class_name) {
4793 if matches!(class_name, Node::XhpName(..))
4794 && self.opts.disable_xhp_element_mangling
4795 && self.retain_or_omit_user_attributes_for_facts
4797 // for facts, allow xhp class consts to be mangled later
4798 // on even when xhp_element_mangling is disabled
4799 let mut qualified = String::with_capacity_in(id.1.len() + 1, self.arena);
4800 qualified.push_str("\\");
4801 qualified.push_str(id.1);
4802 Id(id.0, self.arena.alloc_str(&qualified))
4804 self.elaborate_id(id)
4807 None => return Node::Ignored(SK::ScopeResolutionExpression),
4809 let class_id = self.alloc(aast::ClassId(
4813 Node::Name(("self", _)) => aast::ClassId_::CIself,
4814 _ => aast::ClassId_::CI(self.alloc(Id(class_name_pos, class_name_str))),
4817 let value_id = match self.expect_name(value) {
4819 None => return Node::Ignored(SK::ScopeResolutionExpression),
4821 self.accumulate_const_ref(class_id, &value_id);
4822 Node::Expr(self.alloc(aast::Expr(
4825 nast::Expr_::ClassConst(self.alloc((class_id, self.alloc((value_id.0, value_id.1))))),
4829 fn make_field_specifier(
4831 question_token: Self::R,
4836 let optional = question_token.is_present();
4837 let ty = match self.node_to_ty(type_) {
4839 None => return Node::Ignored(SK::FieldSpecifier),
4841 let name = match self.make_shape_field_name(name) {
4843 None => return Node::Ignored(SK::FieldSpecifier),
4845 Node::ShapeFieldSpecifier(self.alloc(ShapeFieldNode {
4846 name: self.alloc(ShapeField(name)),
4847 type_: self.alloc(ShapeFieldType { optional, ty }),
4851 fn make_field_initializer(&mut self, key: Self::R, _arrow: Self::R, value: Self::R) -> Self::R {
4852 Node::ListItem(self.alloc((key, value)))
4855 fn make_varray_type_specifier(
4857 varray_keyword: Self::R,
4858 _less_than: Self::R,
4860 _trailing_comma: Self::R,
4861 greater_than: Self::R,
4863 let tparam = match self.node_to_ty(tparam) {
4865 None => self.tany_with_pos(self.get_pos(varray_keyword)),
4868 self.merge_positions(varray_keyword, greater_than),
4869 Ty_::Tapply(self.alloc((
4871 self.get_pos(varray_keyword),
4872 naming_special_names::collections::VEC,
4874 self.alloc([tparam]),
4879 fn make_darray_type_specifier(
4882 _less_than: Self::R,
4885 value_type: Self::R,
4886 _trailing_comma: Self::R,
4887 greater_than: Self::R,
4889 let pos = self.merge_positions(darray, greater_than);
4890 let key_type = self.node_to_ty(key_type).unwrap_or(TANY);
4891 let value_type = self.node_to_ty(value_type).unwrap_or(TANY);
4894 Ty_::Tapply(self.alloc((
4896 self.get_pos(darray),
4897 naming_special_names::collections::DICT,
4899 self.alloc([key_type, value_type]),
4904 fn make_old_attribute_specification(
4911 Node::List(nodes) => {
4912 Node::BracketedList(self.alloc((self.get_pos(ltlt), nodes, self.get_pos(gtgt))))
4914 _ => Node::Ignored(SK::OldAttributeSpecification),
4918 fn make_constructor_call(
4921 _left_paren: Self::R,
4923 _right_paren: Self::R,
4925 let unqualified_name = match self.expect_name(name) {
4927 None => return Node::Ignored(SK::ConstructorCall),
4929 let name = if unqualified_name.1.starts_with("__") {
4932 match self.expect_name(name) {
4933 Some(name) => self.elaborate_id(name),
4934 None => return Node::Ignored(SK::ConstructorCall),
4937 let classname_params = self.slice(args.iter().filter_map(|node| match node {
4938 Node::Expr(aast::Expr(
4941 aast::Expr_::ClassConst(&(
4942 aast::ClassId(_, _, aast::ClassId_::CI(&Id(pos, class_name))),
4946 let name = if class_name.starts_with(':')
4947 && self.opts.disable_xhp_element_mangling
4948 && self.retain_or_omit_user_attributes_for_facts
4950 // for facts, allow xhp class consts to be mangled later on
4951 // even when xhp_element_mangling is disabled
4952 let mut qualified = String::with_capacity_in(class_name.len() + 1, self.arena);
4953 qualified.push_str("\\");
4954 qualified.push_str(class_name);
4955 Id(pos, self.arena.alloc_str(&qualified))
4957 self.elaborate_id(Id(pos, class_name))
4959 Some(ClassNameParam { name, full_pos })
4961 Node::StringLiteral((name, full_pos))
4962 if self.retain_or_omit_user_attributes_for_facts =>
4964 Some(ClassNameParam {
4965 name: Id(NO_POS, self.str_from_utf8_for_bytes_in_arena(*name)),
4969 Node::IntLiteral((name, full_pos)) if self.retain_or_omit_user_attributes_for_facts => {
4970 Some(ClassNameParam {
4971 name: Id(NO_POS, name),
4978 let string_literal_params = if match name.1 {
4979 "__Deprecated" | "__Cipp" | "__CippLocal" | "__Policied" | "__Module" => true,
4982 fn fold_string_concat<'a>(expr: &nast::Expr<'a>, acc: &mut Vec<'a, u8>) {
4984 aast::Expr(_, _, aast::Expr_::String(val)) => acc.extend_from_slice(val),
4985 aast::Expr(_, _, aast::Expr_::Binop(&(Bop::Dot, e1, e2))) => {
4986 fold_string_concat(e1, acc);
4987 fold_string_concat(e2, acc);
4993 self.slice(args.iter().filter_map(|expr| match expr {
4994 Node::StringLiteral((x, p)) => Some((*p, *x)),
4995 Node::Expr(e @ aast::Expr(_, p, aast::Expr_::Binop(_))) => {
4996 let mut acc = Vec::new_in(self.arena);
4997 fold_string_concat(e, &mut acc);
4998 Some((p, acc.into_bump_slice().into()))
5006 Node::Attribute(self.alloc(UserAttributeNode {
5009 string_literal_params,
5017 _semicolon: Self::R,
5019 Node::TraitUse(self.alloc(names))
5022 fn make_trait_use_conflict_resolution(
5026 _left_brace: Self::R,
5028 _right_brace: Self::R,
5030 Node::TraitUse(self.alloc(names))
5033 fn make_require_clause(
5036 require_type: Self::R,
5038 _semicolon: Self::R,
5040 Node::RequireClause(self.alloc(RequireClause { require_type, name }))
5043 fn make_nullable_type_specifier(&mut self, question_mark: Self::R, hint: Self::R) -> Self::R {
5044 let pos = self.merge_positions(question_mark, hint);
5045 let ty = match self.node_to_ty(hint) {
5047 None => return Node::Ignored(SK::NullableTypeSpecifier),
5049 self.hint_ty(pos, Ty_::Toption(ty))
5052 fn make_like_type_specifier(&mut self, tilde: Self::R, hint: Self::R) -> Self::R {
5053 let pos = self.merge_positions(tilde, hint);
5054 let ty = match self.node_to_ty(hint) {
5056 None => return Node::Ignored(SK::LikeTypeSpecifier),
5058 self.hint_ty(pos, Ty_::Tlike(ty))
5061 fn make_closure_type_specifier(
5063 outer_left_paren: Self::R,
5064 readonly_keyword: Self::R,
5065 _function_keyword: Self::R,
5066 _inner_left_paren: Self::R,
5067 parameter_list: Self::R,
5068 _inner_right_paren: Self::R,
5069 capability: Self::R,
5071 readonly_ret: Self::R,
5072 return_type: Self::R,
5073 outer_right_paren: Self::R,
5075 let mut ft_variadic = false;
5076 let mut make_param = |fp: &'a FunParamDecl<'a>| -> &'a FunParam<'a> {
5077 let mut flags = FunParamFlags::empty();
5080 ParamMode::FPinout => {
5081 flags |= FunParamFlags::INOUT;
5083 ParamMode::FPnormal => {}
5087 flags |= FunParamFlags::READONLY;
5093 self.alloc(FunParam {
5094 pos: self.get_pos(fp.hint),
5096 type_: self.alloc(PossiblyEnforcedTy {
5097 enforced: Enforcement::Unenforced,
5098 type_: self.node_to_ty(fp.hint).unwrap_or(TANY),
5104 let params = self.slice(parameter_list.iter().filter_map(|&node| match node {
5105 Node::FunParam(fp) => Some(make_param(fp)),
5109 let ret = match self.node_to_ty(return_type) {
5111 None => return Node::Ignored(SK::ClosureTypeSpecifier),
5113 let pos = self.merge_positions(outer_left_paren, outer_right_paren);
5114 let implicit_params = self.as_fun_implicit_params(capability, pos);
5116 let mut flags = FunTypeFlags::empty();
5117 if readonly_ret.is_token(TokenKind::Readonly) {
5118 flags |= FunTypeFlags::RETURNS_READONLY;
5120 if readonly_keyword.is_token(TokenKind::Readonly) {
5121 flags |= FunTypeFlags::READONLY_THIS;
5124 flags |= FunTypeFlags::VARIADIC
5129 Ty_::Tfun(self.alloc(FunType {
5131 where_constraints: &[],
5134 ret: self.alloc(PossiblyEnforcedTy {
5135 enforced: Enforcement::Unenforced,
5139 ifc_decl: default_ifc_fun_decl(),
5144 fn make_closure_parameter_type_specifier(
5150 let kind = if inout.is_token(TokenKind::Inout) {
5155 Node::FunParam(self.alloc(FunParamDecl {
5156 attributes: Node::Ignored(SK::Missing),
5157 visibility: Node::Ignored(SK::Missing),
5160 readonly: readonly.is_token(TokenKind::Readonly),
5161 pos: self.get_pos(hint),
5164 initializer: Node::Ignored(SK::Missing),
5168 fn make_type_const_declaration(
5170 attributes: Self::R,
5172 _const_keyword: Self::R,
5173 _type_keyword: Self::R,
5175 _type_parameters: Self::R,
5176 as_constraint: Self::R,
5179 _semicolon: Self::R,
5181 let attributes = self.to_attributes(attributes);
5182 let has_abstract_keyword = modifiers
5184 .any(|node| node.is_token(TokenKind::Abstract));
5185 let as_constraint = match as_constraint {
5186 Node::TypeConstraint(innards) => self.node_to_ty(innards.1),
5189 let type_ = self.node_to_ty(type_);
5190 let kind = if has_abstract_keyword {
5191 // Abstract type constant:
5192 // abstract const type T [as X] [super Y] [= Z];
5193 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5195 super_constraint: None,
5198 } else if let Some(tc_type) = type_ {
5199 // Concrete type constant:
5200 // const type T = Z;
5201 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type }))
5203 // concrete or type constant requires a value
5204 return Node::Ignored(SK::TypeConstDeclaration);
5206 let name = match name.as_id() {
5208 None => return Node::Ignored(SK::TypeConstDeclaration),
5210 Node::TypeConstant(self.alloc(ShallowTypeconst {
5213 enforceable: match attributes.enforceable {
5214 Some(pos) => (pos, true),
5215 None => (NO_POS, false),
5217 reifiable: attributes.reifiable,
5222 fn make_context_const_declaration(
5225 _const_keyword: Self::R,
5226 _ctx_keyword: Self::R,
5228 _type_parameters: Self::R,
5229 constraints: Self::R,
5232 _semicolon: Self::R,
5234 let name = match name.as_id() {
5236 None => return Node::Ignored(SK::TypeConstDeclaration),
5238 let has_abstract_keyword = modifiers
5240 .any(|node| node.is_token(TokenKind::Abstract));
5241 let context = self.node_to_ty(ctx_list);
5243 // note: lowerer ensures that there's at most 1 constraint of each kind
5244 let mut as_constraint = None;
5245 let mut super_constraint = None;
5246 for c in constraints.iter() {
5247 if let Node::ContextConstraint(&(kind, hint)) = c {
5248 let ty = self.node_to_ty(hint);
5250 ConstraintKind::ConstraintSuper => super_constraint = ty,
5251 ConstraintKind::ConstraintAs => as_constraint = ty,
5256 let kind = if has_abstract_keyword {
5257 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5262 } else if let Some(tc_type) = context {
5263 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type }))
5265 /* Concrete type const must have a value */
5266 return Node::Ignored(SK::TypeConstDeclaration);
5268 Node::TypeConstant(self.alloc(ShallowTypeconst {
5271 enforceable: (NO_POS, false),
5277 fn make_decorated_expression(&mut self, decorator: Self::R, expr: Self::R) -> Self::R {
5278 Node::ListItem(self.alloc((decorator, expr)))
5281 fn make_type_constant(
5284 _coloncolon: Self::R,
5285 constant_name: Self::R,
5287 let id = match self.expect_name(constant_name) {
5289 None => return Node::Ignored(SK::TypeConstant),
5291 let pos = self.merge_positions(ty, constant_name);
5292 let ty = match (ty, self.classish_name_builder.get_current_classish_name()) {
5293 (Node::Name(("self", self_pos)), Some((name, class_name_pos))) => {
5294 // In classes, we modify the position when rewriting the
5295 // `self` keyword to point to the class name. In traits,
5296 // we don't (because traits are not types). We indicate
5297 // that the position shouldn't be rewritten with the
5299 let id_pos = if class_name_pos.is_none() {
5304 let reason = self.alloc(Reason::hint(self_pos));
5305 let ty_ = Ty_::Tapply(self.alloc(((id_pos, name), &[][..])));
5306 self.alloc(Ty(reason, ty_))
5308 _ => match self.node_to_ty(ty) {
5310 None => return Node::Ignored(SK::TypeConstant),
5313 let reason = self.alloc(Reason::hint(pos));
5314 // The reason-rewriting here is only necessary to match the
5315 // behavior of OCaml decl (which flattens and then unflattens
5316 // Haccess hints, losing some position information).
5317 let ty = self.rewrite_taccess_reasons(ty, reason);
5318 Node::Ty(self.alloc(Ty(
5320 Ty_::Taccess(self.alloc(TaccessType(ty, id.into()))),
5324 fn make_soft_type_specifier(&mut self, at_token: Self::R, hint: Self::R) -> Self::R {
5325 let pos = self.merge_positions(at_token, hint);
5326 let hint = match self.node_to_ty(hint) {
5328 None => return Node::Ignored(SK::SoftTypeSpecifier),
5330 // Use the type of the hint as-is (i.e., throw away the knowledge that
5331 // we had a soft type specifier here--the typechecker does not use it).
5332 // Replace its Reason with one including the position of the `@` token.
5335 if self.opts.interpret_soft_types_as_like_types {
5343 fn make_attribute_specification(&mut self, attributes: Self::R) -> Self::R {
5344 if self.retain_or_omit_user_attributes_for_facts {
5347 Node::Ignored(SK::AttributeSpecification)
5351 fn make_attribute(&mut self, _at: Self::R, attribute: Self::R) -> Self::R {
5352 if self.retain_or_omit_user_attributes_for_facts {
5355 Node::Ignored(SK::Attribute)
5359 // A type specifier preceded by an attribute list. At the time of writing,
5360 // only the <<__Soft>> attribute is permitted here.
5361 fn make_attributized_specifier(&mut self, attributes: Self::R, hint: Self::R) -> Self::R {
5363 Node::BracketedList((
5365 [Node::Attribute(UserAttributeNode {
5366 name: Id(_, "__Soft"),
5371 let attributes_pos = self.merge(*ltlt_pos, *gtgt_pos);
5372 let hint_pos = self.get_pos(hint);
5373 // Use the type of the hint as-is (i.e., throw away the
5374 // knowledge that we had a soft type specifier here--the
5375 // typechecker does not use it). Replace its Reason with one
5376 // including the position of the attribute list.
5377 let hint = match self.node_to_ty(hint) {
5379 None => return Node::Ignored(SK::AttributizedSpecifier),
5383 self.merge(attributes_pos, hint_pos),
5384 if self.opts.interpret_soft_types_as_like_types {
5395 fn make_vector_type_specifier(
5398 _left_angle: Self::R,
5400 _trailing_comma: Self::R,
5401 right_angle: Self::R,
5403 let id = match self.expect_name(vec) {
5405 None => return Node::Ignored(SK::VectorTypeSpecifier),
5407 let id = (id.0, self.elaborate_raw_id(id.1));
5408 self.make_apply(id, hint, self.get_pos(right_angle))
5411 fn make_dictionary_type_specifier(
5414 _left_angle: Self::R,
5415 type_arguments: Self::R,
5416 right_angle: Self::R,
5418 let id = match self.expect_name(dict) {
5420 None => return Node::Ignored(SK::DictionaryTypeSpecifier),
5422 let id = (id.0, self.elaborate_raw_id(id.1));
5423 self.make_apply(id, type_arguments, self.get_pos(right_angle))
5426 fn make_keyset_type_specifier(
5429 _left_angle: Self::R,
5431 _trailing_comma: Self::R,
5432 right_angle: Self::R,
5434 let id = match self.expect_name(keyset) {
5436 None => return Node::Ignored(SK::KeysetTypeSpecifier),
5438 let id = (id.0, self.elaborate_raw_id(id.1));
5439 self.make_apply(id, hint, self.get_pos(right_angle))
5442 fn make_variable_expression(&mut self, _expression: Self::R) -> Self::R {
5443 Node::Ignored(SK::VariableExpression)
5446 fn make_file_attribute_specification(
5448 _left_double_angle: Self::R,
5451 attributes: Self::R,
5452 _right_double_angle: Self::R,
5454 if self.retain_or_omit_user_attributes_for_facts {
5455 self.file_attributes = List::empty();
5456 for attr in attributes.iter() {
5458 Node::Attribute(attr) => self
5460 .push_front(self.user_attribute_to_decl(attr), self.arena),
5465 self.set_module(&attributes);
5466 Node::Ignored(SK::FileAttributeSpecification)
5469 fn make_subscript_expression(
5472 _left_bracket: Self::R,
5474 _right_bracket: Self::R,
5476 Node::Ignored(SK::SubscriptExpression)
5479 fn make_member_selection_expression(
5485 Node::Ignored(SK::MemberSelectionExpression)
5488 fn make_object_creation_expression(
5490 _new_keyword: Self::R,
5493 Node::Ignored(SK::ObjectCreationExpression)
5496 fn make_safe_member_selection_expression(
5502 Node::Ignored(SK::SafeMemberSelectionExpression)
5505 fn make_function_call_expression(
5508 _type_args: Self::R,
5509 _enum_class_label: Self::R,
5510 _left_paren: Self::R,
5511 _argument_list: Self::R,
5512 _right_paren: Self::R,
5514 Node::Ignored(SK::FunctionCallExpression)
5517 fn make_list_expression(
5520 _left_paren: Self::R,
5522 _right_paren: Self::R,
5524 Node::Ignored(SK::ListExpression)
5527 fn make_module_declaration(
5529 _attributes: Self::R,
5530 _module_keyword: Self::R,
5532 _left_brace: Self::R,
5533 _right_brace: Self::R,
5536 Node::Name(&(name, mdt_pos)) => {
5537 let module = self.alloc(shallow_decl_defs::ModuleDefType { mdt_pos });
5538 self.add_module(name, module);
5542 Node::Ignored(SK::ModuleDeclaration)