1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use std::collections::BTreeMap;
11 collections::{String, Vec},
15 use hh_autoimport_rust as hh_autoimport;
16 use naming_special_names_rust as naming_special_names;
18 use arena_collections::{AssocListMut, List, MultiSetMut};
19 use flatten_smart_constructors::{FlattenOp, FlattenSmartConstructors};
20 use namespaces::ElaborateKind;
21 use namespaces_rust as namespaces;
22 use oxidized_by_ref::{
25 Abstraction, Bop, ClassishKind, ConstraintKind, FunKind, Id, ShapeFieldName, Uop, Variance,
28 decl_parser_options::DeclParserOptions,
29 direct_decl_parser::Decls,
31 method_flags::MethodFlags,
32 namespace_env::Env as NamespaceEnv,
35 prop_flags::PropFlags,
36 relative_path::RelativePath,
39 self, Decl, ShallowClassConst, ShallowMethod, ShallowProp, ShallowTypeconst,
41 shape_map::ShapeField,
42 t_shape_map::TShapeField,
44 self, AbstractTypeconst, Capability::*, ClassConstKind, ConcreteTypeconst, ConstDecl,
45 Enforcement, EnumType, FunArity, FunElt, FunImplicitParams, FunParam, FunParams, FunType,
46 IfcFunDecl, ParamMode, PosByteString, PosId, PosString, PossiblyEnforcedTy, ShapeFieldType,
47 ShapeKind, TaccessType, Tparam, TshapeFieldName, Ty, Ty_, Typeconst, TypedefType,
50 typing_defs_flags::{FunParamFlags, FunTypeFlags},
51 typing_reason::Reason,
54 use parser_core_types::{
55 compact_token::CompactToken, indexed_source_text::IndexedSourceText, source_text::SourceText,
56 syntax_kind::SyntaxKind, token_factory::SimpleTokenFactoryImpl, token_kind::TokenKind,
59 mod direct_decl_smart_constructors_generated;
63 type SSet<'a> = arena_collections::SortedSet<'a, &'a str>;
66 pub struct DirectDeclSmartConstructors<'a, 'text, S: SourceTextAllocator<'text, 'a>> {
67 pub token_factory: SimpleTokenFactoryImpl<CompactToken>,
69 pub source_text: IndexedSourceText<'text>,
70 pub arena: &'a bumpalo::Bump,
72 pub file_attributes: List<'a, &'a typing_defs::UserAttribute<'a>>,
73 // const_refs will accumulate all scope-resolution-expressions it enconuters while it's "Some"
74 const_refs: Option<arena_collections::set::Set<'a, typing_defs::ClassConstRef<'a>>>,
75 opts: &'a DeclParserOptions<'a>,
76 filename: &'a RelativePath<'a>,
78 namespace_builder: Rc<NamespaceBuilder<'a>>,
79 classish_name_builder: ClassishNameBuilder<'a>,
80 type_parameters: Rc<Vec<'a, SSet<'a>>>,
81 retain_or_omit_user_attributes_for_facts: bool,
82 simplify_naming_for_facts: bool,
83 previous_token_kind: TokenKind,
85 source_text_allocator: S,
86 module: Option<Id<'a>>,
89 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> DirectDeclSmartConstructors<'a, 'text, S> {
91 opts: &'a DeclParserOptions<'a>,
92 src: &SourceText<'text>,
95 source_text_allocator: S,
96 retain_or_omit_user_attributes_for_facts: bool,
97 simplify_naming_for_facts: bool,
99 let source_text = IndexedSourceText::new(src.clone());
100 let path = source_text.source_text().file_path();
101 let prefix = path.prefix();
102 let path = String::from_str_in(path.path_str(), arena).into_bump_str();
103 let filename = RelativePath::make(prefix, path);
105 token_factory: SimpleTokenFactoryImpl::new(),
110 filename: arena.alloc(filename),
112 decls: Decls::empty(),
113 file_attributes: List::empty(),
115 namespace_builder: Rc::new(NamespaceBuilder::new_in(
116 opts.auto_namespace_map,
117 opts.disable_xhp_element_mangling,
118 simplify_naming_for_facts,
121 classish_name_builder: ClassishNameBuilder::new(),
122 type_parameters: Rc::new(Vec::new_in(arena)),
123 // EndOfFile is used here as a None value (signifying "beginning of
124 // file") to save space. There is no legitimate circumstance where
125 // we would parse a token and the previous token kind would be
127 previous_token_kind: TokenKind::EndOfFile,
128 source_text_allocator,
129 retain_or_omit_user_attributes_for_facts,
130 simplify_naming_for_facts,
136 pub fn alloc<T>(&self, val: T) -> &'a T {
137 self.arena.alloc(val)
140 fn qualified_name_from_parts(&self, parts: &'a [Node<'a>], pos: &'a Pos<'a>) -> Id<'a> {
141 // Count the length of the qualified name, so that we can allocate
142 // exactly the right amount of space for it in our arena.
146 Node::Name(&(name, _)) => len += name.len(),
147 Node::Token(t) if t.kind() == TokenKind::Backslash => len += 1,
148 Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => len += name.len() + 1,
149 Node::ListItem(&(Node::Token(t), _backslash))
150 if t.kind() == TokenKind::Namespace =>
152 len += t.width() + 1;
157 // If there's no internal trivia, then we can just reference the
158 // qualified name in the original source text instead of copying it.
159 let source_len = pos.end_offset() - pos.start_offset();
160 if source_len == len {
161 let qualified_name: &'a str = self.str_from_utf8(self.source_text_at_pos(pos));
162 return Id(pos, qualified_name);
164 // Allocate `len` bytes and fill them with the fully qualified name.
165 let mut qualified_name = String::with_capacity_in(len, self.arena);
168 Node::Name(&(name, _pos)) => qualified_name.push_str(&name),
169 Node::Token(t) if t.kind() == TokenKind::Backslash => qualified_name.push('\\'),
170 &Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => {
171 qualified_name.push_str(&name);
172 qualified_name.push_str("\\");
174 &Node::ListItem(&(Node::Token(t), _backslash))
175 if t.kind() == TokenKind::Namespace =>
177 qualified_name.push_str("namespace\\");
182 debug_assert_eq!(len, qualified_name.len());
183 debug_assert_eq!(len, qualified_name.capacity());
184 Id(pos, qualified_name.into_bump_str())
187 /// If the given node is an identifier, XHP name, or qualified name,
188 /// elaborate it in the current namespace and return Some. To be used for
189 /// the name of a decl in its definition (e.g., "C" in `class C {}` or "f"
190 /// in `function f() {}`).
191 fn elaborate_defined_id(&self, name: Node<'a>) -> Option<Id<'a>> {
192 let id = match name {
193 Node::Name(&(name, pos)) => Id(pos, name),
194 Node::XhpName(&(name, pos)) => Id(pos, name),
195 Node::QualifiedName(&(parts, pos)) => self.qualified_name_from_parts(parts, pos),
196 // This is always an error; e.g. using a reserved word where a name
198 Node::Token(t) | Node::IgnoredToken(t) => {
199 let pos = self.token_pos(t);
200 let text = self.str_from_utf8(self.source_text_at_pos(pos));
205 Some(self.namespace_builder.elaborate_defined_id(id))
208 /// If the given node is a name (i.e., an identifier or a qualified name),
209 /// return Some. No namespace elaboration is performed.
210 fn expect_name(&self, name: Node<'a>) -> Option<Id<'a>> {
211 // If it's a simple identifier, return it.
212 if let id @ Some(_) = name.as_id() {
216 Node::QualifiedName(&(parts, pos)) => Some(self.qualified_name_from_parts(parts, pos)),
217 // The IgnoredToken case is always an error; e.g. using a reserved
218 // word where a name is expected. The Token case is not an error if
219 // the token is TokenKind::XHP (which is legal to use as a name),
220 // but an error otherwise (since we expect a Name or QualifiedName
221 // here, and the Name case would have been handled in `as_id`
223 Node::Token(t) | Node::IgnoredToken(t) => {
224 let pos = self.token_pos(t);
225 let text = self.str_from_utf8(self.source_text_at_pos(pos));
232 /// Fully qualify the given identifier as a type name (with consideration
233 /// to `use` statements in scope).
234 fn elaborate_id(&self, id: Id<'a>) -> Id<'a> {
235 let Id(pos, name) = id;
236 Id(pos, self.elaborate_raw_id(name))
239 /// Fully qualify the given identifier as a type name (with consideration
240 /// to `use` statements in scope).
241 fn elaborate_raw_id(&self, id: &'a str) -> &'a str {
242 self.namespace_builder
243 .elaborate_raw_id(ElaborateKind::Class, id)
246 /// Fully qualify the given identifier as a constant name (with
247 /// consideration to `use` statements in scope).
248 fn elaborate_const_id(&self, id: Id<'a>) -> Id<'a> {
249 let Id(pos, name) = id;
252 self.namespace_builder
253 .elaborate_raw_id(ElaborateKind::Const, name),
257 fn slice<T>(&self, iter: impl Iterator<Item = T>) -> &'a [T] {
258 let mut result = match iter.size_hint().1 {
259 Some(upper_bound) => Vec::with_capacity_in(upper_bound, self.arena),
260 None => Vec::new_in(self.arena),
265 result.into_bump_slice()
268 fn start_accumulating_const_refs(&mut self) {
269 self.const_refs = Some(arena_collections::set::Set::empty());
272 fn accumulate_const_ref(&mut self, class_id: &'a aast::ClassId<'_, (), ()>, value_id: &Id<'a>) {
273 // The decl for a class constant stores a list of all the scope-resolution expressions
274 // it contains. For example "const C=A::X" stores A::X, and "const D=self::Y" stores self::Y.
275 // (This is so we can detect cross-type circularity in constant initializers).
276 // TODO: Hack is the wrong place to detect circularity (because we can never do it completely soundly,
277 // and because it's a cross-body problem). The right place to do it is in a linter. All this should be
278 // removed from here and put into a linter.
279 if let Some(const_refs) = self.const_refs {
281 nast::ClassId_::CI(sid) => {
282 self.const_refs = Some(const_refs.add(
284 typing_defs::ClassConstRef(
285 typing_defs::ClassConstFrom::From(sid.1),
290 nast::ClassId_::CIself => {
291 self.const_refs = Some(const_refs.add(
293 typing_defs::ClassConstRef(typing_defs::ClassConstFrom::Self_, value_id.1),
297 nast::ClassId_::CIparent
298 | nast::ClassId_::CIstatic
299 | nast::ClassId_::CIexpr(_) => {}
304 fn stop_accumulating_const_refs(&mut self) -> &'a [typing_defs::ClassConstRef<'a>] {
305 let const_refs = self.const_refs;
306 self.const_refs = None;
308 Some(const_refs) => {
309 let mut elements: Vec<'_, typing_defs::ClassConstRef<'_>> =
310 bumpalo::collections::Vec::with_capacity_in(const_refs.count(), self.arena);
311 elements.extend(const_refs.into_iter());
312 elements.into_bump_slice()
319 pub trait SourceTextAllocator<'text, 'target>: Clone {
320 fn alloc(&self, text: &'text str) -> &'target str;
324 pub struct NoSourceTextAllocator;
326 impl<'text> SourceTextAllocator<'text, 'text> for NoSourceTextAllocator {
328 fn alloc(&self, text: &'text str) -> &'text str {
334 pub struct ArenaSourceTextAllocator<'arena>(pub &'arena bumpalo::Bump);
336 impl<'text, 'arena> SourceTextAllocator<'text, 'arena> for ArenaSourceTextAllocator<'arena> {
338 fn alloc(&self, text: &'text str) -> &'arena str {
339 self.0.alloc_str(text)
343 fn prefix_slash<'a>(arena: &'a Bump, name: &str) -> &'a str {
344 let mut s = String::with_capacity_in(1 + name.len(), arena);
350 fn prefix_colon<'a>(arena: &'a Bump, name: &str) -> &'a str {
351 let mut s = String::with_capacity_in(1 + name.len(), arena);
357 fn concat<'a>(arena: &'a Bump, str1: &str, str2: &str) -> &'a str {
358 let mut result = String::with_capacity_in(str1.len() + str2.len(), arena);
359 result.push_str(str1);
360 result.push_str(str2);
361 result.into_bump_str()
364 fn strip_dollar_prefix<'a>(name: &'a str) -> &'a str {
365 name.trim_start_matches("$")
368 const TANY_: Ty_<'_> = Ty_::Tany(oxidized_by_ref::tany_sentinel::TanySentinel);
369 const TANY: &Ty<'_> = &Ty(Reason::none(), TANY_);
371 fn tany() -> &'static Ty<'static> {
375 fn default_ifc_fun_decl<'a>() -> IfcFunDecl<'a> {
376 IfcFunDecl::FDPolicied(Some("PUBLIC"))
382 visibility: aast::Visibility,
388 fn read_member_modifiers<'a: 'b, 'b>(modifiers: impl Iterator<Item = &'b Node<'a>>) -> Modifiers {
389 let mut ret = Modifiers {
391 visibility: aast::Visibility::Public,
396 for modifier in modifiers {
397 if let Some(vis) = modifier.as_visibility() {
398 ret.visibility = vis;
400 match modifier.token_kind() {
401 Some(TokenKind::Static) => ret.is_static = true,
402 Some(TokenKind::Abstract) => ret.is_abstract = true,
403 Some(TokenKind::Final) => ret.is_final = true,
404 Some(TokenKind::Readonly) => ret.is_readonly = true,
411 #[derive(Clone, Debug)]
412 struct NamespaceBuilder<'a> {
414 stack: Vec<'a, NamespaceEnv<'a>>,
416 auto_ns_map: &'a [(&'a str, &'a str)],
417 simplify_naming_for_facts: bool,
420 impl<'a> NamespaceBuilder<'a> {
422 auto_ns_map: &'a [(&'a str, &'a str)],
423 disable_xhp_element_mangling: bool,
424 simplify_naming_for_facts: bool,
427 let mut ns_uses = SMap::empty();
428 for &alias in hh_autoimport::NAMESPACES {
429 ns_uses = ns_uses.add(arena, alias, concat(arena, "HH\\", alias));
431 for (alias, ns) in auto_ns_map.iter() {
432 ns_uses = ns_uses.add(arena, alias, ns);
435 let mut class_uses = SMap::empty();
436 for &alias in hh_autoimport::TYPES {
437 class_uses = class_uses.add(arena, alias, concat(arena, "HH\\", alias));
442 stack: bumpalo::vec![in arena; NamespaceEnv {
445 fun_uses: SMap::empty(),
446 const_uses: SMap::empty(),
450 disable_xhp_element_mangling,
453 simplify_naming_for_facts,
457 fn push_namespace(&mut self, name: Option<&str>) {
458 let current = self.current_namespace();
459 let nsenv = self.stack.last().unwrap().clone(); // shallow clone
460 if let Some(name) = name {
461 let mut fully_qualified = match current {
462 None => String::with_capacity_in(name.len(), self.arena),
464 let mut fully_qualified =
465 String::with_capacity_in(current.len() + name.len() + 1, self.arena);
466 fully_qualified.push_str(current);
467 fully_qualified.push('\\');
471 fully_qualified.push_str(name);
472 self.stack.push(NamespaceEnv {
473 name: Some(fully_qualified.into_bump_str()),
477 self.stack.push(NamespaceEnv {
484 fn pop_namespace(&mut self) {
485 // We'll never push a namespace for a declaration of items in the global
486 // namespace (e.g., `namespace { ... }`), so only pop if we are in some
487 // namespace other than the global one.
488 if self.stack.len() > 1 {
489 self.stack.pop().unwrap();
493 // push_namespace(Y) + pop_namespace() + push_namespace(X) should be equivalent to
494 // push_namespace(Y) + push_namespace(X) + pop_previous_namespace()
495 fn pop_previous_namespace(&mut self) {
496 if self.stack.len() > 2 {
497 let last = self.stack.pop().unwrap().name.unwrap_or("\\");
498 let previous = self.stack.pop().unwrap().name.unwrap_or("\\");
499 assert!(last.starts_with(previous));
500 let name = &last[previous.len() + 1..last.len()];
501 self.push_namespace(Some(name));
505 fn current_namespace(&self) -> Option<&'a str> {
506 self.stack.last().and_then(|nsenv| nsenv.name)
509 fn add_import(&mut self, kind: NamespaceUseKind, name: &'a str, aliased_name: Option<&'a str>) {
510 let stack_top = &mut self
513 .expect("Attempted to get the current import map, but namespace stack was empty");
514 let aliased_name = aliased_name.unwrap_or_else(|| {
515 name.rsplit_terminator('\\')
517 .expect("Expected at least one entry in import name")
519 let name = name.trim_end_matches('\\');
520 let name = if name.starts_with('\\') {
523 prefix_slash(self.arena, name)
526 NamespaceUseKind::Type => {
527 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
529 NamespaceUseKind::Namespace => {
530 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
532 NamespaceUseKind::Mixed => {
533 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
534 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
539 fn elaborate_raw_id(&self, kind: ElaborateKind, name: &'a str) -> &'a str {
540 if name.starts_with('\\') {
543 let env = self.stack.last().unwrap();
544 namespaces::elaborate_raw_id_in(self.arena, env, kind, name, self.simplify_naming_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.simplify_naming_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);
1031 fn concat(&self, str1: &str, str2: &str) -> &'a str {
1032 concat(self.arena, str1, str2)
1035 fn token_bytes(&self, token: &CompactToken) -> &'text [u8] {
1038 .sub(token.start_offset(), token.width())
1041 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1042 // the same data. Otherwise, copy the slice into our arena using
1043 // String::from_utf8_lossy_in, and return a reference to the arena str.
1044 fn str_from_utf8(&self, slice: &'text [u8]) -> &'a str {
1045 if let Ok(s) = std::str::from_utf8(slice) {
1046 self.source_text_allocator.alloc(s)
1048 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1052 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1053 // the same data. Otherwise, copy the slice into our arena using
1054 // String::from_utf8_lossy_in, and return a reference to the arena str.
1055 fn str_from_utf8_for_bytes_in_arena(&self, slice: &'a [u8]) -> &'a str {
1056 if let Ok(s) = std::str::from_utf8(slice) {
1059 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1065 pos1: impl Into<Option<&'a Pos<'a>>>,
1066 pos2: impl Into<Option<&'a Pos<'a>>>,
1068 match (pos1.into(), pos2.into()) {
1069 (None, None) => Pos::none(),
1070 (Some(pos), None) | (None, Some(pos)) => pos,
1071 (Some(pos1), Some(pos2)) => match (pos1.is_none(), pos2.is_none()) {
1072 (true, true) => Pos::none(),
1073 (true, false) => pos2,
1074 (false, true) => pos1,
1075 (false, false) => Pos::merge_without_checking_filename(self.arena, pos1, pos2),
1080 fn merge_positions(&self, node1: Node<'a>, node2: Node<'a>) -> &'a Pos<'a> {
1081 self.merge(self.get_pos(node1), self.get_pos(node2))
1084 fn pos_from_slice(&self, nodes: &[Node<'a>]) -> &'a Pos<'a> {
1085 nodes.iter().fold(Pos::none(), |acc, &node| {
1086 self.merge(acc, self.get_pos(node))
1090 fn get_pos(&self, node: Node<'a>) -> &'a Pos<'a> {
1091 self.get_pos_opt(node).unwrap_or(Pos::none())
1094 fn get_pos_opt(&self, node: Node<'a>) -> Option<&'a Pos<'a>> {
1095 let pos = match node {
1096 Node::Name(&(_, pos)) | Node::Variable(&(_, pos)) => pos,
1097 Node::Ty(ty) => return ty.get_pos(),
1098 Node::XhpName(&(_, pos)) => pos,
1099 Node::QualifiedName(&(_, pos)) => pos,
1100 Node::IntLiteral(&(_, pos))
1101 | Node::FloatingLiteral(&(_, pos))
1102 | Node::StringLiteral(&(_, pos))
1103 | Node::BooleanLiteral(&(_, pos)) => pos,
1104 Node::ListItem(&(fst, snd)) => self.merge_positions(fst, snd),
1105 Node::List(items) => self.pos_from_slice(&items),
1106 Node::BracketedList(&(first_pos, inner_list, second_pos)) => self.merge(
1108 self.merge(self.pos_from_slice(inner_list), second_pos),
1110 Node::Expr(&aast::Expr(_, pos, _)) => pos,
1111 Node::Token(token) => self.token_pos(token),
1114 if pos.is_none() { None } else { Some(pos) }
1117 fn token_pos(&self, token: FixedWidthToken) -> &'a Pos<'a> {
1118 let start = token.offset();
1119 let end = start + token.width();
1120 let start = self.source_text.offset_to_file_pos_triple(start);
1121 let end = self.source_text.offset_to_file_pos_triple(end);
1122 Pos::from_lnum_bol_offset(self.arena, self.filename, start, end)
1125 fn node_to_expr(&self, node: Node<'a>) -> Option<&'a nast::Expr<'a>> {
1126 let expr_ = match node {
1127 Node::Expr(expr) => return Some(expr),
1128 Node::IntLiteral(&(s, _)) => aast::Expr_::Int(s),
1129 Node::FloatingLiteral(&(s, _)) => aast::Expr_::Float(s),
1130 Node::StringLiteral(&(s, _)) => aast::Expr_::String(s),
1131 Node::BooleanLiteral((s, _)) => {
1132 if s.eq_ignore_ascii_case("true") {
1138 Node::Token(t) if t.kind() == TokenKind::NullLiteral => aast::Expr_::Null,
1139 Node::Name(..) | Node::QualifiedName(..) => {
1140 aast::Expr_::Id(self.alloc(self.elaborate_const_id(self.expect_name(node)?)))
1144 let pos = self.get_pos(node);
1145 Some(self.alloc(aast::Expr((), pos, expr_)))
1148 fn node_to_non_ret_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1149 self.node_to_ty_(node, false)
1152 fn node_to_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1153 self.node_to_ty_(node, true)
1156 fn make_supportdynamic(&self, pos: &'a Pos<'a>) -> Ty_<'a> {
1157 Ty_::Tapply(self.alloc((
1158 (pos, naming_special_names::typehints::HH_SUPPORTDYN),
1159 self.alloc([self.alloc(Ty(
1160 self.alloc(Reason::witness_from_decl(pos)),
1165 fn node_to_ty_(&self, node: Node<'a>, allow_non_ret_ty: bool) -> Option<&'a Ty<'a>> {
1167 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tvoid))) if !allow_non_ret_ty => {
1168 Some(self.alloc(Ty(reason, Ty_::Terr)))
1170 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tnoreturn))) if !allow_non_ret_ty => {
1171 Some(self.alloc(Ty(reason, Ty_::Terr)))
1173 Node::Ty(ty) => Some(ty),
1174 Node::Expr(expr) => {
1175 fn expr_to_ty<'a>(arena: &'a Bump, expr: &'a nast::Expr<'a>) -> Option<Ty_<'a>> {
1178 Null => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tnull))),
1179 This => Some(Ty_::Tthis),
1180 True | False => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tbool))),
1181 Int(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tint))),
1182 Float(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tfloat))),
1183 String(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1184 String2(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1185 PrefixedString(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1186 Unop(&(_op, expr)) => expr_to_ty(arena, expr),
1187 Hole(&(expr, _, _, _)) => expr_to_ty(arena, expr),
1189 ArrayGet(_) | As(_) | Await(_) | Binop(_) | Call(_) | Cast(_)
1190 | ClassConst(_) | ClassGet(_) | Clone(_) | Collection(_) | Darray(_)
1191 | Dollardollar(_) | Efun(_) | Eif(_) | EnumClassLabel(_) | ETSplice(_)
1192 | ExpressionTree(_) | FunctionPointer(_) | FunId(_) | Id(_) | Import(_)
1193 | Is(_) | KeyValCollection(_) | Lfun(_) | List(_) | Lplaceholder(_)
1194 | Lvar(_) | MethodCaller(_) | MethodId(_) | New(_) | ObjGet(_)
1195 | Omitted | Pair(_) | Pipe(_) | ReadonlyExpr(_) | Shape(_)
1196 | SmethodId(_) | Tuple(_) | Upcast(_) | ValCollection(_) | Varray(_)
1197 | Xml(_) | Yield(_) => None,
1201 self.alloc(Reason::witness_from_decl(expr.1)),
1202 expr_to_ty(self.arena, expr)?,
1205 Node::IntLiteral((_, pos)) => Some(self.alloc(Ty(
1206 self.alloc(Reason::witness_from_decl(pos)),
1207 Ty_::Tprim(self.alloc(aast::Tprim::Tint)),
1209 Node::FloatingLiteral((_, pos)) => Some(self.alloc(Ty(
1210 self.alloc(Reason::witness_from_decl(pos)),
1211 Ty_::Tprim(self.alloc(aast::Tprim::Tfloat)),
1213 Node::StringLiteral((_, pos)) => Some(self.alloc(Ty(
1214 self.alloc(Reason::witness_from_decl(pos)),
1215 Ty_::Tprim(self.alloc(aast::Tprim::Tstring)),
1217 Node::BooleanLiteral((_, pos)) => Some(self.alloc(Ty(
1218 self.alloc(Reason::witness_from_decl(pos)),
1219 Ty_::Tprim(self.alloc(aast::Tprim::Tbool)),
1221 Node::Token(t) if t.kind() == TokenKind::Varray => {
1222 let pos = self.token_pos(t);
1223 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1224 let ty_ = Ty_::Tapply(self.alloc((
1225 (self.token_pos(t), naming_special_names::collections::VEC),
1228 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1230 Node::Token(t) if t.kind() == TokenKind::Darray => {
1231 let pos = self.token_pos(t);
1232 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1233 let ty_ = Ty_::Tapply(self.alloc((
1234 (self.token_pos(t), naming_special_names::collections::DICT),
1235 self.alloc([tany, tany]),
1237 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1239 Node::Token(t) if t.kind() == TokenKind::This => {
1240 Some(self.alloc(Ty(self.alloc(Reason::hint(self.token_pos(t))), Ty_::Tthis)))
1242 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1243 let pos = self.token_pos(t);
1245 self.alloc(Reason::hint(pos)),
1246 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1249 // In coeffects contexts, we get types like `ctx $f` or `$v::C`.
1250 // Node::Variable is used for the `$f` and `$v`, so that we don't
1251 // incorrectly attempt to elaborate them as names.
1252 Node::Variable(&(name, pos)) => Some(self.alloc(Ty(
1253 self.alloc(Reason::hint(pos)),
1254 Ty_::Tapply(self.alloc(((pos, name), &[][..]))),
1257 let Id(pos, name) = self.expect_name(node)?;
1258 let reason = self.alloc(Reason::hint(pos));
1259 let ty_ = if self.is_type_param_in_scope(name) {
1260 // TODO (T69662957) must fill type args of Tgeneric
1261 Ty_::Tgeneric(self.alloc((name, &[])))
1264 "nothing" => Ty_::Tunion(&[]),
1266 if self.opts.everything_sdt {
1267 self.make_supportdynamic(pos)
1272 "dynamic" => Ty_::Tdynamic,
1273 "supportdynamic" => self.make_supportdynamic(pos),
1274 "varray_or_darray" | "vec_or_dict" => {
1275 let key_type = self.vec_or_dict_key(pos);
1276 let value_type = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1277 Ty_::TvecOrDict(self.alloc((key_type, value_type)))
1281 let name = self.elaborate_raw_id(name);
1282 Ty_::Tapply(self.alloc(((pos, name), &[][..])))
1286 Some(self.alloc(Ty(reason, ty_)))
1291 fn rewrite_ty_for_global_inference(
1293 ty: Option<&'a Ty<'a>>,
1295 ) -> Option<&'a Ty<'a>> {
1296 if !self.opts.global_inference {
1299 let tvar = self.alloc(Ty(self.alloc(reason), Ty_::Tvar(0)));
1303 fn cut_namespace<'a>(id: &'a str) -> &'a str {
1304 id.rsplit_terminator('\\').next().unwrap()
1306 fn reinfer_type_to_string_opt<'a>(arena: &'a Bump, ty: Ty_<'a>) -> Option<&'a str> {
1308 Ty_::Tmixed => Some("mixed"),
1309 Ty_::Tnonnull => Some("nonnull"),
1310 Ty_::Tdynamic => Some("dynamic"),
1311 Ty_::Tunion([]) => Some("nothing"),
1312 Ty_::Tthis => Some("this"),
1313 Ty_::Tprim(prim) => Some(match prim {
1314 aast::Tprim::Tnull => "null",
1315 aast::Tprim::Tvoid => "void",
1316 aast::Tprim::Tint => "int",
1317 aast::Tprim::Tnum => "num",
1318 aast::Tprim::Tfloat => "float",
1319 aast::Tprim::Tstring => "string",
1320 aast::Tprim::Tarraykey => "arraykey",
1321 aast::Tprim::Tresource => "resource",
1322 aast::Tprim::Tnoreturn => "noreturn",
1323 aast::Tprim::Tbool => "bool",
1325 Ty_::Tapply(((_p, id), _tyl)) => Some(cut_namespace(id)),
1326 Ty_::Taccess(TaccessType(ty, id)) => {
1327 reinfer_type_to_string_opt(arena, ty.1).map(|s| {
1328 bumpalo::format!(in arena, "{}::{}", s, id.1).into_bump_str()
1334 fn create_vars_for_reinfer_types<'a, 'text, S: SourceTextAllocator<'text, 'a>>(
1335 this: &DirectDeclSmartConstructors<'a, 'text, S>,
1339 let mk = |r, ty_| this.alloc(Ty(r, ty_));
1340 let must_reinfer_type = |ty| match reinfer_type_to_string_opt(this.arena, ty) {
1342 Some(ty_str) => this.opts.gi_reinfer_types.contains(&ty_str),
1345 Ty(r, Ty_::Tapply(&(id, [ty1]))) if id.1 == "\\HH\\Awaitable" => {
1346 let ty1 = this.alloc(create_vars_for_reinfer_types(this, *ty1, tvar));
1347 mk(r, Ty_::Tapply(this.alloc((id, std::slice::from_ref(ty1)))))
1349 Ty(r, Ty_::Toption(ty1)) => {
1350 let ty1 = create_vars_for_reinfer_types(this, ty1, tvar);
1351 mk(r, Ty_::Toption(ty1))
1353 Ty(r, Ty_::Tapply(((_p, id), [])))
1354 if cut_namespace(id) == "PHPism_FIXME_Array" =>
1356 if must_reinfer_type(ty.1) {
1357 let tvar = mk(r, Ty_::Tvar(0));
1358 mk(r, Ty_::TvecOrDict(this.alloc((tvar, tvar))))
1364 if must_reinfer_type(ty_) {
1372 create_vars_for_reinfer_types(self, ty, tvar)
1378 fn to_attributes(&self, node: Node<'a>) -> Attributes<'a> {
1379 let mut attributes = Attributes {
1389 accept_disposable: false,
1390 dynamically_callable: false,
1391 returns_disposable: false,
1393 ifc_attribute: default_ifc_fun_decl(),
1397 support_dynamic_type: false,
1398 module: self.module,
1402 let nodes = match node {
1403 Node::List(&nodes) | Node::BracketedList(&(_, nodes, _)) => nodes,
1404 _ => return attributes,
1407 let mut ifc_already_policied = false;
1409 // Iterate in reverse, to match the behavior of OCaml decl in error conditions.
1410 for attribute in nodes.iter().rev() {
1411 if let Node::Attribute(attribute) = attribute {
1412 match attribute.name.1.as_ref() {
1414 attributes.deprecated = attribute
1415 .string_literal_params
1417 .map(|&(_, x)| self.str_from_utf8_for_bytes_in_arena(x));
1419 "__Reifiable" => attributes.reifiable = Some(attribute.name.0),
1421 attributes.late_init = true;
1424 attributes.const_ = true;
1427 attributes.lsb = true;
1430 attributes.memoize = true;
1433 attributes.memoizelsb = true;
1436 attributes.override_ = true;
1438 "__Enforceable" => {
1439 attributes.enforceable = Some(attribute.name.0);
1441 "__AcceptDisposable" => {
1442 attributes.accept_disposable = true;
1444 "__DynamicallyCallable" => {
1445 attributes.dynamically_callable = true;
1447 "__ReturnDisposable" => {
1448 attributes.returns_disposable = true;
1451 attributes.php_std_lib = true;
1454 let string_literal_params = || {
1456 .string_literal_params
1458 .map(|&(_, x)| self.str_from_utf8_for_bytes_in_arena(x))
1460 // Take the classname param by default
1461 attributes.ifc_attribute =
1462 IfcFunDecl::FDPolicied(attribute.classname_params.first().map_or_else(
1463 string_literal_params, // default
1464 |&x| Some(x.name.1), // f
1466 ifc_already_policied = true;
1469 if !ifc_already_policied {
1470 attributes.ifc_attribute = IfcFunDecl::FDInferFlows;
1474 attributes.external = true;
1477 attributes.can_call = true;
1480 attributes.soft = true;
1482 "__SupportDynamicType" => {
1483 attributes.support_dynamic_type = true;
1486 attributes.internal = true;
1496 // Limited version of node_to_ty that matches behavior of Decl_utils.infer_const
1497 fn infer_const(&self, name: Node<'a>, node: Node<'a>) -> Option<&'a Ty<'a>> {
1499 Node::StringLiteral(_)
1500 | Node::BooleanLiteral(_)
1501 | Node::IntLiteral(_)
1502 | Node::FloatingLiteral(_)
1503 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uminus, _))))
1504 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uplus, _))))
1505 | Node::Expr(aast::Expr(_, _, aast::Expr_::String(..))) => self.node_to_ty(node),
1506 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1507 let pos = self.token_pos(t);
1509 self.alloc(Reason::witness_from_decl(pos)),
1510 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1513 _ => Some(self.tany_with_pos(self.get_pos(name))),
1517 fn pop_type_params(&mut self, node: Node<'a>) -> &'a [&'a Tparam<'a>] {
1519 Node::TypeParameters(tparams) => {
1520 Rc::make_mut(&mut self.type_parameters).pop().unwrap();
1527 fn ret_from_fun_kind(&self, kind: FunKind, type_: &'a Ty<'a>) -> &'a Ty<'a> {
1528 let pos = type_.get_pos().unwrap_or_else(|| Pos::none());
1530 FunKind::FAsyncGenerator => self.alloc(Ty(
1531 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1532 Ty_::Tapply(self.alloc((
1533 (pos, naming_special_names::classes::ASYNC_GENERATOR),
1534 self.alloc([type_, type_, type_]),
1537 FunKind::FGenerator => self.alloc(Ty(
1538 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1539 Ty_::Tapply(self.alloc((
1540 (pos, naming_special_names::classes::GENERATOR),
1541 self.alloc([type_, type_, type_]),
1544 FunKind::FAsync => self.alloc(Ty(
1545 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1546 Ty_::Tapply(self.alloc((
1547 (pos, naming_special_names::classes::AWAITABLE),
1548 self.alloc([type_]),
1555 fn is_type_param_in_scope(&self, name: &str) -> bool {
1556 self.type_parameters.iter().any(|tps| tps.contains(name))
1559 fn as_fun_implicit_params(
1561 capability: Node<'a>,
1562 default_pos: &'a Pos<'a>,
1563 ) -> &'a FunImplicitParams<'a> {
1564 /* Note: do not simplify intersections, keep empty / singleton intersections
1565 * for coeffect contexts
1567 let capability = match self.node_to_ty(capability) {
1568 Some(ty) => CapTy(ty),
1569 None => CapDefaults(default_pos),
1571 self.alloc(FunImplicitParams { capability })
1577 attributes: Node<'a>,
1578 header: &'a FunctionHeader<'a>,
1580 ) -> Option<(PosId<'a>, &'a Ty<'a>, &'a [ShallowProp<'a>])> {
1581 let id_opt = match (is_method, header.name) {
1582 // If the name is missing, we use the left paren here, just to get a
1583 // position to point to.
1584 (_, Node::Token(t)) if t.kind() == TokenKind::LeftParen => {
1585 let pos = self.token_pos(t);
1588 (true, Node::Token(t)) if t.kind() == TokenKind::Construct => {
1589 let pos = self.token_pos(t);
1590 Some(Id(pos, naming_special_names::members::__CONSTRUCT))
1592 (true, _) => self.expect_name(header.name),
1593 (false, _) => self.elaborate_defined_id(header.name),
1595 let id = id_opt.unwrap_or(Id(self.get_pos(header.name), ""));
1596 let (params, properties, arity) = self.as_fun_params(header.param_list)?;
1597 let f_pos = self.get_pos(header.name);
1598 let implicit_params = self.as_fun_implicit_params(header.capability, f_pos);
1600 let type_ = match header.name {
1601 Node::Token(t) if t.kind() == TokenKind::Construct => {
1602 let pos = self.token_pos(t);
1604 self.alloc(Reason::witness_from_decl(pos)),
1605 Ty_::Tprim(self.alloc(aast::Tprim::Tvoid)),
1609 .rewrite_ty_for_global_inference(
1610 self.node_to_ty(header.ret_hint),
1611 Reason::RglobalFunRet(f_pos),
1613 .unwrap_or_else(|| self.tany_with_pos(f_pos)),
1618 .any(|n| n.is_token(TokenKind::Async));
1619 let readonly = header
1622 .any(|n| n.is_token(TokenKind::Readonly));
1624 let fun_kind = if body.iter().any(|node| node.is_token(TokenKind::Yield)) {
1626 FunKind::FAsyncGenerator
1637 let type_ = if !header.ret_hint.is_present() {
1638 self.ret_from_fun_kind(fun_kind, type_)
1642 let attributes = self.to_attributes(attributes);
1643 // TODO(hrust) Put this in a helper. Possibly do this for all flags.
1644 let mut flags = match fun_kind {
1645 FunKind::FSync => FunTypeFlags::empty(),
1646 FunKind::FAsync => FunTypeFlags::ASYNC,
1647 FunKind::FGenerator => FunTypeFlags::GENERATOR,
1648 FunKind::FAsyncGenerator => FunTypeFlags::ASYNC | FunTypeFlags::GENERATOR,
1651 if attributes.returns_disposable {
1652 flags |= FunTypeFlags::RETURN_DISPOSABLE;
1654 if attributes.support_dynamic_type {
1655 flags |= FunTypeFlags::SUPPORT_DYNAMIC_TYPE;
1657 if header.readonly_return.is_token(TokenKind::Readonly) {
1658 flags |= FunTypeFlags::RETURNS_READONLY;
1660 if attributes.memoize || attributes.memoizelsb {
1661 flags |= FunTypeFlags::IS_MEMOIZED;
1664 flags |= FunTypeFlags::READONLY_THIS
1667 let ifc_decl = attributes.ifc_attribute;
1669 // Pop the type params stack only after creating all inner types.
1670 let tparams = self.pop_type_params(header.type_params);
1672 let where_constraints =
1673 self.slice(header.where_constraints.iter().filter_map(|&x| match x {
1674 Node::WhereConstraint(x) => Some(x),
1678 let (params, tparams, implicit_params, where_constraints) =
1679 self.rewrite_effect_polymorphism(params, tparams, implicit_params, where_constraints);
1681 let ft = self.alloc(FunType {
1687 ret: self.alloc(PossiblyEnforcedTy {
1688 enforced: Enforcement::Unenforced,
1695 let ty = self.alloc(Ty(
1696 self.alloc(Reason::witness_from_decl(id.0)),
1699 Some((id.into(), ty, properties))
1705 ) -> Option<(&'a FunParams<'a>, &'a [ShallowProp<'a>], FunArity<'a>)> {
1707 Node::List(nodes) => {
1708 let mut params = Vec::with_capacity_in(nodes.len(), self.arena);
1709 let mut properties = Vec::new_in(self.arena);
1710 let mut arity = FunArity::Fstandard;
1711 for node in nodes.iter() {
1713 Node::FunParam(&FunParamDecl {
1724 let attributes = self.to_attributes(attributes);
1726 if let Some(visibility) = visibility.as_visibility() {
1727 let name = name.unwrap_or("");
1728 let name = strip_dollar_prefix(name);
1729 let mut flags = PropFlags::empty();
1730 flags.set(PropFlags::CONST, attributes.const_);
1731 flags.set(PropFlags::NEEDS_INIT, self.file_mode != Mode::Mhhi);
1732 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
1733 flags.set(PropFlags::READONLY, readonly);
1734 properties.push(ShallowProp {
1737 type_: self.rewrite_ty_for_global_inference(
1738 self.node_to_ty(hint),
1739 Reason::RglobalFunParam(pos),
1741 visibility: if attributes.internal
1742 && visibility == aast::Visibility::Public
1744 aast::Visibility::Internal
1753 .rewrite_ty_for_global_inference(
1754 self.node_to_ty(hint),
1755 Reason::RglobalFunParam(pos),
1757 .unwrap_or_else(|| self.tany_with_pos(pos));
1758 // These are illegal here--they can only be used on
1759 // parameters in a function type hint (see
1760 // make_closure_type_specifier and unwrap_mutability).
1761 // Unwrap them here anyway for better error recovery.
1762 let type_ = match type_ {
1763 Ty(_, Ty_::Tapply(((_, "\\Mutable"), [t]))) => t,
1764 Ty(_, Ty_::Tapply(((_, "\\OwnedMutable"), [t]))) => t,
1765 Ty(_, Ty_::Tapply(((_, "\\MaybeMutable"), [t]))) => t,
1768 let mut flags = FunParamFlags::empty();
1769 if attributes.accept_disposable {
1770 flags |= FunParamFlags::ACCEPT_DISPOSABLE
1772 if attributes.external {
1773 flags |= FunParamFlags::IFC_EXTERNAL
1775 if attributes.can_call {
1776 flags |= FunParamFlags::IFC_CAN_CALL
1779 flags |= FunParamFlags::READONLY
1782 ParamMode::FPinout => {
1783 flags |= FunParamFlags::INOUT;
1785 ParamMode::FPnormal => {}
1788 if initializer.is_present() {
1789 flags |= FunParamFlags::HAS_DEFAULT;
1791 let variadic = initializer.is_ignored() && variadic;
1792 let type_ = if variadic {
1794 self.alloc(if name.is_some() {
1795 Reason::RvarParamFromDecl(pos)
1797 Reason::witness_from_decl(pos)
1804 let param = self.alloc(FunParam {
1807 type_: self.alloc(PossiblyEnforcedTy {
1808 enforced: Enforcement::Unenforced,
1813 arity = match arity {
1814 FunArity::Fstandard if variadic => FunArity::Fvariadic(param),
1825 params.into_bump_slice(),
1826 properties.into_bump_slice(),
1830 n if n.is_ignored() => Some((&[], &[], FunArity::Fstandard)),
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 arity = match fun_type.arity {
1985 FunArity::Fstandard => FunArity::Fstandard,
1986 FunArity::Fvariadic(param) => FunArity::Fvariadic(convert_param(param)),
1988 let params = self.slice(fun_type.params.iter().copied().map(convert_param));
1989 let implicit_params = fun_type.implicit_params;
1990 let ret = self.alloc(PossiblyEnforcedTy {
1991 enforced: fun_type.ret.enforced,
1992 type_: self.convert_tapply_to_tgeneric(fun_type.ret.type_),
1994 Ty_::Tfun(self.alloc(FunType {
2002 Ty_::Tshape(&(kind, fields)) => {
2003 let mut converted_fields = AssocListMut::with_capacity_in(fields.len(), self.arena);
2004 for (&name, ty) in fields.iter() {
2005 converted_fields.insert(
2007 self.alloc(ShapeFieldType {
2008 optional: ty.optional,
2009 ty: self.convert_tapply_to_tgeneric(ty.ty),
2013 Ty_::Tshape(self.alloc((kind, converted_fields.into())))
2015 Ty_::TvecOrDict(&(tk, tv)) => Ty_::TvecOrDict(self.alloc((
2016 self.convert_tapply_to_tgeneric(tk),
2017 self.convert_tapply_to_tgeneric(tv),
2019 Ty_::Ttuple(tys) => Ty_::Ttuple(
2022 .map(|&targ| self.convert_tapply_to_tgeneric(targ)),
2027 self.alloc(Ty(ty.0, ty_))
2030 // This is the logic for determining if convert_tapply_to_tgeneric should turn
2031 // a Tapply into a Tgeneric
2032 fn tapply_should_be_tgeneric(&self, reason: &'a Reason<'a>, id: PosId<'a>) -> Option<&'a str> {
2033 match reason.pos() {
2034 // If the name contained a namespace delimiter in the original
2035 // source text, then it can't have referred to a type parameter
2036 // (since type parameters cannot be namespaced).
2038 if self.source_text_at_pos(pos).contains(&b'\\') {
2042 None => return None,
2044 // However, the direct decl parser will unconditionally prefix
2045 // the name with the current namespace (as it does for any
2046 // Tapply). We need to remove it.
2047 match id.1.rsplit('\\').next() {
2048 Some(name) if self.is_type_param_in_scope(name) => return Some(name),
2053 fn rewrite_taccess_reasons(&self, ty: &'a Ty<'a>, r: &'a Reason<'a>) -> &'a Ty<'a> {
2054 let ty_ = match ty.1 {
2055 Ty_::Taccess(&TaccessType(ty, id)) => {
2056 Ty_::Taccess(self.alloc(TaccessType(self.rewrite_taccess_reasons(ty, r), id)))
2060 self.alloc(Ty(r, ty_))
2063 fn user_attribute_to_decl(
2065 attr: &UserAttributeNode<'a>,
2066 ) -> &'a shallow_decl_defs::UserAttribute<'a> {
2067 self.alloc(shallow_decl_defs::UserAttribute {
2068 name: attr.name.into(),
2069 classname_params: self.slice(attr.classname_params.iter().map(|p| p.name.1)),
2073 fn set_module(&mut self, attributes: &Node<'a>) {
2074 for attr in attributes.iter() {
2075 if let Node::Attribute(attr) = attr {
2076 if attr.name.1 == "__Module" {
2077 self.module = attr.string_literal_params.first().map(|&(p, m)| {
2078 oxidized_by_ref::ast::Id(p, self.str_from_utf8_for_bytes_in_arena(m))
2086 fn namespace_use_kind(use_kind: &Node<'_>) -> Option<NamespaceUseKind> {
2087 match use_kind.token_kind() {
2088 Some(TokenKind::Const) => None,
2089 Some(TokenKind::Function) => None,
2090 Some(TokenKind::Type) => Some(NamespaceUseKind::Type),
2091 Some(TokenKind::Namespace) => Some(NamespaceUseKind::Namespace),
2092 _ if !use_kind.is_present() => Some(NamespaceUseKind::Mixed),
2097 fn has_polymorphic_context(contexts: &[&Ty<'_>]) -> bool {
2098 contexts.iter().any(|&ty| match ty.1 {
2099 Ty_::Tapply((root, &[])) // Hfun_context in the AST
2100 | Ty_::Taccess(TaccessType(Ty(_, Ty_::Tapply((root, &[]))), _)) => root.1.contains('$'),
2101 | Ty_::Taccess(TaccessType(t, _)) => Self::taccess_root_is_generic(t),
2106 fn ctx_generic_for_fun(&self, name: &str) -> &'a str {
2107 bumpalo::format!(in self.arena, "T/[ctx {}]", name).into_bump_str()
2110 fn ctx_generic_for_dependent(&self, name: &str, cst: &str) -> &'a str {
2111 bumpalo::format!(in self.arena, "T/[{}::{}]", name, cst).into_bump_str()
2114 // Note: the reason for the divergence between this and the lowerer is that
2115 // hint Haccess is a flat list, whereas decl ty Taccess is a tree.
2116 fn taccess_root_is_generic(ty: &Ty<'_>) -> bool {
2118 Ty(_, Ty_::Tgeneric((_, &[]))) => true,
2119 Ty(_, Ty_::Taccess(&TaccessType(t, _))) => Self::taccess_root_is_generic(t),
2124 fn ctx_generic_for_generic_taccess_inner(&self, ty: &Ty<'_>, cst: &str) -> std::string::String {
2125 let left = match ty {
2126 Ty(_, Ty_::Tgeneric((name, &[]))) => name.to_string(),
2127 Ty(_, Ty_::Taccess(&TaccessType(ty, cst))) => {
2128 self.ctx_generic_for_generic_taccess_inner(ty, cst.1)
2130 _ => panic!("Unexpected element in Taccess"),
2132 format!("{}::{}", left, cst)
2134 fn ctx_generic_for_generic_taccess(&self, ty: &Ty<'_>, cst: &str) -> &'a str {
2135 bumpalo::format!(in self.arena, "T/[{}]", self.ctx_generic_for_generic_taccess_inner(ty, cst))
2139 // For a polymorphic context with form `ctx $f` (represented here as
2140 // `Tapply "$f"`), add a type parameter named `Tctx$f`, and rewrite the
2141 // parameter `(function (ts)[_]: t) $f` as `(function (ts)[Tctx$f]: t) $f`
2144 tparams: &mut Vec<'_, &'a Tparam<'a>>,
2150 let cap_ty = match ft.implicit_params.capability {
2151 CapTy(&Ty(_, Ty_::Tintersection(&[ty]))) | CapTy(ty) => ty,
2152 _ => return ty.clone(),
2154 let pos = match cap_ty.1 {
2155 Ty_::Tapply(((pos, "_"), _)) => pos,
2156 _ => return ty.clone(),
2158 let name = self.ctx_generic_for_fun(param_name);
2159 let tparam = self.alloc(Tparam {
2160 variance: Variance::Invariant,
2164 reified: aast::ReifyKind::Erased,
2165 user_attributes: &[],
2167 tparams.push(tparam);
2168 let cap_ty = self.alloc(Ty(cap_ty.0, Ty_::Tgeneric(self.alloc((name, &[])))));
2169 let ft = self.alloc(FunType {
2170 implicit_params: self.alloc(FunImplicitParams {
2171 capability: CapTy(cap_ty),
2175 Ty(ty.0, Ty_::Tfun(ft))
2177 Ty_::Tlike(ref t) => Ty(
2179 Ty_::Tlike(self.alloc(self.rewrite_fun_ctx(tparams, t, param_name))),
2181 Ty_::Toption(ref t) => Ty(
2183 Ty_::Toption(self.alloc(self.rewrite_fun_ctx(tparams, t, param_name))),
2185 Ty_::Tapply(((p, name), targs))
2186 if *name == naming_special_names::typehints::HH_SUPPORTDYN =>
2188 if let Some(ref t) = targs.first() {
2191 Ty_::Tapply(self.alloc((
2193 self.alloc([self.alloc(self.rewrite_fun_ctx(tparams, t, param_name))]),
2204 fn rewrite_effect_polymorphism(
2206 params: &'a [&'a FunParam<'a>],
2207 tparams: &'a [&'a Tparam<'a>],
2208 implicit_params: &'a FunImplicitParams<'a>,
2209 where_constraints: &'a [&'a WhereConstraint<'a>],
2211 &'a [&'a FunParam<'a>],
2212 &'a [&'a Tparam<'a>],
2213 &'a FunImplicitParams<'a>,
2214 &'a [&'a WhereConstraint<'a>],
2216 let (cap_reason, context_tys) = match implicit_params.capability {
2217 CapTy(&Ty(r, Ty_::Tintersection(tys))) if Self::has_polymorphic_context(tys) => {
2220 CapTy(ty) if Self::has_polymorphic_context(&[ty]) => {
2221 (ty.0, std::slice::from_ref(self.alloc(ty)))
2223 _ => return (params, tparams, implicit_params, where_constraints),
2225 let tp = |name, constraints| {
2227 variance: Variance::Invariant,
2231 reified: aast::ReifyKind::Erased,
2232 user_attributes: &[],
2236 // For a polymorphic context with form `$g::C`, if we have a function
2237 // parameter `$g` with type `G` (where `G` is not a type parameter),
2238 // - add a type parameter constrained by $g's type: `T/$g as G`
2239 // - replace $g's type hint (`G`) with the new type parameter `T/$g`
2240 // Then, for each polymorphic context with form `$g::C`,
2241 // - add a type parameter `T/[$g::C]`
2242 // - add a where constraint `T/[$g::C] = T$g :: C`
2243 let rewrite_arg_ctx = |
2244 tparams: &mut Vec<'_, &'a Tparam<'a>>,
2245 where_constraints: &mut Vec<'_, &'a WhereConstraint<'a>>,
2247 param_pos: &'a Pos<'a>,
2249 context_reason: &'a Reason<'a>,
2252 let rewritten_ty = match ty.1 {
2253 // If the type hint for this function parameter is a type
2254 // parameter introduced in this function declaration, don't add
2255 // a new type parameter.
2256 Ty_::Tgeneric(&(type_name, _))
2257 if tparams.iter().any(|tp| tp.name.1 == type_name) =>
2261 // Otherwise, if the parameter is `G $g`, create tparam
2262 // `T$g as G` and replace $g's type hint
2264 let id = (param_pos, self.concat("T/", name));
2267 std::slice::from_ref(
2268 self.alloc((ConstraintKind::ConstraintAs, self.alloc(ty.clone()))),
2272 self.alloc(Reason::hint(param_pos)),
2273 Ty_::Tgeneric(self.alloc((id.1, &[]))),
2277 let ty = self.alloc(Ty(context_reason, rewritten_ty.1));
2278 let right = self.alloc(Ty(
2280 Ty_::Taccess(self.alloc(TaccessType(ty, cst))),
2283 context_reason.pos().unwrap_or(Pos::none()),
2284 self.ctx_generic_for_dependent(name, &cst.1),
2286 tparams.push(tp(left_id, &[]));
2287 let left = self.alloc(Ty(
2289 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2291 where_constraints.push(self.alloc(WhereConstraint(
2293 ConstraintKind::ConstraintEq,
2299 let mut tparams = Vec::from_iter_in(tparams.iter().copied(), self.arena);
2300 let mut where_constraints =
2301 Vec::from_iter_in(where_constraints.iter().copied(), self.arena);
2303 // The divergence here from the lowerer comes from using oxidized_by_ref instead of oxidized
2304 let mut ty_by_param: BTreeMap<&str, (Ty<'a>, &'a Pos<'a>)> = params
2306 .filter_map(|param| Some((param.name?, (param.type_.type_.clone(), param.pos))))
2309 for context_ty in context_tys {
2310 match context_ty.1 {
2311 // Hfun_context in the AST.
2312 Ty_::Tapply(((_, name), _)) if name.starts_with('$') => {
2313 if let Some((param_ty, _)) = ty_by_param.get_mut(name) {
2314 *param_ty = self.rewrite_fun_ctx(&mut tparams, param_ty, name);
2317 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), _))), cst)) => {
2318 if let Some((param_ty, param_pos)) = ty_by_param.get_mut(name) {
2319 let mut rewrite = |t| {
2322 &mut where_constraints,
2331 Ty_::Tlike(ref mut ty) => match ty {
2332 Ty(r, Ty_::Toption(tinner)) => {
2334 self.alloc(Ty(r, Ty_::Toption(self.alloc(rewrite(tinner)))))
2337 *ty = self.alloc(rewrite(ty));
2340 Ty_::Toption(ref mut ty) => {
2341 *ty = self.alloc(rewrite(ty));
2344 *param_ty = rewrite(param_ty);
2349 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2351 context_ty.0.pos().unwrap_or(Pos::none()),
2352 self.ctx_generic_for_generic_taccess(t, &cst.1),
2354 tparams.push(tp(left_id, &[]));
2355 let left = self.alloc(Ty(
2357 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2359 where_constraints.push(self.alloc(WhereConstraint(
2361 ConstraintKind::ConstraintEq,
2369 let params = self.slice(params.iter().copied().map(|param| match param.name {
2371 Some(name) => match ty_by_param.get(name) {
2372 Some((type_, _)) if param.type_.type_ != type_ => self.alloc(FunParam {
2373 type_: self.alloc(PossiblyEnforcedTy {
2374 type_: self.alloc(type_.clone()),
2383 let context_tys = self.slice(context_tys.iter().copied().map(|ty| {
2384 let ty_ = match ty.1 {
2385 Ty_::Tapply(((_, name), &[])) if name.starts_with('$') => {
2386 Ty_::Tgeneric(self.alloc((self.ctx_generic_for_fun(name), &[])))
2388 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), &[]))), cst))
2389 if name.starts_with('$') =>
2391 let name = self.ctx_generic_for_dependent(name, &cst.1);
2392 Ty_::Tgeneric(self.alloc((name, &[])))
2394 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2395 let name = self.ctx_generic_for_generic_taccess(t, &cst.1);
2396 Ty_::Tgeneric(self.alloc((name, &[])))
2400 self.alloc(Ty(ty.0, ty_))
2402 let cap_ty = match context_tys {
2404 _ => self.alloc(Ty(cap_reason, Ty_::Tintersection(context_tys))),
2406 let implicit_params = self.alloc(FunImplicitParams {
2407 capability: CapTy(cap_ty),
2412 tparams.into_bump_slice(),
2414 where_constraints.into_bump_slice(),
2419 enum NodeIterHelper<'a, 'b> {
2421 Single(&'b Node<'a>),
2422 Vec(std::slice::Iter<'b, Node<'a>>),
2425 impl<'a, 'b> Iterator for NodeIterHelper<'a, 'b> {
2426 type Item = &'b Node<'a>;
2428 fn next(&mut self) -> Option<Self::Item> {
2430 NodeIterHelper::Empty => None,
2431 NodeIterHelper::Single(node) => {
2433 *self = NodeIterHelper::Empty;
2436 NodeIterHelper::Vec(ref mut iter) => iter.next(),
2440 // Must return the upper bound returned by Node::len.
2441 fn size_hint(&self) -> (usize, Option<usize>) {
2443 NodeIterHelper::Empty => (0, Some(0)),
2444 NodeIterHelper::Single(_) => (1, Some(1)),
2445 NodeIterHelper::Vec(iter) => iter.size_hint(),
2450 impl<'a, 'b> DoubleEndedIterator for NodeIterHelper<'a, 'b> {
2451 fn next_back(&mut self) -> Option<Self::Item> {
2453 NodeIterHelper::Empty => None,
2454 NodeIterHelper::Single(_) => self.next(),
2455 NodeIterHelper::Vec(ref mut iter) => iter.next_back(),
2460 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> FlattenOp
2461 for DirectDeclSmartConstructors<'a, 'text, S>
2465 fn flatten(&self, kind: SyntaxKind, lst: std::vec::Vec<Self::S>) -> Self::S {
2469 Node::List(children) => children.len(),
2471 if Self::is_zero(x) {
2479 let mut r = Vec::with_capacity_in(size, self.arena);
2480 for s in lst.into_iter() {
2482 Node::List(children) => r.extend(children.iter().copied()),
2484 if !Self::is_zero(&x) {
2490 match r.into_bump_slice() {
2491 [] => Node::Ignored(kind),
2493 slice => Node::List(self.alloc(slice)),
2497 fn zero(kind: SyntaxKind) -> Self::S {
2501 fn is_zero(s: &Self::S) -> bool {
2503 Node::Token(token) => match token.kind() {
2504 TokenKind::Yield | TokenKind::Required | TokenKind::Lateinit => false,
2507 Node::List(inner) => inner.iter().all(Self::is_zero),
2513 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>>
2514 FlattenSmartConstructors<'a, DirectDeclSmartConstructors<'a, 'text, S>>
2515 for DirectDeclSmartConstructors<'a, 'text, S>
2517 fn make_token(&mut self, token: CompactToken) -> Self::R {
2518 let token_text = |this: &Self| this.str_from_utf8(this.token_bytes(&token));
2519 let token_pos = |this: &Self| {
2522 .offset_to_file_pos_triple(token.start_offset());
2525 .offset_to_file_pos_triple(token.end_offset());
2526 Pos::from_lnum_bol_offset(this.arena, this.filename, start, end)
2528 let kind = token.kind();
2530 let result = match kind {
2531 TokenKind::Name | TokenKind::XHPClassName => {
2532 let text = token_text(self);
2533 let pos = token_pos(self);
2535 let name = if kind == TokenKind::XHPClassName {
2536 Node::XhpName(self.alloc((text, pos)))
2538 Node::Name(self.alloc((text, pos)))
2541 if self.previous_token_kind == TokenKind::Class
2542 || self.previous_token_kind == TokenKind::Trait
2543 || self.previous_token_kind == TokenKind::Interface
2545 if let Some(current_class_name) = self.elaborate_defined_id(name) {
2546 self.classish_name_builder
2547 .lexed_name_after_classish_keyword(
2549 current_class_name.1,
2551 self.previous_token_kind,
2557 TokenKind::Class => Node::Name(self.alloc((token_text(self), token_pos(self)))),
2558 TokenKind::Variable => Node::Variable(self.alloc((token_text(self), token_pos(self)))),
2559 // There are a few types whose string representations we have to
2560 // grab anyway, so just go ahead and treat them as generic names.
2565 | TokenKind::Classname
2566 | TokenKind::SelfToken => Node::Name(self.alloc((token_text(self), token_pos(self)))),
2567 TokenKind::XHPElementName => {
2568 Node::XhpName(self.alloc((token_text(self), token_pos(self))))
2570 TokenKind::SingleQuotedStringLiteral => match escaper::unescape_single_in(
2571 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2574 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2575 Err(_) => Node::Ignored(SK::Token(kind)),
2577 TokenKind::DoubleQuotedStringLiteral => match escaper::unescape_double_in(
2578 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2581 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2582 Err(_) => Node::Ignored(SK::Token(kind)),
2584 TokenKind::HeredocStringLiteral => match escaper::unescape_heredoc_in(
2585 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2588 Ok(text) if !self.retain_or_omit_user_attributes_for_facts => {
2589 Node::StringLiteral(self.alloc((text.into(), token_pos(self))))
2591 _ => Node::Ignored(SK::Token(kind)),
2593 TokenKind::NowdocStringLiteral => match escaper::unescape_nowdoc_in(
2594 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2597 Ok(text) if !self.retain_or_omit_user_attributes_for_facts => {
2598 Node::StringLiteral(self.alloc((text.into(), token_pos(self))))
2600 _ => Node::Ignored(SK::Token(kind)),
2602 TokenKind::DecimalLiteral
2603 | TokenKind::OctalLiteral
2604 | TokenKind::HexadecimalLiteral
2605 | TokenKind::BinaryLiteral => {
2606 Node::IntLiteral(self.alloc((token_text(self), token_pos(self))))
2608 TokenKind::FloatingLiteral => {
2609 Node::FloatingLiteral(self.alloc((token_text(self), token_pos(self))))
2611 TokenKind::BooleanLiteral => {
2612 Node::BooleanLiteral(self.alloc((token_text(self), token_pos(self))))
2614 TokenKind::String => self.prim_ty(aast::Tprim::Tstring, token_pos(self)),
2615 TokenKind::Int => self.prim_ty(aast::Tprim::Tint, token_pos(self)),
2616 TokenKind::Float => self.prim_ty(aast::Tprim::Tfloat, token_pos(self)),
2617 // "double" and "boolean" are parse errors--they should be written
2618 // "float" and "bool". The decl-parser treats the incorrect names as
2619 // type names rather than primitives.
2620 TokenKind::Double | TokenKind::Boolean => self.hint_ty(
2622 Ty_::Tapply(self.alloc(((token_pos(self), token_text(self)), &[][..]))),
2624 TokenKind::Num => self.prim_ty(aast::Tprim::Tnum, token_pos(self)),
2625 TokenKind::Bool => self.prim_ty(aast::Tprim::Tbool, token_pos(self)),
2626 TokenKind::Mixed => {
2627 if self.opts.everything_sdt {
2628 Node::Ty(self.alloc(Ty(
2629 self.alloc(Reason::hint(token_pos(self))),
2630 Ty_::Toption(self.alloc(Ty(
2631 self.alloc(Reason::hint(token_pos(self))),
2632 self.make_supportdynamic(token_pos(self)),
2636 Node::Ty(self.alloc(Ty(self.alloc(Reason::hint(token_pos(self))), Ty_::Tmixed)))
2639 TokenKind::Void => self.prim_ty(aast::Tprim::Tvoid, token_pos(self)),
2640 TokenKind::Arraykey => self.prim_ty(aast::Tprim::Tarraykey, token_pos(self)),
2641 TokenKind::Noreturn => self.prim_ty(aast::Tprim::Tnoreturn, token_pos(self)),
2642 TokenKind::Resource => self.prim_ty(aast::Tprim::Tresource, token_pos(self)),
2643 TokenKind::NullLiteral
2646 | TokenKind::Backslash
2647 | TokenKind::Construct
2648 | TokenKind::LeftParen
2649 | TokenKind::RightParen
2650 | TokenKind::LeftBracket
2651 | TokenKind::RightBracket
2653 | TokenKind::Question
2656 | TokenKind::Exclamation
2659 | TokenKind::PlusPlus
2660 | TokenKind::MinusMinus
2664 | TokenKind::EqualEqual
2665 | TokenKind::EqualEqualEqual
2666 | TokenKind::StarStar
2667 | TokenKind::AmpersandAmpersand
2669 | TokenKind::LessThan
2670 | TokenKind::LessThanEqual
2671 | TokenKind::GreaterThan
2672 | TokenKind::GreaterThanEqual
2674 | TokenKind::Ampersand
2676 | TokenKind::LessThanLessThan
2677 | TokenKind::GreaterThanGreaterThan
2678 | TokenKind::Percent
2679 | TokenKind::QuestionQuestion
2681 | TokenKind::Abstract
2685 | TokenKind::DotDotDot
2686 | TokenKind::Extends
2688 | TokenKind::Implements
2690 | TokenKind::Interface
2692 | TokenKind::Newtype
2695 | TokenKind::Semicolon
2696 | TokenKind::Private
2697 | TokenKind::Protected
2702 | TokenKind::Lateinit
2703 | TokenKind::RightBrace
2706 | TokenKind::Function
2707 | TokenKind::Namespace
2709 | TokenKind::Required
2711 | TokenKind::Readonly => Node::Token(FixedWidthToken::new(kind, token.start_offset())),
2712 _ if kind.fixed_width().is_some() => {
2713 Node::IgnoredToken(FixedWidthToken::new(kind, token.start_offset()))
2715 _ => Node::Ignored(SK::Token(kind)),
2717 self.previous_token_kind = kind;
2721 fn make_error(&mut self, error: Self::R) -> Self::R {
2722 // If it's a Token or IgnoredToken, we can use it for error recovery.
2723 // For instance, in `function using() {}`, the `using` keyword will be a
2724 // token wrapped in an error CST node, since the keyword isn't legal in
2729 fn make_missing(&mut self, _: usize) -> Self::R {
2730 Node::Ignored(SK::Missing)
2733 fn make_list(&mut self, items: std::vec::Vec<Self::R>, _: usize) -> Self::R {
2734 if let Some(&yield_) = items
2736 .flat_map(|node| node.iter())
2737 .find(|node| node.is_token(TokenKind::Yield))
2741 let size = items.iter().filter(|node| node.is_present()).count();
2742 let items_iter = items.into_iter();
2743 let mut items = Vec::with_capacity_in(size, self.arena);
2744 for node in items_iter {
2745 if node.is_present() {
2749 let items = items.into_bump_slice();
2750 if items.is_empty() {
2751 Node::Ignored(SK::SyntaxList)
2753 Node::List(self.alloc(items))
2758 fn make_qualified_name(&mut self, parts: Self::R) -> Self::R {
2759 let pos = self.get_pos(parts);
2761 Node::List(nodes) => Node::QualifiedName(self.alloc((nodes, pos))),
2762 node if node.is_ignored() => Node::Ignored(SK::QualifiedName),
2763 node => Node::QualifiedName(
2764 self.alloc((bumpalo::vec![in self.arena; node].into_bump_slice(), pos)),
2769 fn make_simple_type_specifier(&mut self, specifier: Self::R) -> Self::R {
2770 // Return this explicitly because flatten filters out zero nodes, and
2771 // we treat most non-error nodes as zeroes.
2775 fn make_literal_expression(&mut self, expression: Self::R) -> Self::R {
2779 fn make_simple_initializer(&mut self, equals: Self::R, expr: Self::R) -> Self::R {
2780 // If the expr is Ignored, bubble up the assignment operator so that we
2781 // can tell that *some* initializer was here. Useful for class
2782 // properties, where we need to enforce that properties without default
2783 // values are initialized in the constructor.
2784 if expr.is_ignored() { equals } else { expr }
2787 fn make_anonymous_function(
2789 _attribute_spec: Self::R,
2790 _async_keyword: Self::R,
2791 _function_keyword: Self::R,
2792 _left_paren: Self::R,
2793 _parameters: Self::R,
2794 _right_paren: Self::R,
2797 _readonly_return: Self::R,
2802 // do not allow Yield to bubble up
2803 Node::Ignored(SK::AnonymousFunction)
2806 fn make_lambda_expression(
2808 _attribute_spec: Self::R,
2810 _signature: Self::R,
2814 // do not allow Yield to bubble up
2815 Node::Ignored(SK::LambdaExpression)
2818 fn make_awaitable_creation_expression(
2820 _attribute_spec: Self::R,
2822 _compound_statement: Self::R,
2824 // do not allow Yield to bubble up
2825 Node::Ignored(SK::AwaitableCreationExpression)
2828 fn make_element_initializer(
2834 Node::ListItem(self.alloc((key, value)))
2837 fn make_prefix_unary_expression(&mut self, op: Self::R, value: Self::R) -> Self::R {
2838 let pos = self.merge_positions(op, value);
2839 let op = match op.token_kind() {
2840 Some(TokenKind::Tilde) => Uop::Utild,
2841 Some(TokenKind::Exclamation) => Uop::Unot,
2842 Some(TokenKind::Plus) => Uop::Uplus,
2843 Some(TokenKind::Minus) => Uop::Uminus,
2844 Some(TokenKind::PlusPlus) => Uop::Uincr,
2845 Some(TokenKind::MinusMinus) => Uop::Udecr,
2846 Some(TokenKind::At) => Uop::Usilence,
2847 _ => return Node::Ignored(SK::PrefixUnaryExpression),
2849 let value = match self.node_to_expr(value) {
2850 Some(value) => value,
2851 None => return Node::Ignored(SK::PrefixUnaryExpression),
2853 Node::Expr(self.alloc(aast::Expr(
2856 aast::Expr_::Unop(self.alloc((op, value))),
2860 fn make_postfix_unary_expression(&mut self, value: Self::R, op: Self::R) -> Self::R {
2861 let pos = self.merge_positions(value, op);
2862 let op = match op.token_kind() {
2863 Some(TokenKind::PlusPlus) => Uop::Upincr,
2864 Some(TokenKind::MinusMinus) => Uop::Updecr,
2865 _ => return Node::Ignored(SK::PostfixUnaryExpression),
2867 let value = match self.node_to_expr(value) {
2868 Some(value) => value,
2869 None => return Node::Ignored(SK::PostfixUnaryExpression),
2871 Node::Expr(self.alloc(aast::Expr(
2874 aast::Expr_::Unop(self.alloc((op, value))),
2878 fn make_binary_expression(&mut self, lhs: Self::R, op_node: Self::R, rhs: Self::R) -> Self::R {
2879 let op = match op_node.token_kind() {
2880 Some(TokenKind::Plus) => Bop::Plus,
2881 Some(TokenKind::Minus) => Bop::Minus,
2882 Some(TokenKind::Star) => Bop::Star,
2883 Some(TokenKind::Slash) => Bop::Slash,
2884 Some(TokenKind::Equal) => Bop::Eq(None),
2885 Some(TokenKind::EqualEqual) => Bop::Eqeq,
2886 Some(TokenKind::EqualEqualEqual) => Bop::Eqeqeq,
2887 Some(TokenKind::StarStar) => Bop::Starstar,
2888 Some(TokenKind::AmpersandAmpersand) => Bop::Ampamp,
2889 Some(TokenKind::BarBar) => Bop::Barbar,
2890 Some(TokenKind::LessThan) => Bop::Lt,
2891 Some(TokenKind::LessThanEqual) => Bop::Lte,
2892 Some(TokenKind::LessThanLessThan) => Bop::Ltlt,
2893 Some(TokenKind::GreaterThan) => Bop::Gt,
2894 Some(TokenKind::GreaterThanEqual) => Bop::Gte,
2895 Some(TokenKind::GreaterThanGreaterThan) => Bop::Gtgt,
2896 Some(TokenKind::Dot) => Bop::Dot,
2897 Some(TokenKind::Ampersand) => Bop::Amp,
2898 Some(TokenKind::Bar) => Bop::Bar,
2899 Some(TokenKind::Percent) => Bop::Percent,
2900 Some(TokenKind::QuestionQuestion) => Bop::QuestionQuestion,
2901 _ => return Node::Ignored(SK::BinaryExpression),
2904 match (&op, rhs.is_token(TokenKind::Yield)) {
2905 (Bop::Eq(_), true) => return rhs,
2909 let pos = self.merge(self.merge_positions(lhs, op_node), self.get_pos(rhs));
2911 let lhs = match self.node_to_expr(lhs) {
2913 None => return Node::Ignored(SK::BinaryExpression),
2915 let rhs = match self.node_to_expr(rhs) {
2917 None => return Node::Ignored(SK::BinaryExpression),
2920 Node::Expr(self.alloc(aast::Expr(
2923 aast::Expr_::Binop(self.alloc((op, lhs, rhs))),
2927 fn make_parenthesized_expression(
2933 if self.retain_or_omit_user_attributes_for_facts {
2934 Node::Ignored(SK::ParenthesizedExpression)
2940 fn make_keyset_intrinsic_expression(
2942 _keyset_intrinsic_keyword: Self::R,
2944 _left_bracket: Self::R,
2946 _right_bracket: Self::R,
2948 if !self.retain_or_omit_user_attributes_for_facts {
2949 Node::Ignored(SK::KeysetIntrinsicExpression)
2950 } else if let Node::List([node]) = expr {
2957 fn make_varray_intrinsic_expression(
2959 _varray_intrinsic_keyword: Self::R,
2961 _left_bracket: Self::R,
2963 _right_bracket: Self::R,
2965 if !self.retain_or_omit_user_attributes_for_facts {
2966 Node::Ignored(SK::VarrayIntrinsicExpression)
2967 } else if let Node::List([node]) = expr {
2974 fn make_darray_intrinsic_expression(
2976 _varray_intrinsic_keyword: Self::R,
2978 _left_bracket: Self::R,
2980 _right_bracket: Self::R,
2982 if !self.retain_or_omit_user_attributes_for_facts {
2983 Node::Ignored(SK::DarrayIntrinsicExpression)
2984 } else if let Node::List([node]) = expr {
2991 fn make_vector_intrinsic_expression(
2993 _vector_intrinsic_keyword: Self::R,
2995 _left_bracket: Self::R,
2997 _right_bracket: Self::R,
2999 if !self.retain_or_omit_user_attributes_for_facts {
3000 Node::Ignored(SK::VectorIntrinsicExpression)
3001 } else if let Node::List([node]) = expr {
3008 fn make_list_item(&mut self, item: Self::R, sep: Self::R) -> Self::R {
3009 match (item.is_ignored(), sep.is_ignored()) {
3010 (true, true) => Node::Ignored(SK::ListItem),
3011 (false, true) => item,
3012 (true, false) => sep,
3013 (false, false) => Node::ListItem(self.alloc((item, sep))),
3017 fn make_type_arguments(
3021 greater_than: Self::R,
3023 Node::BracketedList(self.alloc((
3024 self.get_pos(less_than),
3025 arguments.as_slice(self.arena),
3026 self.get_pos(greater_than),
3030 fn make_generic_type_specifier(
3032 class_type: Self::R,
3033 type_arguments: Self::R,
3035 let class_id = match self.expect_name(class_type) {
3037 None => return Node::Ignored(SK::GenericTypeSpecifier),
3039 match class_id.1.trim_start_matches("\\") {
3040 "varray_or_darray" | "vec_or_dict" => {
3041 let id_pos = class_id.0;
3042 let pos = self.merge(id_pos, self.get_pos(type_arguments));
3043 let type_arguments = type_arguments.as_slice(self.arena);
3044 let ty_ = match type_arguments {
3045 [tk, tv] => Ty_::TvecOrDict(
3047 self.node_to_ty(*tk)
3048 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
3049 self.node_to_ty(*tv)
3050 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
3053 [tv] => Ty_::TvecOrDict(
3055 self.vec_or_dict_key(pos),
3056 self.node_to_ty(*tv)
3057 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
3062 self.hint_ty(pos, ty_)
3065 let Id(pos, class_type) = class_id;
3066 match class_type.rsplit('\\').next() {
3067 Some(name) if self.is_type_param_in_scope(name) => {
3068 let pos = self.merge(pos, self.get_pos(type_arguments));
3069 let type_arguments = self.slice(
3072 .filter_map(|&node| self.node_to_ty(node)),
3074 let ty_ = if self.simplify_naming_for_facts {
3075 Ty_::Tgeneric(self.alloc((class_type, type_arguments)))
3077 Ty_::Tgeneric(self.alloc((name, type_arguments)))
3079 self.hint_ty(pos, ty_)
3082 let class_type = self.elaborate_raw_id(class_type);
3086 self.get_pos(type_arguments),
3094 fn make_alias_declaration(
3096 attributes: Self::R,
3099 generic_params: Self::R,
3100 constraint: Self::R,
3102 aliased_type: Self::R,
3103 _semicolon: Self::R,
3105 if name.is_ignored() {
3106 return Node::Ignored(SK::AliasDeclaration);
3108 let Id(pos, name) = match self.elaborate_defined_id(name) {
3110 None => return Node::Ignored(SK::AliasDeclaration),
3112 let ty = match self.node_to_ty(aliased_type) {
3114 None => return Node::Ignored(SK::AliasDeclaration),
3116 let constraint = match constraint {
3117 Node::TypeConstraint(&(_kind, hint)) => self.node_to_ty(hint),
3121 // Pop the type params stack only after creating all inner types.
3122 let tparams = self.pop_type_params(generic_params);
3123 let parsed_attributes = self.to_attributes(attributes);
3124 let user_attributes = if self.retain_or_omit_user_attributes_for_facts {
3125 self.slice(attributes.iter().rev().filter_map(|attribute| {
3126 if let Node::Attribute(attr) = attribute {
3127 Some(self.user_attribute_to_decl(attr))
3135 let typedef = self.alloc(TypedefType {
3136 module: parsed_attributes.module,
3138 vis: if parsed_attributes.internal {
3139 aast::TypedefVisibility::Tinternal
3141 match keyword.token_kind() {
3142 Some(TokenKind::Type) => aast::TypedefVisibility::Transparent,
3143 Some(TokenKind::Newtype) => aast::TypedefVisibility::Opaque,
3144 _ => aast::TypedefVisibility::Transparent,
3151 attributes: user_attributes,
3154 self.add_typedef(name, typedef);
3156 Node::Ignored(SK::AliasDeclaration)
3159 fn make_context_alias_declaration(
3161 attributes: Self::R,
3164 generic_params: Self::R,
3165 constraint: Self::R,
3168 _semicolon: Self::R,
3170 if name.is_ignored() {
3171 return Node::Ignored(SK::ContextAliasDeclaration);
3173 let Id(pos, name) = match self.elaborate_defined_id(name) {
3175 None => return Node::Ignored(SK::ContextAliasDeclaration),
3177 let ty = match self.node_to_ty(ctx_list) {
3179 None => self.alloc(Ty(
3180 self.alloc(Reason::hint(pos)),
3181 Ty_::Tapply(self.alloc(((pos, "\\HH\\Contexts\\defaults"), &[]))),
3185 // lowerer ensures there is only one as constraint
3186 let mut as_constraint = None;
3187 for c in constraint.iter() {
3188 if let Node::ContextConstraint(&(kind, hint)) = c {
3189 let ty = self.node_to_ty(hint);
3191 ConstraintKind::ConstraintAs => as_constraint = ty,
3196 // Pop the type params stack only after creating all inner types.
3197 let tparams = self.pop_type_params(generic_params);
3198 let parsed_attributes = self.to_attributes(attributes);
3199 let user_attributes = if self.retain_or_omit_user_attributes_for_facts {
3200 self.slice(attributes.iter().rev().filter_map(|attribute| {
3201 if let Node::Attribute(attr) = attribute {
3202 Some(self.user_attribute_to_decl(attr))
3210 let typedef = self.alloc(TypedefType {
3211 module: parsed_attributes.module,
3213 vis: if parsed_attributes.internal {
3214 aast::TypedefVisibility::Tinternal
3216 aast::TypedefVisibility::Opaque
3219 constraint: as_constraint,
3222 attributes: user_attributes,
3225 self.add_typedef(name, typedef);
3227 Node::Ignored(SK::ContextAliasDeclaration)
3230 fn make_type_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3231 let kind = match kind.token_kind() {
3232 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3233 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3234 _ => return Node::Ignored(SK::TypeConstraint),
3236 Node::TypeConstraint(self.alloc((kind, value)))
3239 fn make_context_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3240 let kind = match kind.token_kind() {
3241 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3242 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3243 _ => return Node::Ignored(SK::ContextConstraint),
3245 Node::ContextConstraint(self.alloc((kind, value)))
3248 fn make_type_parameter(
3250 user_attributes: Self::R,
3254 tparam_params: Self::R,
3255 constraints: Self::R,
3257 let user_attributes = match user_attributes {
3258 Node::BracketedList((_, attributes, _)) => {
3259 self.slice(attributes.into_iter().filter_map(|x| match x {
3260 Node::Attribute(a) => Some(*a),
3267 let constraints = self.slice(constraints.iter().filter_map(|node| match node {
3268 Node::TypeConstraint(&constraint) => Some(constraint),
3272 // TODO(T70068435) Once we add support for constraints on higher-kinded types
3273 // (in particular, constraints on nested type parameters), we need to ensure
3274 // that we correctly handle the scoping of nested type parameters.
3275 // This includes making sure that the call to convert_type_appl_to_generic
3276 // in make_type_parameters handles nested constraints.
3277 // For now, we just make sure that the nested type parameters that make_type_parameters
3278 // added to the global list of in-scope type parameters are removed immediately:
3279 self.pop_type_params(tparam_params);
3281 let tparam_params = match tparam_params {
3282 Node::TypeParameters(¶ms) => params,
3286 Node::TypeParameter(self.alloc(TypeParameterDecl {
3288 variance: match variance.token_kind() {
3289 Some(TokenKind::Minus) => Variance::Contravariant,
3290 Some(TokenKind::Plus) => Variance::Covariant,
3291 _ => Variance::Invariant,
3293 reified: if reify.is_token(TokenKind::Reify) {
3294 if user_attributes.iter().any(|node| node.name.1 == "__Soft") {
3295 aast::ReifyKind::SoftReified
3297 aast::ReifyKind::Reified
3300 aast::ReifyKind::Erased
3308 fn make_type_parameters(&mut self, _lt: Self::R, tparams: Self::R, _gt: Self::R) -> Self::R {
3309 let size = tparams.len();
3310 let mut tparams_with_name = Vec::with_capacity_in(size, self.arena);
3311 let mut tparam_names = MultiSetMut::with_capacity_in(size, self.arena);
3312 for node in tparams.iter() {
3314 &Node::TypeParameter(decl) => {
3315 let name = match decl.name.as_id() {
3317 None => return Node::Ignored(SK::TypeParameters),
3319 tparam_names.insert(name.1);
3320 tparams_with_name.push((decl, name));
3325 Rc::make_mut(&mut self.type_parameters).push(tparam_names.into());
3326 let mut tparams = Vec::with_capacity_in(tparams_with_name.len(), self.arena);
3327 for (decl, name) in tparams_with_name.into_iter() {
3328 let &TypeParameterDecl {
3336 let constraints = self.slice(constraints.iter().filter_map(|constraint| {
3337 let &(kind, ty) = constraint;
3338 let ty = self.node_to_ty(ty)?;
3339 let ty = self.convert_tapply_to_tgeneric(ty);
3343 let user_attributes = self.slice(
3347 .map(|x| self.user_attribute_to_decl(x)),
3349 tparams.push(self.alloc(Tparam {
3355 tparams: tparam_params,
3358 Node::TypeParameters(self.alloc(tparams.into_bump_slice()))
3361 fn make_parameter_declaration(
3363 attributes: Self::R,
3364 visibility: Self::R,
3369 initializer: Self::R,
3371 let (variadic, pos, name) = match name {
3372 Node::ListItem(&(ellipsis, id)) => {
3373 let Id(pos, name) = match id.as_variable() {
3375 None => return Node::Ignored(SK::ParameterDeclaration),
3377 let variadic = ellipsis.is_token(TokenKind::DotDotDot);
3378 (variadic, pos, Some(name))
3381 let Id(pos, name) = match name.as_variable() {
3383 None => return Node::Ignored(SK::ParameterDeclaration),
3385 (false, pos, Some(name))
3388 let kind = if inout.is_token(TokenKind::Inout) {
3393 let is_readonly = readonly.is_token(TokenKind::Readonly);
3394 let hint = if self.opts.interpret_soft_types_as_like_types {
3395 let attributes = self.to_attributes(attributes);
3396 if attributes.soft {
3398 Node::Ty(ty) => self.hint_ty(self.get_pos(hint), Ty_::Tlike(ty)),
3407 Node::FunParam(self.alloc(FunParamDecl {
3411 readonly: is_readonly,
3420 fn make_variadic_parameter(&mut self, _: Self::R, hint: Self::R, ellipsis: Self::R) -> Self::R {
3422 self.alloc(FunParamDecl {
3423 attributes: Node::Ignored(SK::Missing),
3424 visibility: Node::Ignored(SK::Missing),
3425 kind: ParamMode::FPnormal,
3430 .unwrap_or_else(|| self.get_pos(ellipsis)),
3433 initializer: Node::Ignored(SK::Missing),
3438 fn make_function_declaration(
3440 attributes: Self::R,
3444 let parsed_attributes = self.to_attributes(attributes);
3446 Node::FunctionHeader(header) => {
3447 let is_method = false;
3448 let ((pos, name), type_, _) =
3449 match self.function_to_ty(is_method, attributes, header, body) {
3451 None => return Node::Ignored(SK::FunctionDeclaration),
3453 let deprecated = parsed_attributes.deprecated.map(|msg| {
3454 let mut s = String::new_in(self.arena);
3455 s.push_str("The function ");
3456 s.push_str(name.trim_start_matches("\\"));
3457 s.push_str(" is deprecated: ");
3461 let fun_elt = self.alloc(FunElt {
3462 module: parsed_attributes.module,
3463 internal: parsed_attributes.internal,
3467 php_std_lib: parsed_attributes.php_std_lib,
3468 support_dynamic_type: self.opts.everything_sdt
3469 || parsed_attributes.support_dynamic_type,
3471 self.add_fun(name, fun_elt);
3472 Node::Ignored(SK::FunctionDeclaration)
3474 _ => Node::Ignored(SK::FunctionDeclaration),
3480 left_bracket: Self::R,
3482 right_bracket: Self::R,
3484 let tys = self.slice(tys.iter().filter_map(|ty| match ty {
3485 Node::ListItem(&(ty, _)) | &ty => {
3486 // A wildcard is used for the context of a closure type on a
3487 // parameter of a function with a function context (e.g.,
3488 // `function f((function ()[_]: void) $f)[ctx $f]: void {}`).
3489 if let Some(Id(pos, "_")) = self.expect_name(ty) {
3490 return Some(self.alloc(Ty(
3491 self.alloc(Reason::hint(pos)),
3492 Ty_::Tapply(self.alloc(((pos, "_"), &[]))),
3495 let ty = self.node_to_ty(ty)?;
3497 // Only three forms of type can appear here in a valid program:
3498 // - function contexts (`ctx $f`)
3499 // - value-dependent paths (`$v::C`)
3500 // - built-in contexts (`rx`, `cipp_of<EntFoo>`)
3501 // The first and last will be represented with `Tapply`,
3502 // but function contexts will use a variable name
3503 // (containing a `$`). Built-in contexts are always in the
3504 // \HH\Contexts namespace, so we rewrite those names here.
3505 Ty_::Tapply(&((pos, name), targs)) if !name.starts_with('$') => {
3506 // The name will have been elaborated in the current
3507 // namespace, but we actually want it to be in the
3508 // \HH\Contexts namespace. Grab the last component of
3509 // the name, and rewrite it in the correct namespace.
3510 // Note that this makes it impossible to express names
3511 // in any sub-namespace of \HH\Contexts (e.g.,
3512 // "Unsafe\\cipp" will be rewritten as
3513 // "\\HH\\Contexts\\cipp" rather than
3514 // "\\HH\\Contexts\\Unsafe\\cipp").
3515 let name = match name.trim_end_matches('\\').split('\\').next_back() {
3517 if let Some(first_char) = ctxname.chars().nth(0) {
3518 if first_char.is_lowercase() {
3519 self.concat("\\HH\\Contexts\\", ctxname)
3529 Some(self.alloc(Ty(ty.0, Ty_::Tapply(self.alloc(((pos, name), targs))))))
3535 /* Like in as_fun_implicit_params, we keep the intersection as is: we do not simplify
3536 * empty or singleton intersections.
3538 let pos = self.merge_positions(left_bracket, right_bracket);
3539 self.hint_ty(pos, Ty_::Tintersection(tys))
3542 fn make_function_ctx_type_specifier(
3544 ctx_keyword: Self::R,
3547 match variable.as_variable() {
3548 Some(Id(pos, name)) => {
3549 Node::Variable(self.alloc((name, self.merge(pos, self.get_pos(ctx_keyword)))))
3551 None => Node::Ignored(SK::FunctionCtxTypeSpecifier),
3555 fn make_function_declaration_header(
3560 type_params: Self::R,
3561 left_paren: Self::R,
3562 param_list: Self::R,
3563 _right_paren: Self::R,
3564 capability: Self::R,
3566 readonly_return: Self::R,
3568 where_constraints: Self::R,
3570 // Use the position of the left paren if the name is missing.
3571 // Keep the name if it's an IgnoredToken rather than an Ignored. An
3572 // IgnoredToken here should always be an error, but it's better to treat
3573 // a keyword as a name than to claim the function has no name at all.
3574 let name = if matches!(name, Node::Ignored(..)) {
3579 Node::FunctionHeader(self.alloc(FunctionHeader {
3591 fn make_yield_expression(&mut self, keyword: Self::R, _operand: Self::R) -> Self::R {
3592 assert!(keyword.token_kind() == Some(TokenKind::Yield));
3596 fn make_const_declaration(
3598 _attributes: Self::R,
3600 const_keyword: Self::R,
3609 .classish_name_builder
3610 .get_current_classish_name()
3613 let ty = self.node_to_ty(hint);
3615 self.alloc(self.slice(consts.iter().filter_map(|cst| match cst {
3616 Node::ConstInitializer(&(name, initializer, refs)) => {
3617 let id = name.as_id()?;
3618 let modifiers = read_member_modifiers(modifiers.iter());
3619 let abstract_ = if modifiers.is_abstract {
3620 ClassConstKind::CCAbstract(!initializer.is_ignored())
3622 ClassConstKind::CCConcrete
3625 .or_else(|| self.infer_const(name, initializer))
3626 .unwrap_or_else(|| tany());
3627 Some(Node::Const(self.alloc(
3628 shallow_decl_defs::ShallowClassConst {
3641 Node::List(consts) => {
3642 // This case always returns Node::Ignored,
3643 // but has the side effect of calling self.add_const
3645 // Note: given "const int X=1,Y=2;", the legacy decl-parser
3646 // allows both decls, and it gives them both an identical text-span -
3647 // from start of "const" to end of semicolon. This is a bug but
3648 // the code here preserves it.
3649 let pos = self.merge_positions(const_keyword, semicolon);
3650 for cst in consts.iter() {
3652 Node::ConstInitializer(&(name, initializer, _refs)) => {
3653 if let Some(Id(id_pos, id)) = self.elaborate_defined_id(name) {
3656 .or_else(|| self.infer_const(name, initializer))
3657 .unwrap_or_else(|| self.tany_with_pos(id_pos));
3658 self.add_const(id, self.alloc(ConstDecl { pos, type_: ty }));
3664 Node::Ignored(SK::ConstDeclaration)
3666 _ => Node::Ignored(SK::ConstDeclaration),
3670 fn begin_constant_declarator(&mut self) {
3671 self.start_accumulating_const_refs();
3674 fn make_constant_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
3675 // The "X=1" part of either a member const "class C {const int X=1;}" or a top-level const "const int X=1;"
3676 // Note: the the declarator itself doesn't yet know whether a type was provided by the user;
3677 // that's only known in the parent, make_const_declaration
3678 let refs = self.stop_accumulating_const_refs();
3679 if name.is_ignored() {
3680 Node::Ignored(SK::ConstantDeclarator)
3682 Node::ConstInitializer(self.alloc((name, initializer, refs)))
3686 fn make_namespace_declaration(&mut self, _name: Self::R, body: Self::R) -> Self::R {
3687 if let Node::Ignored(SK::NamespaceBody) = body {
3688 Rc::make_mut(&mut self.namespace_builder).pop_namespace();
3690 Node::Ignored(SK::NamespaceDeclaration)
3693 fn make_namespace_declaration_header(&mut self, _keyword: Self::R, name: Self::R) -> Self::R {
3694 let name = self.expect_name(name).map(|Id(_, name)| name);
3695 // if this is header of semicolon-style (one with NamespaceEmptyBody) namespace, we should pop
3696 // the previous namespace first, but we don't have the body yet. We'll fix it retroactively in
3697 // make_namespace_empty_body
3698 Rc::make_mut(&mut self.namespace_builder).push_namespace(name);
3699 Node::Ignored(SK::NamespaceDeclarationHeader)
3702 fn make_namespace_body(
3704 _left_brace: Self::R,
3705 _declarations: Self::R,
3706 _right_brace: Self::R,
3708 Node::Ignored(SK::NamespaceBody)
3711 fn make_namespace_empty_body(&mut self, _semicolon: Self::R) -> Self::R {
3712 Rc::make_mut(&mut self.namespace_builder).pop_previous_namespace();
3713 Node::Ignored(SK::NamespaceEmptyBody)
3716 fn make_namespace_use_declaration(
3719 namespace_use_kind: Self::R,
3721 _semicolon: Self::R,
3723 if let Some(import_kind) = Self::namespace_use_kind(&namespace_use_kind) {
3724 for clause in clauses.iter() {
3725 if let Node::NamespaceUseClause(nuc) = clause {
3726 Rc::make_mut(&mut self.namespace_builder).add_import(
3734 Node::Ignored(SK::NamespaceUseDeclaration)
3737 fn make_namespace_group_use_declaration(
3742 _left_brace: Self::R,
3744 _right_brace: Self::R,
3745 _semicolon: Self::R,
3747 let Id(_, prefix) = match self.expect_name(prefix) {
3749 None => return Node::Ignored(SK::NamespaceGroupUseDeclaration),
3751 for clause in clauses.iter() {
3752 if let Node::NamespaceUseClause(nuc) = clause {
3753 let mut id = String::new_in(self.arena);
3754 id.push_str(prefix);
3755 id.push_str(nuc.id.1);
3756 Rc::make_mut(&mut self.namespace_builder).add_import(
3763 Node::Ignored(SK::NamespaceGroupUseDeclaration)
3766 fn make_namespace_use_clause(
3768 clause_kind: Self::R,
3771 aliased_name: Self::R,
3773 let id = match self.expect_name(name) {
3775 None => return Node::Ignored(SK::NamespaceUseClause),
3777 let as_ = if as_.is_token(TokenKind::As) {
3778 match aliased_name.as_id() {
3779 Some(name) => Some(name.1),
3780 None => return Node::Ignored(SK::NamespaceUseClause),
3785 if let Some(kind) = Self::namespace_use_kind(&clause_kind) {
3786 Node::NamespaceUseClause(self.alloc(NamespaceUseClause { kind, id, as_ }))
3788 Node::Ignored(SK::NamespaceUseClause)
3792 fn make_where_clause(&mut self, _: Self::R, where_constraints: Self::R) -> Self::R {
3796 fn make_where_constraint(
3800 right_type: Self::R,
3802 Node::WhereConstraint(self.alloc(WhereConstraint(
3803 self.node_to_ty(left_type).unwrap_or_else(|| tany()),
3804 match operator.token_kind() {
3805 Some(TokenKind::Equal) => ConstraintKind::ConstraintEq,
3806 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3807 _ => ConstraintKind::ConstraintAs,
3809 self.node_to_ty(right_type).unwrap_or_else(|| tany()),
3813 fn make_classish_declaration(
3815 attributes: Self::R,
3817 xhp_keyword: Self::R,
3818 class_keyword: Self::R,
3821 _extends_keyword: Self::R,
3823 _implements_keyword: Self::R,
3824 implements: Self::R,
3825 where_clause: Self::R,
3828 let raw_name = match self.expect_name(name) {
3829 Some(Id(_, name)) => name,
3830 None => return Node::Ignored(SK::ClassishDeclaration),
3832 let Id(pos, name) = match self.elaborate_defined_id(name) {
3834 None => return Node::Ignored(SK::ClassishDeclaration),
3836 let is_xhp = raw_name.starts_with(':') || xhp_keyword.is_present();
3838 let mut final_ = false;
3839 let mut abstract_ = false;
3841 for modifier in modifiers.iter() {
3842 match modifier.token_kind() {
3843 Some(TokenKind::Abstract) => {
3846 Some(TokenKind::Final) => final_ = true,
3850 let class_kind = match class_keyword.token_kind() {
3851 Some(TokenKind::Interface) => ClassishKind::Cinterface,
3852 Some(TokenKind::Trait) => ClassishKind::Ctrait,
3853 _ => ClassishKind::Cclass(if abstract_ {
3854 &Abstraction::Abstract
3856 &Abstraction::Concrete
3860 let where_constraints = self.slice(where_clause.iter().filter_map(|&x| match x {
3861 Node::WhereConstraint(x) => Some(x),
3865 let body = match body {
3866 Node::ClassishBody(body) => body,
3867 _ => return Node::Ignored(SK::ClassishDeclaration),
3870 let mut uses_len = 0;
3871 let mut xhp_attr_uses_len = 0;
3872 let mut xhp_enum_values = SMap::empty();
3873 let mut req_extends_len = 0;
3874 let mut req_implements_len = 0;
3875 let mut consts_len = 0;
3876 let mut typeconsts_len = 0;
3877 let mut props_len = 0;
3878 let mut sprops_len = 0;
3879 let mut static_methods_len = 0;
3880 let mut methods_len = 0;
3882 let mut user_attributes_len = 0;
3883 for attribute in attributes.iter() {
3885 &Node::Attribute(..) => user_attributes_len += 1,
3890 for element in body.iter().copied() {
3892 Node::TraitUse(names) => uses_len += names.len(),
3893 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
3895 xhp_attr_uses_decls,
3896 xhp_attr_enum_values,
3898 props_len += xhp_attr_decls.len();
3899 xhp_attr_uses_len += xhp_attr_uses_decls.len();
3901 for (name, values) in xhp_attr_enum_values {
3902 xhp_enum_values = xhp_enum_values.add(self.arena, name, *values);
3905 Node::TypeConstant(..) => typeconsts_len += 1,
3906 Node::RequireClause(require) => match require.require_type.token_kind() {
3907 Some(TokenKind::Extends) => req_extends_len += 1,
3908 Some(TokenKind::Implements) => req_implements_len += 1,
3911 Node::List(consts @ [Node::Const(..), ..]) => consts_len += consts.len(),
3912 Node::Property(&PropertyNode { decls, is_static }) => {
3914 sprops_len += decls.len()
3916 props_len += decls.len()
3919 Node::Constructor(&ConstructorNode { properties, .. }) => {
3920 props_len += properties.len()
3922 Node::Method(&MethodNode { is_static, .. }) => {
3924 static_methods_len += 1
3933 let mut constructor = None;
3935 let mut uses = Vec::with_capacity_in(uses_len, self.arena);
3936 let mut xhp_attr_uses = Vec::with_capacity_in(xhp_attr_uses_len, self.arena);
3937 let mut req_extends = Vec::with_capacity_in(req_extends_len, self.arena);
3938 let mut req_implements = Vec::with_capacity_in(req_implements_len, self.arena);
3939 let mut consts = Vec::with_capacity_in(consts_len, self.arena);
3940 let mut typeconsts = Vec::with_capacity_in(typeconsts_len, self.arena);
3941 let mut props = Vec::with_capacity_in(props_len, self.arena);
3942 let mut sprops = Vec::with_capacity_in(sprops_len, self.arena);
3943 let mut static_methods = Vec::with_capacity_in(static_methods_len, self.arena);
3944 let mut methods = Vec::with_capacity_in(methods_len, self.arena);
3946 let mut user_attributes = Vec::with_capacity_in(user_attributes_len, self.arena);
3947 for attribute in attributes.iter() {
3949 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(&attr)),
3953 // Match ordering of attributes produced by the OCaml decl parser (even
3954 // though it's the reverse of the syntactic ordering).
3955 user_attributes.reverse();
3957 let class_attributes = self.to_attributes(attributes);
3959 // xhp props go after regular props, regardless of their order in file
3960 let mut xhp_props = vec![];
3962 for element in body.iter().copied() {
3964 Node::TraitUse(names) => {
3965 uses.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
3967 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
3969 xhp_attr_uses_decls,
3972 xhp_props.extend(xhp_attr_decls);
3973 xhp_attr_uses.extend(
3976 .filter_map(|&node| self.node_to_ty(node)),
3979 Node::TypeConstant(constant) => typeconsts.push(constant),
3980 Node::RequireClause(require) => match require.require_type.token_kind() {
3981 Some(TokenKind::Extends) => {
3982 req_extends.extend(self.node_to_ty(require.name).iter())
3984 Some(TokenKind::Implements) => {
3985 req_implements.extend(self.node_to_ty(require.name).iter())
3989 Node::List(&const_nodes @ [Node::Const(..), ..]) => {
3990 for node in const_nodes {
3991 if let &Node::Const(decl) = node {
3996 Node::Property(&PropertyNode { decls, is_static }) => {
3997 for property in decls {
3999 sprops.push(property)
4001 props.push(property)
4005 Node::Constructor(&ConstructorNode { method, properties }) => {
4006 constructor = Some(method);
4007 for property in properties {
4008 props.push(property)
4011 Node::Method(&MethodNode { method, is_static }) => {
4012 // Annoyingly, the <<__SupportDynamicType>> annotation on a
4013 // class implicitly changes the decls of every method inside
4014 // it, so we have to reallocate them here.
4015 let method = if class_attributes.support_dynamic_type
4016 && !method.flags.contains(MethodFlags::SUPPORT_DYNAMIC_TYPE)
4018 let type_ = match method.type_.1 {
4020 let flags = ft.flags | FunTypeFlags::SUPPORT_DYNAMIC_TYPE;
4021 let ft = self.alloc(FunType { flags, ..*ft });
4022 self.alloc(Ty(method.type_.0, Ty_::Tfun(ft)))
4026 let flags = method.flags | MethodFlags::SUPPORT_DYNAMIC_TYPE;
4027 self.alloc(ShallowMethod {
4036 static_methods.push(method);
4038 methods.push(method);
4041 _ => {} // It's not our job to report errors here.
4045 props.extend(xhp_props.into_iter());
4047 if class_attributes.const_ {
4048 for prop in props.iter_mut() {
4049 if !prop.flags.contains(PropFlags::CONST) {
4050 *prop = self.alloc(ShallowProp {
4051 flags: prop.flags | PropFlags::CONST,
4058 let uses = uses.into_bump_slice();
4059 let xhp_attr_uses = xhp_attr_uses.into_bump_slice();
4060 let req_extends = req_extends.into_bump_slice();
4061 let req_implements = req_implements.into_bump_slice();
4062 let consts = consts.into_bump_slice();
4063 let typeconsts = typeconsts.into_bump_slice();
4064 let props = props.into_bump_slice();
4065 let sprops = sprops.into_bump_slice();
4066 let static_methods = static_methods.into_bump_slice();
4067 let methods = methods.into_bump_slice();
4068 let user_attributes = user_attributes.into_bump_slice();
4069 let extends = self.slice(extends.iter().filter_map(|&node| self.node_to_ty(node)));
4070 let implements = self.slice(implements.iter().filter_map(|&node| self.node_to_ty(node)));
4071 let support_dynamic_type =
4072 self.opts.everything_sdt || class_attributes.support_dynamic_type;
4073 // Pop the type params stack only after creating all inner types.
4074 let tparams = self.pop_type_params(tparams);
4075 let module = class_attributes.module;
4077 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4078 mode: self.file_mode,
4082 has_xhp_keyword: xhp_keyword.is_token(TokenKind::XHP),
4095 support_dynamic_type,
4106 self.add_class(name, cls);
4108 self.classish_name_builder.parsed_classish_declaration();
4110 Node::Ignored(SK::ClassishDeclaration)
4113 fn make_property_declaration(
4118 declarators: Self::R,
4119 _semicolon: Self::R,
4121 let (attrs, modifiers, hint) = (attrs, modifiers, hint);
4122 let modifiers = read_member_modifiers(modifiers.iter());
4123 let declarators = self.slice(declarators.iter().filter_map(
4124 |declarator| match declarator {
4125 Node::ListItem(&(name, initializer)) => {
4126 let attributes = self.to_attributes(attrs);
4127 let Id(pos, name) = name.as_variable()?;
4128 let name = if modifiers.is_static {
4131 strip_dollar_prefix(name)
4133 let ty = self.rewrite_ty_for_global_inference(
4134 self.node_to_non_ret_ty(hint),
4135 Reason::RglobalClassProp(pos),
4137 let ty = if self.opts.interpret_soft_types_as_like_types {
4138 if attributes.soft {
4141 self.alloc(Reason::hint(self.get_pos(hint))),
4151 let needs_init = if self.file_mode == Mode::Mhhi {
4154 initializer.is_ignored()
4156 let mut flags = PropFlags::empty();
4157 flags.set(PropFlags::CONST, attributes.const_);
4158 flags.set(PropFlags::LATEINIT, attributes.late_init);
4159 flags.set(PropFlags::LSB, attributes.lsb);
4160 flags.set(PropFlags::NEEDS_INIT, needs_init);
4161 flags.set(PropFlags::ABSTRACT, modifiers.is_abstract);
4162 flags.set(PropFlags::READONLY, modifiers.is_readonly);
4163 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
4168 visibility: if attributes.internal
4169 && modifiers.visibility == aast::Visibility::Public
4171 aast::Visibility::Internal
4173 modifiers.visibility
4181 Node::Property(self.alloc(PropertyNode {
4183 is_static: modifiers.is_static,
4187 fn make_xhp_class_attribute_declaration(
4190 attributes: Self::R,
4191 _semicolon: Self::R,
4193 let mut xhp_attr_enum_values = Vec::new_in(self.arena);
4195 let xhp_attr_decls = self.slice(attributes.iter().filter_map(|node| {
4196 let node = match node {
4197 Node::XhpClassAttribute(x) => x,
4200 let Id(pos, name) = node.name;
4201 let name = prefix_colon(self.arena, name);
4203 let (like, type_, enum_values) = match node.hint {
4204 Node::XhpEnumTy((like, ty, values)) => (*like, Some(*ty), Some(values)),
4205 _ => (None, self.node_to_ty(node.hint), None),
4207 if let Some(enum_values) = enum_values {
4208 xhp_attr_enum_values.push((name, *enum_values));
4211 let type_ = if node.nullable && node.tag.is_none() {
4212 type_.and_then(|x| match x {
4214 Ty(_, Ty_::Toption(_)) | Ty(_, Ty_::Tmixed) => type_,
4216 _ => self.node_to_ty(self.hint_ty(x.get_pos()?, Ty_::Toption(x))),
4221 let type_ = type_.map(|t| match like {
4222 Some(p) => self.alloc(Ty(self.alloc(Reason::hint(p)), Ty_::Tlike(t))),
4226 let mut flags = PropFlags::empty();
4227 flags.set(PropFlags::NEEDS_INIT, node.needs_init);
4230 visibility: aast::Visibility::Public,
4232 xhp_attr: Some(xhp_attribute::XhpAttribute {
4234 has_default: !node.needs_init,
4240 let xhp_attr_uses_decls = self.slice(attributes.iter().filter_map(|x| match x {
4241 Node::XhpAttributeUse(&name) => Some(name),
4245 Node::XhpClassAttributeDeclaration(self.alloc(XhpClassAttributeDeclarationNode {
4246 xhp_attr_enum_values: xhp_attr_enum_values.into_bump_slice(),
4248 xhp_attr_uses_decls,
4252 /// Handle XHP attribute enum declarations.
4254 /// class :foo implements XHPChild {
4256 /// enum {'big', 'small'} size; // this line
4258 fn make_xhp_enum_type(
4261 enum_keyword: Self::R,
4262 _left_brace: Self::R,
4263 xhp_enum_values: Self::R,
4264 right_brace: Self::R,
4266 // Infer the type hint from the first value.
4267 // TODO: T88207956 consider all the values.
4268 let ty = xhp_enum_values
4271 .and_then(|node| self.node_to_ty(*node))
4272 .and_then(|node_ty| {
4273 let pos = self.merge_positions(enum_keyword, right_brace);
4274 let ty_ = node_ty.1;
4275 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
4277 let mut values = Vec::new_in(self.arena);
4278 for node in xhp_enum_values.iter() {
4279 // XHP enum values may only be string or int literals.
4281 Node::IntLiteral(&(s, _)) => {
4282 let i = s.parse::<isize>().unwrap_or(0);
4283 values.push(XhpEnumValue::XEVInt(i));
4285 Node::StringLiteral(&(s, _)) => {
4286 let owned_str = std::string::String::from_utf8_lossy(s);
4287 values.push(XhpEnumValue::XEVString(self.arena.alloc_str(&owned_str)));
4295 Node::XhpEnumTy(self.alloc((self.get_pos_opt(like), &ty, values.into_bump_slice())))
4297 None => Node::Ignored(SK::XHPEnumType),
4301 fn make_xhp_class_attribute(
4305 initializer: Self::R,
4308 let name = match name.as_id() {
4310 None => return Node::Ignored(SK::XHPClassAttribute),
4312 Node::XhpClassAttribute(self.alloc(XhpClassAttributeNode {
4315 needs_init: !initializer.is_present(),
4316 tag: match tag.token_kind() {
4317 Some(TokenKind::Required) => Some(xhp_attribute::Tag::Required),
4318 Some(TokenKind::Lateinit) => Some(xhp_attribute::Tag::LateInit),
4321 nullable: initializer.is_token(TokenKind::NullLiteral) || !initializer.is_present(),
4325 fn make_xhp_simple_class_attribute(&mut self, name: Self::R) -> Self::R {
4326 Node::XhpAttributeUse(self.alloc(name))
4329 fn make_property_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
4330 Node::ListItem(self.alloc((name, initializer)))
4333 fn make_methodish_declaration(
4340 let header = match header {
4341 Node::FunctionHeader(header) => header,
4342 _ => return Node::Ignored(SK::MethodishDeclaration),
4344 // If we don't have a body, use the closing token. A closing token of
4345 // '}' indicates a regular function, while a closing token of ';'
4346 // indicates an abstract function.
4347 let body = if body.is_ignored() { closer } else { body };
4348 let modifiers = read_member_modifiers(header.modifiers.iter());
4349 let is_constructor = header.name.is_token(TokenKind::Construct);
4350 let is_method = true;
4351 let (id, ty, properties) = match self.function_to_ty(is_method, attrs, header, body) {
4352 Some(tuple) => tuple,
4353 None => return Node::Ignored(SK::MethodishDeclaration),
4355 let attributes = self.to_attributes(attrs);
4356 let deprecated = attributes.deprecated.map(|msg| {
4357 let mut s = String::new_in(self.arena);
4358 s.push_str("The method ");
4360 s.push_str(" is deprecated: ");
4364 let mut flags = MethodFlags::empty();
4366 MethodFlags::ABSTRACT,
4367 self.classish_name_builder.in_interface() || modifiers.is_abstract,
4369 flags.set(MethodFlags::FINAL, modifiers.is_final);
4370 flags.set(MethodFlags::OVERRIDE, attributes.override_);
4372 MethodFlags::DYNAMICALLYCALLABLE,
4373 attributes.dynamically_callable,
4375 flags.set(MethodFlags::PHP_STD_LIB, attributes.php_std_lib);
4377 MethodFlags::SUPPORT_DYNAMIC_TYPE,
4378 !is_constructor && attributes.support_dynamic_type,
4380 let visibility = match modifiers.visibility {
4381 aast::Visibility::Public => {
4382 if attributes.internal {
4383 aast::Visibility::Internal
4385 aast::Visibility::Public
4388 _ => modifiers.visibility,
4391 let mut user_attributes = Vec::new_in(self.arena);
4392 if self.retain_or_omit_user_attributes_for_facts {
4393 for attribute in attrs.iter() {
4395 Node::Attribute(attr) => {
4396 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();
4405 let user_attributes = user_attributes.into_bump_slice();
4407 let method = self.alloc(ShallowMethod {
4413 attributes: user_attributes,
4416 Node::Constructor(self.alloc(ConstructorNode { method, properties }))
4418 Node::Method(self.alloc(MethodNode {
4420 is_static: modifiers.is_static,
4425 fn make_classish_body(
4427 _left_brace: Self::R,
4429 _right_brace: Self::R,
4431 Node::ClassishBody(self.alloc(elements.as_slice(self.arena)))
4434 fn make_enum_declaration(
4436 attributes: Self::R,
4441 constraint: Self::R,
4442 _left_brace: Self::R,
4443 use_clauses: Self::R,
4444 enumerators: Self::R,
4445 _right_brace: Self::R,
4447 let id = match self.elaborate_defined_id(name) {
4449 None => return Node::Ignored(SK::EnumDeclaration),
4451 let hint = match self.node_to_ty(extends) {
4453 None => return Node::Ignored(SK::EnumDeclaration),
4455 let extends = match self.node_to_ty(self.make_apply(
4456 (self.get_pos(name), "\\HH\\BuiltinEnum"),
4461 None => return Node::Ignored(SK::EnumDeclaration),
4464 let consts = self.slice(enumerators.iter().filter_map(|node| match node {
4465 &Node::Const(const_) => Some(const_),
4468 let mut user_attributes = Vec::with_capacity_in(attributes.len(), self.arena);
4469 for attribute in attributes.iter() {
4471 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4475 // Match ordering of attributes produced by the OCaml decl parser (even
4476 // though it's the reverse of the syntactic ordering).
4477 user_attributes.reverse();
4478 let user_attributes = user_attributes.into_bump_slice();
4480 let constraint = match constraint {
4481 Node::TypeConstraint(&(_kind, ty)) => self.node_to_ty(ty),
4485 let mut includes_len = 0;
4486 for element in use_clauses.iter() {
4488 Node::EnumUse(names) => includes_len += names.len(),
4492 let mut includes = Vec::with_capacity_in(includes_len, self.arena);
4493 for element in use_clauses.iter() {
4495 Node::EnumUse(names) => {
4496 includes.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
4501 let includes = includes.into_bump_slice();
4503 let parsed_attributes = self.to_attributes(attributes);
4505 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4506 mode: self.file_mode,
4510 has_xhp_keyword: false,
4511 kind: ClassishKind::Cenum,
4512 module: parsed_attributes.module,
4515 where_constraints: &[],
4516 extends: bumpalo::vec![in self.arena; extends].into_bump_slice(),
4519 xhp_enum_values: SMap::empty(),
4521 req_implements: &[],
4523 support_dynamic_type: parsed_attributes.support_dynamic_type,
4529 static_methods: &[],
4532 enum_type: Some(self.alloc(EnumType {
4538 self.add_class(key, cls);
4540 self.classish_name_builder.parsed_classish_declaration();
4542 Node::Ignored(SK::EnumDeclaration)
4545 fn make_enum_use(&mut self, _keyword: Self::R, names: Self::R, _semicolon: Self::R) -> Self::R {
4546 Node::EnumUse(self.alloc(names))
4549 fn begin_enumerator(&mut self) {
4550 self.start_accumulating_const_refs();
4558 _semicolon: Self::R,
4560 let refs = self.stop_accumulating_const_refs();
4561 let id = match self.expect_name(name) {
4563 None => return Node::Ignored(SyntaxKind::Enumerator),
4567 self.alloc(ShallowClassConst {
4568 abstract_: ClassConstKind::CCConcrete,
4571 .infer_const(name, value)
4572 .unwrap_or_else(|| self.tany_with_pos(id.0)),
4578 fn make_enum_class_declaration(
4580 attributes: Self::R,
4582 _enum_keyword: Self::R,
4583 _class_keyword: Self::R,
4587 _extends_keyword: Self::R,
4588 extends_list: Self::R,
4589 _left_brace: Self::R,
4591 _right_brace: Self::R,
4593 let name = match self.elaborate_defined_id(name) {
4595 None => return Node::Ignored(SyntaxKind::EnumClassDeclaration),
4599 .unwrap_or_else(|| self.tany_with_pos(name.0));
4601 let mut is_abstract = false;
4602 let mut is_final = false;
4603 for modifier in modifiers.iter() {
4604 match modifier.token_kind() {
4605 Some(TokenKind::Abstract) => is_abstract = true,
4606 Some(TokenKind::Final) => is_final = true,
4611 let class_kind = if is_abstract {
4612 ClassishKind::CenumClass(&Abstraction::Abstract)
4614 ClassishKind::CenumClass(&Abstraction::Concrete)
4617 let builtin_enum_class_ty = {
4619 let enum_class_ty_ = Ty_::Tapply(self.alloc((name.into(), &[])));
4620 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4621 let elt_ty_ = Ty_::Tapply(self.alloc((
4622 (pos, "\\HH\\MemberOf"),
4623 bumpalo::vec![in self.arena; enum_class_ty, base].into_bump_slice(),
4625 let elt_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), elt_ty_));
4626 let builtin_enum_ty_ = if is_abstract {
4627 Ty_::Tapply(self.alloc(((pos, "\\HH\\BuiltinAbstractEnumClass"), &[])))
4629 Ty_::Tapply(self.alloc((
4630 (pos, "\\HH\\BuiltinEnumClass"),
4631 std::slice::from_ref(self.alloc(elt_ty)),
4634 self.alloc(Ty(self.alloc(Reason::hint(pos)), builtin_enum_ty_))
4637 let consts = self.slice(elements.iter().filter_map(|node| match node {
4638 &Node::Const(const_) => Some(const_),
4642 let mut extends = Vec::with_capacity_in(extends_list.len() + 1, self.arena);
4643 extends.push(builtin_enum_class_ty);
4644 extends.extend(extends_list.iter().filter_map(|&n| self.node_to_ty(n)));
4645 let extends = extends.into_bump_slice();
4646 let includes = &extends[1..];
4648 let mut user_attributes = Vec::with_capacity_in(attributes.len() + 1, self.arena);
4649 for attribute in attributes.iter() {
4651 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4655 user_attributes.push(self.alloc(shallow_decl_defs::UserAttribute {
4656 name: (name.0, "__EnumClass"),
4657 classname_params: &[],
4659 // Match ordering of attributes produced by the OCaml decl parser (even
4660 // though it's the reverse of the syntactic ordering).
4661 user_attributes.reverse();
4662 let user_attributes = user_attributes.into_bump_slice();
4664 let parsed_attributes = self.to_attributes(attributes);
4666 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4667 mode: self.file_mode,
4669 abstract_: is_abstract,
4671 has_xhp_keyword: false,
4673 module: None, // TODO: grab module from attributes
4676 where_constraints: &[],
4680 xhp_enum_values: SMap::empty(),
4682 req_implements: &[],
4684 support_dynamic_type: parsed_attributes.support_dynamic_type,
4690 static_methods: &[],
4693 enum_type: Some(self.alloc(EnumType {
4699 self.add_class(name.1, cls);
4701 self.classish_name_builder.parsed_classish_declaration();
4703 Node::Ignored(SyntaxKind::EnumClassDeclaration)
4706 fn begin_enum_class_enumerator(&mut self) {
4707 self.start_accumulating_const_refs();
4710 fn make_enum_class_enumerator(
4715 _initializer: Self::R,
4716 _semicolon: Self::R,
4718 let refs = self.stop_accumulating_const_refs();
4719 let name = match self.expect_name(name) {
4721 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4724 let has_abstract_keyword = modifiers
4726 .any(|node| node.is_token(TokenKind::Abstract));
4727 let abstract_ = if has_abstract_keyword {
4728 /* default values not allowed atm */
4729 ClassConstKind::CCAbstract(false)
4731 ClassConstKind::CCConcrete
4735 .unwrap_or_else(|| self.tany_with_pos(name.0));
4736 let class_name = match self.classish_name_builder.get_current_classish_name() {
4738 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4740 let enum_class_ty_ = Ty_::Tapply(self.alloc(((pos, class_name.0), &[])));
4741 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4742 let type_ = Ty_::Tapply(self.alloc((
4743 (pos, "\\HH\\MemberOf"),
4744 bumpalo::vec![in self.arena; enum_class_ty, type_].into_bump_slice(),
4746 let type_ = self.alloc(Ty(self.alloc(Reason::hint(pos)), type_));
4747 Node::Const(self.alloc(ShallowClassConst {
4755 fn make_tuple_type_specifier(
4757 left_paren: Self::R,
4759 right_paren: Self::R,
4761 // We don't need to include the tys list in this position merging
4762 // because by definition it's already contained by the two brackets.
4763 let pos = self.merge_positions(left_paren, right_paren);
4764 let tys = self.slice(tys.iter().filter_map(|&node| self.node_to_ty(node)));
4765 self.hint_ty(pos, Ty_::Ttuple(tys))
4768 fn make_tuple_type_explicit_specifier(
4771 _left_angle: Self::R,
4773 right_angle: Self::R,
4775 let id = (self.get_pos(keyword), "\\tuple");
4776 // This is an error--tuple syntax is (A, B), not tuple<A, B>.
4777 // OCaml decl makes a Tapply rather than a Ttuple here.
4778 self.make_apply(id, types, self.get_pos(right_angle))
4781 fn make_intersection_type_specifier(
4783 left_paren: Self::R,
4785 right_paren: Self::R,
4787 let pos = self.merge_positions(left_paren, right_paren);
4788 let tys = self.slice(tys.iter().filter_map(|x| match x {
4789 Node::ListItem(&(ty, _ampersand)) => self.node_to_ty(ty),
4790 &x => self.node_to_ty(x),
4792 self.hint_ty(pos, Ty_::Tintersection(tys))
4795 fn make_union_type_specifier(
4797 left_paren: Self::R,
4799 right_paren: Self::R,
4801 let pos = self.merge_positions(left_paren, right_paren);
4802 let tys = self.slice(tys.iter().filter_map(|x| match x {
4803 Node::ListItem(&(ty, _bar)) => self.node_to_ty(ty),
4804 &x => self.node_to_ty(x),
4806 self.hint_ty(pos, Ty_::Tunion(tys))
4809 fn make_shape_type_specifier(
4817 let fields = fields;
4818 let fields_iter = fields.iter();
4819 let mut fields = AssocListMut::new_in(self.arena);
4820 for node in fields_iter {
4821 if let &Node::ShapeFieldSpecifier(&ShapeFieldNode { name, type_ }) = node {
4822 fields.insert(self.make_t_shape_field_name(name), type_)
4825 let kind = match open.token_kind() {
4826 Some(TokenKind::DotDotDot) => ShapeKind::OpenShape,
4827 _ => ShapeKind::ClosedShape,
4829 let pos = self.merge_positions(shape, rparen);
4830 self.hint_ty(pos, Ty_::Tshape(self.alloc((kind, fields.into()))))
4833 fn make_classname_type_specifier(
4838 _trailing_comma: Self::R,
4841 let id = match classname.as_id() {
4843 None => return Node::Ignored(SK::ClassnameTypeSpecifier),
4845 if gt.is_ignored() {
4846 self.prim_ty(aast::Tprim::Tstring, id.0)
4849 (id.0, self.elaborate_raw_id(id.1)),
4851 self.merge_positions(classname, gt),
4856 fn make_scope_resolution_expression(
4858 class_name: Self::R,
4862 let pos = self.merge_positions(class_name, value);
4863 let Id(class_name_pos, class_name_str) = match self.expect_name(class_name) {
4865 if matches!(class_name, Node::XhpName(..))
4866 && self.opts.disable_xhp_element_mangling
4867 && self.retain_or_omit_user_attributes_for_facts
4869 // for facts, allow xhp class consts to be mangled later
4870 // on even when xhp_element_mangling is disabled
4871 let mut qualified = String::with_capacity_in(id.1.len() + 1, self.arena);
4872 qualified.push_str("\\");
4873 qualified.push_str(id.1);
4874 Id(id.0, self.arena.alloc_str(&qualified))
4876 self.elaborate_id(id)
4879 None => return Node::Ignored(SK::ScopeResolutionExpression),
4881 let class_id = self.alloc(aast::ClassId(
4885 Node::Name(("self", _)) => aast::ClassId_::CIself,
4886 _ => aast::ClassId_::CI(self.alloc(Id(class_name_pos, class_name_str))),
4889 let value_id = match self.expect_name(value) {
4891 None => return Node::Ignored(SK::ScopeResolutionExpression),
4893 self.accumulate_const_ref(class_id, &value_id);
4894 Node::Expr(self.alloc(aast::Expr(
4897 nast::Expr_::ClassConst(self.alloc((class_id, self.alloc((value_id.0, value_id.1))))),
4901 fn make_field_specifier(
4903 question_token: Self::R,
4908 let optional = question_token.is_present();
4909 let ty = match self.node_to_ty(type_) {
4911 None => return Node::Ignored(SK::FieldSpecifier),
4913 let name = match self.make_shape_field_name(name) {
4915 None => return Node::Ignored(SK::FieldSpecifier),
4917 Node::ShapeFieldSpecifier(self.alloc(ShapeFieldNode {
4918 name: self.alloc(ShapeField(name)),
4919 type_: self.alloc(ShapeFieldType { optional, ty }),
4923 fn make_field_initializer(&mut self, key: Self::R, _arrow: Self::R, value: Self::R) -> Self::R {
4924 Node::ListItem(self.alloc((key, value)))
4927 fn make_varray_type_specifier(
4929 varray_keyword: Self::R,
4930 _less_than: Self::R,
4932 _trailing_comma: Self::R,
4933 greater_than: Self::R,
4935 let tparam = match self.node_to_ty(tparam) {
4937 None => self.tany_with_pos(self.get_pos(varray_keyword)),
4940 self.merge_positions(varray_keyword, greater_than),
4941 Ty_::Tapply(self.alloc((
4943 self.get_pos(varray_keyword),
4944 naming_special_names::collections::VEC,
4946 self.alloc([tparam]),
4951 fn make_darray_type_specifier(
4954 _less_than: Self::R,
4957 value_type: Self::R,
4958 _trailing_comma: Self::R,
4959 greater_than: Self::R,
4961 let pos = self.merge_positions(darray, greater_than);
4962 let key_type = self.node_to_ty(key_type).unwrap_or(TANY);
4963 let value_type = self.node_to_ty(value_type).unwrap_or(TANY);
4966 Ty_::Tapply(self.alloc((
4968 self.get_pos(darray),
4969 naming_special_names::collections::DICT,
4971 self.alloc([key_type, value_type]),
4976 fn make_old_attribute_specification(
4983 Node::List(nodes) => {
4984 Node::BracketedList(self.alloc((self.get_pos(ltlt), nodes, self.get_pos(gtgt))))
4986 _ => Node::Ignored(SK::OldAttributeSpecification),
4990 fn make_constructor_call(
4993 _left_paren: Self::R,
4995 _right_paren: Self::R,
4997 let unqualified_name = match self.expect_name(name) {
4999 None => return Node::Ignored(SK::ConstructorCall),
5001 let name = if unqualified_name.1.starts_with("__") {
5002 if self.simplify_naming_for_facts
5003 && !naming_special_names::user_attributes::AS_SET.contains(unqualified_name.1)
5005 // only elaborate non-builtins
5006 self.elaborate_id(unqualified_name)
5011 match self.expect_name(name) {
5012 Some(name) => self.elaborate_id(name),
5013 None => return Node::Ignored(SK::ConstructorCall),
5016 let classname_params = self.slice(args.iter().filter_map(|node| match node {
5017 Node::Expr(aast::Expr(
5020 aast::Expr_::ClassConst(&(
5021 aast::ClassId(_, _, aast::ClassId_::CI(&Id(pos, class_name))),
5025 let name = if class_name.starts_with(':')
5026 && self.opts.disable_xhp_element_mangling
5027 && self.retain_or_omit_user_attributes_for_facts
5029 // for facts, allow xhp class consts to be mangled later on
5030 // even when xhp_element_mangling is disabled
5031 let mut qualified = String::with_capacity_in(class_name.len() + 1, self.arena);
5032 qualified.push_str("\\");
5033 qualified.push_str(class_name);
5034 Id(pos, self.arena.alloc_str(&qualified))
5036 self.elaborate_id(Id(pos, class_name))
5038 Some(ClassNameParam { name, full_pos })
5040 Node::StringLiteral((name, full_pos))
5041 if self.retain_or_omit_user_attributes_for_facts =>
5043 Some(ClassNameParam {
5044 name: Id(Pos::none(), self.str_from_utf8_for_bytes_in_arena(*name)),
5048 Node::IntLiteral((name, full_pos)) if self.retain_or_omit_user_attributes_for_facts => {
5049 Some(ClassNameParam {
5050 name: Id(Pos::none(), name),
5057 let string_literal_params = if match name.1 {
5058 "__Deprecated" | "__Cipp" | "__CippLocal" | "__Policied" | "__Module" => true,
5061 fn fold_string_concat<'a>(expr: &nast::Expr<'a>, acc: &mut Vec<'a, u8>) {
5063 &aast::Expr(_, _, aast::Expr_::String(val)) => acc.extend_from_slice(val),
5064 &aast::Expr(_, _, aast::Expr_::Binop(&(Bop::Dot, e1, e2))) => {
5065 fold_string_concat(&e1, acc);
5066 fold_string_concat(&e2, acc);
5072 self.slice(args.iter().filter_map(|expr| match expr {
5073 Node::StringLiteral((x, p)) => Some((*p, *x)),
5074 Node::Expr(e @ aast::Expr(_, p, aast::Expr_::Binop(_))) => {
5075 let mut acc = Vec::new_in(self.arena);
5076 fold_string_concat(e, &mut acc);
5077 Some((p, acc.into_bump_slice().into()))
5085 Node::Attribute(self.alloc(UserAttributeNode {
5088 string_literal_params,
5096 _semicolon: Self::R,
5098 Node::TraitUse(self.alloc(names))
5101 fn make_trait_use_conflict_resolution(
5105 _left_brace: Self::R,
5107 _right_brace: Self::R,
5109 Node::TraitUse(self.alloc(names))
5112 fn make_require_clause(
5115 require_type: Self::R,
5117 _semicolon: Self::R,
5119 Node::RequireClause(self.alloc(RequireClause { require_type, name }))
5122 fn make_nullable_type_specifier(&mut self, question_mark: Self::R, hint: Self::R) -> Self::R {
5123 let pos = self.merge_positions(question_mark, hint);
5124 let ty = match self.node_to_ty(hint) {
5126 None => return Node::Ignored(SK::NullableTypeSpecifier),
5128 self.hint_ty(pos, Ty_::Toption(ty))
5131 fn make_like_type_specifier(&mut self, tilde: Self::R, hint: Self::R) -> Self::R {
5132 let pos = self.merge_positions(tilde, hint);
5133 let ty = match self.node_to_ty(hint) {
5135 None => return Node::Ignored(SK::LikeTypeSpecifier),
5137 self.hint_ty(pos, Ty_::Tlike(ty))
5140 fn make_closure_type_specifier(
5142 outer_left_paren: Self::R,
5143 readonly_keyword: Self::R,
5144 _function_keyword: Self::R,
5145 _inner_left_paren: Self::R,
5146 parameter_list: Self::R,
5147 _inner_right_paren: Self::R,
5148 capability: Self::R,
5150 readonly_ret: Self::R,
5151 return_type: Self::R,
5152 outer_right_paren: Self::R,
5154 let make_param = |fp: &'a FunParamDecl<'a>| -> &'a FunParam<'a> {
5155 let mut flags = FunParamFlags::empty();
5158 ParamMode::FPinout => {
5159 flags |= FunParamFlags::INOUT;
5161 ParamMode::FPnormal => {}
5165 flags |= FunParamFlags::READONLY;
5168 self.alloc(FunParam {
5169 pos: self.get_pos(fp.hint),
5171 type_: self.alloc(PossiblyEnforcedTy {
5172 enforced: Enforcement::Unenforced,
5173 type_: self.node_to_ty(fp.hint).unwrap_or_else(|| tany()),
5179 let arity = parameter_list
5181 .find_map(|&node| match node {
5182 Node::FunParam(fp) if fp.variadic => Some(FunArity::Fvariadic(make_param(fp))),
5185 .unwrap_or(FunArity::Fstandard);
5187 let params = self.slice(parameter_list.iter().filter_map(|&node| match node {
5188 Node::FunParam(fp) if !fp.variadic => Some(make_param(fp)),
5192 let ret = match self.node_to_ty(return_type) {
5194 None => return Node::Ignored(SK::ClosureTypeSpecifier),
5196 let pos = self.merge_positions(outer_left_paren, outer_right_paren);
5197 let implicit_params = self.as_fun_implicit_params(capability, pos);
5199 let mut flags = FunTypeFlags::empty();
5200 if readonly_ret.is_token(TokenKind::Readonly) {
5201 flags |= FunTypeFlags::RETURNS_READONLY;
5203 if readonly_keyword.is_token(TokenKind::Readonly) {
5204 flags |= FunTypeFlags::READONLY_THIS;
5209 Ty_::Tfun(self.alloc(FunType {
5212 where_constraints: &[],
5215 ret: self.alloc(PossiblyEnforcedTy {
5216 enforced: Enforcement::Unenforced,
5220 ifc_decl: default_ifc_fun_decl(),
5225 fn make_closure_parameter_type_specifier(
5231 let kind = if inout.is_token(TokenKind::Inout) {
5236 Node::FunParam(self.alloc(FunParamDecl {
5237 attributes: Node::Ignored(SK::Missing),
5238 visibility: Node::Ignored(SK::Missing),
5241 readonly: readonly.is_token(TokenKind::Readonly),
5242 pos: self.get_pos(hint),
5245 initializer: Node::Ignored(SK::Missing),
5249 fn make_type_const_declaration(
5251 attributes: Self::R,
5253 _const_keyword: Self::R,
5254 _type_keyword: Self::R,
5256 _type_parameters: Self::R,
5257 as_constraint: Self::R,
5260 _semicolon: Self::R,
5262 let attributes = self.to_attributes(attributes);
5263 let has_abstract_keyword = modifiers
5265 .any(|node| node.is_token(TokenKind::Abstract));
5266 let as_constraint = match as_constraint {
5267 Node::TypeConstraint(innards) => self.node_to_ty(innards.1),
5270 let type_ = self.node_to_ty(type_);
5271 let kind = if has_abstract_keyword {
5272 // Abstract type constant:
5273 // abstract const type T [as X] [super Y] [= Z];
5274 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5276 super_constraint: None,
5280 if let Some(t) = type_ {
5281 // Concrete type constant:
5282 // const type T = Z;
5283 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type: t }))
5285 // concrete or type constant requires a value
5286 return Node::Ignored(SK::TypeConstDeclaration);
5289 let name = match name.as_id() {
5291 None => return Node::Ignored(SK::TypeConstDeclaration),
5293 Node::TypeConstant(self.alloc(ShallowTypeconst {
5296 enforceable: match attributes.enforceable {
5297 Some(pos) => (pos, true),
5298 None => (Pos::none(), false),
5300 reifiable: attributes.reifiable,
5305 fn make_context_const_declaration(
5308 _const_keyword: Self::R,
5309 _ctx_keyword: Self::R,
5311 _type_parameters: Self::R,
5312 constraints: Self::R,
5315 _semicolon: Self::R,
5317 let name = match name.as_id() {
5319 None => return Node::Ignored(SK::TypeConstDeclaration),
5321 let has_abstract_keyword = modifiers
5323 .any(|node| node.is_token(TokenKind::Abstract));
5324 let context = self.node_to_ty(ctx_list);
5326 // note: lowerer ensures that there's at most 1 constraint of each kind
5327 let mut as_constraint = None;
5328 let mut super_constraint = None;
5329 for c in constraints.iter() {
5330 if let Node::ContextConstraint(&(kind, hint)) = c {
5331 let ty = self.node_to_ty(hint);
5333 ConstraintKind::ConstraintSuper => super_constraint = ty,
5334 ConstraintKind::ConstraintAs => as_constraint = ty,
5339 let kind = if has_abstract_keyword {
5340 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5346 if let Some(tc_type) = context {
5347 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type }))
5349 /* Concrete type const must have a value */
5350 return Node::Ignored(SK::TypeConstDeclaration);
5353 Node::TypeConstant(self.alloc(ShallowTypeconst {
5356 enforceable: (Pos::none(), false),
5362 fn make_decorated_expression(&mut self, decorator: Self::R, expr: Self::R) -> Self::R {
5363 Node::ListItem(self.alloc((decorator, expr)))
5366 fn make_type_constant(
5369 _coloncolon: Self::R,
5370 constant_name: Self::R,
5372 let id = match self.expect_name(constant_name) {
5374 None => return Node::Ignored(SK::TypeConstant),
5376 let pos = self.merge_positions(ty, constant_name);
5377 let ty = match (ty, self.classish_name_builder.get_current_classish_name()) {
5378 (Node::Name(("self", self_pos)), Some((name, class_name_pos))) => {
5379 // In classes, we modify the position when rewriting the
5380 // `self` keyword to point to the class name. In traits,
5381 // we don't (because traits are not types). We indicate
5382 // that the position shouldn't be rewritten with the
5384 let id_pos = if class_name_pos.is_none() {
5389 let reason = self.alloc(Reason::hint(self_pos));
5390 let ty_ = Ty_::Tapply(self.alloc(((id_pos, name), &[][..])));
5391 self.alloc(Ty(reason, ty_))
5393 _ => match self.node_to_ty(ty) {
5395 None => return Node::Ignored(SK::TypeConstant),
5398 let reason = self.alloc(Reason::hint(pos));
5399 // The reason-rewriting here is only necessary to match the
5400 // behavior of OCaml decl (which flattens and then unflattens
5401 // Haccess hints, losing some position information).
5402 let ty = self.rewrite_taccess_reasons(ty, reason);
5403 Node::Ty(self.alloc(Ty(
5405 Ty_::Taccess(self.alloc(TaccessType(ty, id.into()))),
5409 fn make_soft_type_specifier(&mut self, at_token: Self::R, hint: Self::R) -> Self::R {
5410 let pos = self.merge_positions(at_token, hint);
5411 let hint = match self.node_to_ty(hint) {
5413 None => return Node::Ignored(SK::SoftTypeSpecifier),
5415 // Use the type of the hint as-is (i.e., throw away the knowledge that
5416 // we had a soft type specifier here--the typechecker does not use it).
5417 // Replace its Reason with one including the position of the `@` token.
5420 if self.opts.interpret_soft_types_as_like_types {
5428 fn make_attribute_specification(&mut self, attributes: Self::R) -> Self::R {
5429 if self.retain_or_omit_user_attributes_for_facts {
5432 Node::Ignored(SK::AttributeSpecification)
5436 fn make_attribute(&mut self, _at: Self::R, attribute: Self::R) -> Self::R {
5437 if self.retain_or_omit_user_attributes_for_facts {
5440 Node::Ignored(SK::Attribute)
5444 // A type specifier preceded by an attribute list. At the time of writing,
5445 // only the <<__Soft>> attribute is permitted here.
5446 fn make_attributized_specifier(&mut self, attributes: Self::R, hint: Self::R) -> Self::R {
5448 Node::BracketedList((
5450 [Node::Attribute(UserAttributeNode {
5451 name: Id(_, "__Soft"),
5456 let attributes_pos = self.merge(*ltlt_pos, *gtgt_pos);
5457 let hint_pos = self.get_pos(hint);
5458 // Use the type of the hint as-is (i.e., throw away the
5459 // knowledge that we had a soft type specifier here--the
5460 // typechecker does not use it). Replace its Reason with one
5461 // including the position of the attribute list.
5462 let hint = match self.node_to_ty(hint) {
5464 None => return Node::Ignored(SK::AttributizedSpecifier),
5468 self.merge(attributes_pos, hint_pos),
5469 if self.opts.interpret_soft_types_as_like_types {
5480 fn make_vector_type_specifier(
5483 _left_angle: Self::R,
5485 _trailing_comma: Self::R,
5486 right_angle: Self::R,
5488 let id = match self.expect_name(vec) {
5490 None => return Node::Ignored(SK::VectorTypeSpecifier),
5492 let id = (id.0, self.elaborate_raw_id(id.1));
5493 self.make_apply(id, hint, self.get_pos(right_angle))
5496 fn make_dictionary_type_specifier(
5499 _left_angle: Self::R,
5500 type_arguments: Self::R,
5501 right_angle: Self::R,
5503 let id = match self.expect_name(dict) {
5505 None => return Node::Ignored(SK::DictionaryTypeSpecifier),
5507 let id = (id.0, self.elaborate_raw_id(id.1));
5508 self.make_apply(id, type_arguments, self.get_pos(right_angle))
5511 fn make_keyset_type_specifier(
5514 _left_angle: Self::R,
5516 _trailing_comma: Self::R,
5517 right_angle: Self::R,
5519 let id = match self.expect_name(keyset) {
5521 None => return Node::Ignored(SK::KeysetTypeSpecifier),
5523 let id = (id.0, self.elaborate_raw_id(id.1));
5524 self.make_apply(id, hint, self.get_pos(right_angle))
5527 fn make_variable_expression(&mut self, _expression: Self::R) -> Self::R {
5528 Node::Ignored(SK::VariableExpression)
5531 fn make_file_attribute_specification(
5533 _left_double_angle: Self::R,
5536 attributes: Self::R,
5537 _right_double_angle: Self::R,
5539 if self.retain_or_omit_user_attributes_for_facts {
5540 self.file_attributes = List::empty();
5541 for attr in attributes.iter() {
5543 Node::Attribute(attr) => self
5545 .push_front(self.user_attribute_to_decl(attr), self.arena),
5550 self.set_module(&attributes);
5551 Node::Ignored(SK::FileAttributeSpecification)
5554 fn make_subscript_expression(
5557 _left_bracket: Self::R,
5559 _right_bracket: Self::R,
5561 Node::Ignored(SK::SubscriptExpression)
5564 fn make_member_selection_expression(
5570 Node::Ignored(SK::MemberSelectionExpression)
5573 fn make_object_creation_expression(
5575 _new_keyword: Self::R,
5578 Node::Ignored(SK::ObjectCreationExpression)
5581 fn make_safe_member_selection_expression(
5587 Node::Ignored(SK::SafeMemberSelectionExpression)
5590 fn make_function_call_expression(
5593 _type_args: Self::R,
5594 _enum_class_label: Self::R,
5595 _left_paren: Self::R,
5596 _argument_list: Self::R,
5597 _right_paren: Self::R,
5599 Node::Ignored(SK::FunctionCallExpression)
5602 fn make_list_expression(
5605 _left_paren: Self::R,
5607 _right_paren: Self::R,
5609 Node::Ignored(SK::ListExpression)