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, MultiSetMut};
19 use flatten_smart_constructors::{FlattenOp, FlattenSmartConstructors};
20 use namespaces::ElaborateKind;
21 use namespaces_rust as namespaces;
22 use oxidized_by_ref::{
25 Abstraction, Bop, ClassishKind, ConstraintKind, FunKind, Id, ShapeFieldName, Uop, Variance,
28 decl_parser_options::DeclParserOptions,
29 direct_decl_parser::Decls,
31 method_flags::MethodFlags,
32 namespace_env::Env as NamespaceEnv,
35 prop_flags::PropFlags,
36 relative_path::RelativePath,
39 self, Decl, ShallowClassConst, ShallowMethod, ShallowProp, ShallowTypeconst,
41 shape_map::ShapeField,
42 t_shape_map::TShapeField,
44 self, AbstractTypeconst, Capability::*, ClassConstKind, ConcreteTypeconst, ConstDecl,
45 Enforcement, EnumType, FunArity, FunElt, FunImplicitParams, FunParam, FunParams, FunType,
46 IfcFunDecl, ParamMode, PosByteString, PosId, PosString, PossiblyEnforcedTy, RecordFieldReq,
47 ShapeFieldType, ShapeKind, TaccessType, Tparam, TshapeFieldName, Ty, Ty_, Typeconst,
48 TypedefType, WhereConstraint, XhpAttrTag,
50 typing_defs_flags::{FunParamFlags, FunTypeFlags},
51 typing_modules::Module_,
52 typing_reason::Reason,
54 use parser_core_types::{
55 compact_token::CompactToken, indexed_source_text::IndexedSourceText, source_text::SourceText,
56 syntax_kind::SyntaxKind, token_factory::SimpleTokenFactoryImpl, token_kind::TokenKind,
59 mod direct_decl_smart_constructors_generated;
63 type SSet<'a> = arena_collections::SortedSet<'a, &'a str>;
66 pub struct DirectDeclSmartConstructors<'a, 'text, S: SourceTextAllocator<'text, 'a>> {
67 pub token_factory: SimpleTokenFactoryImpl<CompactToken>,
69 pub source_text: IndexedSourceText<'text>,
70 pub arena: &'a bumpalo::Bump,
72 // const_refs will accumulate all scope-resolution-expressions it enconuters while it's "Some"
73 const_refs: Option<arena_collections::set::Set<'a, typing_defs::ClassConstRef<'a>>>,
74 opts: &'a DeclParserOptions<'a>,
75 filename: &'a RelativePath<'a>,
77 namespace_builder: Rc<NamespaceBuilder<'a>>,
78 classish_name_builder: ClassishNameBuilder<'a>,
79 type_parameters: Rc<Vec<'a, SSet<'a>>>,
80 omit_user_attributes_irrelevant_to_typechecking: bool,
81 previous_token_kind: TokenKind,
83 source_text_allocator: S,
86 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> DirectDeclSmartConstructors<'a, 'text, S> {
88 opts: &'a DeclParserOptions<'a>,
89 src: &SourceText<'text>,
92 source_text_allocator: S,
93 omit_user_attributes_irrelevant_to_typechecking: bool,
95 let source_text = IndexedSourceText::new(src.clone());
96 let path = source_text.source_text().file_path();
97 let prefix = path.prefix();
98 let path = String::from_str_in(path.path_str(), arena).into_bump_str();
99 let filename = RelativePath::make(prefix, path);
101 token_factory: SimpleTokenFactoryImpl::new(),
106 filename: arena.alloc(filename),
108 decls: Decls::empty(),
110 namespace_builder: Rc::new(NamespaceBuilder::new_in(
111 opts.auto_namespace_map,
112 opts.disable_xhp_element_mangling,
115 classish_name_builder: ClassishNameBuilder::new(),
116 type_parameters: Rc::new(Vec::new_in(arena)),
117 // EndOfFile is used here as a None value (signifying "beginning of
118 // file") to save space. There is no legitimate circumstance where
119 // we would parse a token and the previous token kind would be
121 previous_token_kind: TokenKind::EndOfFile,
122 source_text_allocator,
123 omit_user_attributes_irrelevant_to_typechecking,
128 pub fn alloc<T>(&self, val: T) -> &'a T {
129 self.arena.alloc(val)
132 fn qualified_name_from_parts(&self, parts: &'a [Node<'a>], pos: &'a Pos<'a>) -> Id<'a> {
133 // Count the length of the qualified name, so that we can allocate
134 // exactly the right amount of space for it in our arena.
138 Node::Name(&(name, _)) => len += name.len(),
139 Node::Token(t) if t.kind() == TokenKind::Backslash => len += 1,
140 Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => len += name.len() + 1,
141 Node::ListItem(&(Node::Token(t), _backslash))
142 if t.kind() == TokenKind::Namespace =>
144 len += t.width() + 1;
149 // If there's no internal trivia, then we can just reference the
150 // qualified name in the original source text instead of copying it.
151 let source_len = pos.end_cnum() - pos.start_cnum();
152 if source_len == len {
153 let qualified_name: &'a str = self.str_from_utf8(self.source_text_at_pos(pos));
154 return Id(pos, qualified_name);
156 // Allocate `len` bytes and fill them with the fully qualified name.
157 let mut qualified_name = String::with_capacity_in(len, self.arena);
160 Node::Name(&(name, _pos)) => qualified_name.push_str(&name),
161 Node::Token(t) if t.kind() == TokenKind::Backslash => qualified_name.push('\\'),
162 &Node::ListItem(&(Node::Name(&(name, _)), _backslash)) => {
163 qualified_name.push_str(&name);
164 qualified_name.push_str("\\");
166 &Node::ListItem(&(Node::Token(t), _backslash))
167 if t.kind() == TokenKind::Namespace =>
169 qualified_name.push_str("namespace\\");
174 debug_assert_eq!(len, qualified_name.len());
175 debug_assert_eq!(len, qualified_name.capacity());
176 Id(pos, qualified_name.into_bump_str())
179 /// If the given node is an identifier, XHP name, or qualified name,
180 /// elaborate it in the current namespace and return Some. To be used for
181 /// the name of a decl in its definition (e.g., "C" in `class C {}` or "f"
182 /// in `function f() {}`).
183 fn elaborate_defined_id(&self, name: Node<'a>) -> Option<Id<'a>> {
184 let id = match name {
185 Node::Name(&(name, pos)) => Id(pos, name),
186 Node::XhpName(&(name, pos)) => Id(pos, name),
187 Node::QualifiedName(&(parts, pos)) => self.qualified_name_from_parts(parts, pos),
190 Some(self.namespace_builder.elaborate_defined_id(id))
193 /// If the given node is a name (i.e., an identifier or a qualified name),
194 /// return Some. No namespace elaboration is performed.
195 fn expect_name(&self, name: Node<'a>) -> Option<Id<'a>> {
196 // If it's a simple identifier, return it.
197 if let id @ Some(_) = name.as_id() {
201 Node::QualifiedName(&(parts, pos)) => Some(self.qualified_name_from_parts(parts, pos)),
202 Node::Token(t) if t.kind() == TokenKind::XHP => {
203 let pos = self.token_pos(t);
204 let text = self.str_from_utf8(self.source_text_at_pos(pos));
211 /// Fully qualify the given identifier as a type name (with consideration
212 /// to `use` statements in scope).
213 fn elaborate_id(&self, id: Id<'a>) -> Id<'a> {
214 let Id(pos, name) = id;
215 Id(pos, self.elaborate_raw_id(name))
218 /// Fully qualify the given identifier as a type name (with consideration
219 /// to `use` statements in scope).
220 fn elaborate_raw_id(&self, id: &'a str) -> &'a str {
221 self.namespace_builder
222 .elaborate_raw_id(ElaborateKind::Class, id)
225 /// Fully qualify the given identifier as a constant name (with
226 /// consideration to `use` statements in scope).
227 fn elaborate_const_id(&self, id: Id<'a>) -> Id<'a> {
228 let Id(pos, name) = id;
231 self.namespace_builder
232 .elaborate_raw_id(ElaborateKind::Const, name),
236 fn slice<T>(&self, iter: impl Iterator<Item = T>) -> &'a [T] {
237 let mut result = match iter.size_hint().1 {
238 Some(upper_bound) => Vec::with_capacity_in(upper_bound, self.arena),
239 None => Vec::new_in(self.arena),
244 result.into_bump_slice()
247 fn start_accumulating_const_refs(&mut self) {
248 self.const_refs = Some(arena_collections::set::Set::empty());
251 fn accumulate_const_ref(&mut self, class_id: &'a aast::ClassId<'_, (), ()>, value_id: &Id<'a>) {
252 // The decl for a class constant stores a list of all the scope-resolution expressions
253 // it contains. For example "const C=A::X" stores A::X, and "const D=self::Y" stores self::Y.
254 // (This is so we can detect cross-type circularity in constant initializers).
255 // TODO: Hack is the wrong place to detect circularity (because we can never do it completely soundly,
256 // and because it's a cross-body problem). The right place to do it is in a linter. All this should be
257 // removed from here and put into a linter.
258 if let Some(const_refs) = self.const_refs {
260 nast::ClassId_::CI(sid) => {
261 self.const_refs = Some(const_refs.add(
263 typing_defs::ClassConstRef(
264 typing_defs::ClassConstFrom::From(sid.1),
269 nast::ClassId_::CIself => {
270 self.const_refs = Some(const_refs.add(
272 typing_defs::ClassConstRef(typing_defs::ClassConstFrom::Self_, value_id.1),
276 nast::ClassId_::CIparent
277 | nast::ClassId_::CIstatic
278 | nast::ClassId_::CIexpr(_) => {}
283 fn stop_accumulating_const_refs(&mut self) -> &'a [typing_defs::ClassConstRef<'a>] {
284 let const_refs = self.const_refs;
285 self.const_refs = None;
287 Some(const_refs) => {
288 let mut elements: Vec<'_, typing_defs::ClassConstRef<'_>> =
289 bumpalo::collections::Vec::with_capacity_in(const_refs.count(), self.arena);
290 elements.extend(const_refs.into_iter());
291 elements.into_bump_slice()
298 pub trait SourceTextAllocator<'text, 'target>: Clone {
299 fn alloc(&self, text: &'text str) -> &'target str;
303 pub struct NoSourceTextAllocator;
305 impl<'text> SourceTextAllocator<'text, 'text> for NoSourceTextAllocator {
307 fn alloc(&self, text: &'text str) -> &'text str {
313 pub struct ArenaSourceTextAllocator<'arena>(pub &'arena bumpalo::Bump);
315 impl<'text, 'arena> SourceTextAllocator<'text, 'arena> for ArenaSourceTextAllocator<'arena> {
317 fn alloc(&self, text: &'text str) -> &'arena str {
318 self.0.alloc_str(text)
322 fn prefix_slash<'a>(arena: &'a Bump, name: &str) -> &'a str {
323 let mut s = String::with_capacity_in(1 + name.len(), arena);
329 fn prefix_colon<'a>(arena: &'a Bump, name: &str) -> &'a str {
330 let mut s = String::with_capacity_in(1 + name.len(), arena);
336 fn concat<'a>(arena: &'a Bump, str1: &str, str2: &str) -> &'a str {
337 let mut result = String::with_capacity_in(str1.len() + str2.len(), arena);
338 result.push_str(str1);
339 result.push_str(str2);
340 result.into_bump_str()
343 fn strip_dollar_prefix<'a>(name: &'a str) -> &'a str {
344 name.trim_start_matches("$")
347 const TANY_: Ty_<'_> = Ty_::Tany(oxidized_by_ref::tany_sentinel::TanySentinel);
348 const TANY: &Ty<'_> = &Ty(Reason::none(), TANY_);
350 fn tany() -> &'static Ty<'static> {
354 fn default_ifc_fun_decl<'a>() -> IfcFunDecl<'a> {
355 IfcFunDecl::FDPolicied(Some("PUBLIC"))
361 visibility: aast::Visibility,
367 fn read_member_modifiers<'a: 'b, 'b>(modifiers: impl Iterator<Item = &'b Node<'a>>) -> Modifiers {
368 let mut ret = Modifiers {
370 visibility: aast::Visibility::Public,
375 for modifier in modifiers {
376 if let Some(vis) = modifier.as_visibility() {
377 ret.visibility = vis;
379 match modifier.token_kind() {
380 Some(TokenKind::Static) => ret.is_static = true,
381 Some(TokenKind::Abstract) => ret.is_abstract = true,
382 Some(TokenKind::Final) => ret.is_final = true,
383 Some(TokenKind::Readonly) => ret.is_readonly = true,
390 #[derive(Clone, Debug)]
391 struct NamespaceBuilder<'a> {
393 stack: Vec<'a, NamespaceEnv<'a>>,
394 auto_ns_map: &'a [(&'a str, &'a str)],
397 impl<'a> NamespaceBuilder<'a> {
399 auto_ns_map: &'a [(&'a str, &'a str)],
400 disable_xhp_element_mangling: bool,
403 let mut ns_uses = SMap::empty();
404 for &alias in hh_autoimport::NAMESPACES {
405 ns_uses = ns_uses.add(arena, alias, concat(arena, "HH\\", alias));
407 for (alias, ns) in auto_ns_map.iter() {
408 ns_uses = ns_uses.add(arena, alias, ns);
411 let mut class_uses = SMap::empty();
412 for &alias in hh_autoimport::TYPES {
413 class_uses = class_uses.add(arena, alias, concat(arena, "HH\\", alias));
418 stack: bumpalo::vec![in arena; NamespaceEnv {
421 fun_uses: SMap::empty(),
422 const_uses: SMap::empty(),
423 record_def_uses: SMap::empty(),
427 disable_xhp_element_mangling,
433 fn push_namespace(&mut self, name: Option<&str>) {
434 let current = self.current_namespace();
435 let nsenv = self.stack.last().unwrap().clone(); // shallow clone
436 if let Some(name) = name {
437 let mut fully_qualified = match current {
438 None => String::with_capacity_in(name.len(), self.arena),
440 let mut fully_qualified =
441 String::with_capacity_in(current.len() + name.len() + 1, self.arena);
442 fully_qualified.push_str(current);
443 fully_qualified.push('\\');
447 fully_qualified.push_str(name);
448 self.stack.push(NamespaceEnv {
449 name: Some(fully_qualified.into_bump_str()),
453 self.stack.push(NamespaceEnv {
460 fn pop_namespace(&mut self) {
461 // We'll never push a namespace for a declaration of items in the global
462 // namespace (e.g., `namespace { ... }`), so only pop if we are in some
463 // namespace other than the global one.
464 if self.stack.len() > 1 {
465 self.stack.pop().unwrap();
469 // push_namespace(Y) + pop_namespace() + push_namespace(X) should be equivalent to
470 // push_namespace(Y) + push_namespace(X) + pop_previous_namespace()
471 fn pop_previous_namespace(&mut self) {
472 if self.stack.len() > 2 {
473 let last = self.stack.pop().unwrap().name.unwrap_or("\\");
474 let previous = self.stack.pop().unwrap().name.unwrap_or("\\");
475 assert!(last.starts_with(previous));
476 let name = &last[previous.len() + 1..last.len()];
477 self.push_namespace(Some(name));
481 fn current_namespace(&self) -> Option<&'a str> {
482 self.stack.last().and_then(|nsenv| nsenv.name)
485 fn add_import(&mut self, kind: NamespaceUseKind, name: &'a str, aliased_name: Option<&'a str>) {
486 let stack_top = &mut self
489 .expect("Attempted to get the current import map, but namespace stack was empty");
490 let aliased_name = aliased_name.unwrap_or_else(|| {
491 name.rsplit_terminator('\\')
493 .expect("Expected at least one entry in import name")
495 let name = name.trim_end_matches('\\');
496 let name = if name.starts_with('\\') {
499 prefix_slash(self.arena, name)
502 NamespaceUseKind::Type => {
503 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
505 NamespaceUseKind::Namespace => {
506 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
508 NamespaceUseKind::Mixed => {
509 stack_top.class_uses = stack_top.class_uses.add(self.arena, aliased_name, name);
510 stack_top.ns_uses = stack_top.ns_uses.add(self.arena, aliased_name, name);
515 fn elaborate_raw_id(&self, kind: ElaborateKind, name: &'a str) -> &'a str {
516 if name.starts_with('\\') {
519 let env = self.stack.last().unwrap();
520 namespaces::elaborate_raw_id_in(self.arena, env, kind, name)
523 fn elaborate_defined_id(&self, id: Id<'a>) -> Id<'a> {
524 let Id(pos, name) = id;
525 let env = self.stack.last().unwrap();
526 let name = if env.disable_xhp_element_mangling && name.contains(':') {
527 let xhp_name_opt = namespaces::elaborate_xhp_namespace(name);
528 let name = xhp_name_opt.map_or(name, |s| self.arena.alloc_str(&s));
529 if !name.starts_with('\\') {
530 namespaces::elaborate_into_current_ns_in(self.arena, env, name)
535 namespaces::elaborate_into_current_ns_in(self.arena, env, name)
541 #[derive(Clone, Debug)]
542 enum ClassishNameBuilder<'a> {
543 /// We are not in a classish declaration.
546 /// We saw a classish keyword token followed by a Name, so we make it
547 /// available as the name of the containing class declaration.
548 InClassish(&'a (&'a str, &'a Pos<'a>, TokenKind)),
551 impl<'a> ClassishNameBuilder<'a> {
553 ClassishNameBuilder::NotInClassish
556 fn lexed_name_after_classish_keyword(
561 token_kind: TokenKind,
563 use ClassishNameBuilder::*;
566 let name = if name.starts_with(':') {
567 prefix_slash(arena, name)
571 *self = InClassish(arena.alloc((name, pos, token_kind)))
577 fn parsed_classish_declaration(&mut self) {
578 *self = ClassishNameBuilder::NotInClassish;
581 fn get_current_classish_name(&self) -> Option<(&'a str, &'a Pos<'a>)> {
582 use ClassishNameBuilder::*;
584 NotInClassish => None,
585 InClassish((name, pos, _)) => Some((name, pos)),
589 fn in_interface(&self) -> bool {
590 use ClassishNameBuilder::*;
592 InClassish((_, _, TokenKind::Interface)) => true,
593 InClassish((_, _, _)) | NotInClassish => false,
599 pub struct FunParamDecl<'a> {
600 attributes: Node<'a>,
601 visibility: Node<'a>,
606 name: Option<&'a str>,
608 initializer: Node<'a>,
612 pub struct FunctionHeader<'a> {
615 type_params: Node<'a>,
616 param_list: Node<'a>,
617 capability: Node<'a>,
619 readonly_return: Node<'a>,
620 where_constraints: Node<'a>,
624 pub struct RequireClause<'a> {
625 require_type: Node<'a>,
630 pub struct TypeParameterDecl<'a> {
632 reified: aast::ReifyKind,
634 constraints: &'a [(ConstraintKind, Node<'a>)],
635 tparam_params: &'a [&'a Tparam<'a>],
636 user_attributes: &'a [&'a UserAttributeNode<'a>],
640 pub struct ClosureTypeHint<'a> {
646 pub struct NamespaceUseClause<'a> {
647 kind: NamespaceUseKind,
649 as_: Option<&'a str>,
652 #[derive(Copy, Clone, Debug)]
653 enum NamespaceUseKind {
660 pub struct ConstructorNode<'a> {
661 method: &'a ShallowMethod<'a>,
662 properties: &'a [ShallowProp<'a>],
666 pub struct MethodNode<'a> {
667 method: &'a ShallowMethod<'a>,
672 pub struct PropertyNode<'a> {
673 decls: &'a [ShallowProp<'a>],
678 pub struct XhpClassAttributeDeclarationNode<'a> {
679 xhp_attr_enum_values: &'a [(&'a str, &'a [XhpEnumValue<'a>])],
680 xhp_attr_decls: &'a [ShallowProp<'a>],
681 xhp_attr_uses_decls: &'a [Node<'a>],
685 pub struct XhpClassAttributeNode<'a> {
687 tag: Option<XhpAttrTag>,
694 pub struct ShapeFieldNode<'a> {
695 name: &'a ShapeField<'a>,
696 type_: &'a ShapeFieldType<'a>,
699 #[derive(Copy, Clone, Debug)]
700 struct ClassNameParam<'a> {
702 full_pos: &'a Pos<'a>, // Position of the full expression `Foo::class`
706 pub struct UserAttributeNode<'a> {
708 classname_params: &'a [ClassNameParam<'a>],
709 string_literal_params: &'a [&'a BStr], // this is only used for __Deprecated attribute message and Cipp parameters
712 mod fixed_width_token {
713 use parser_core_types::token_kind::TokenKind;
715 #[derive(Copy, Clone)]
716 pub struct FixedWidthToken(u64); // { offset: u56, kind: TokenKind }
718 const KIND_BITS: u8 = 8;
719 const KIND_MASK: u64 = u8::MAX as u64;
720 const MAX_OFFSET: u64 = !(KIND_MASK << (64 - KIND_BITS));
722 impl FixedWidthToken {
723 pub fn new(kind: TokenKind, offset: usize) -> Self {
724 // We don't want to spend bits tracking the width of fixed-width
725 // tokens. Since we don't track width, verify that this token kind
726 // is in fact a fixed-width kind.
727 debug_assert!(kind.fixed_width().is_some());
729 let offset: u64 = offset.try_into().unwrap();
730 if offset > MAX_OFFSET {
731 panic!("FixedWidthToken: offset too large: {}", offset);
733 Self(offset << KIND_BITS | kind as u8 as u64)
736 pub fn offset(self) -> usize {
737 (self.0 >> KIND_BITS).try_into().unwrap()
740 pub fn kind(self) -> TokenKind {
741 TokenKind::try_from_u8(self.0 as u8).unwrap()
744 pub fn width(self) -> usize {
745 self.kind().fixed_width().unwrap().get()
749 impl std::fmt::Debug for FixedWidthToken {
750 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
751 fmt.debug_struct("FixedWidthToken")
752 .field("kind", &self.kind())
753 .field("offset", &self.offset())
758 use fixed_width_token::FixedWidthToken;
760 #[derive(Copy, Clone, Debug)]
762 // Nodes which are not useful in constructing a decl are ignored. We keep
763 // track of the SyntaxKind for two reasons.
765 // One is that the parser needs to know the SyntaxKind of a parsed node in
766 // some circumstances (this information is exposed to the parser via an
767 // implementation of `smart_constructors::NodeType`). An adapter called
768 // WithKind exists to provide a `NodeType` implementation for arbitrary
769 // nodes by pairing each node with a SyntaxKind, but in the direct decl
770 // parser, we want to avoid the extra 8 bytes of overhead on each node.
772 // The second reason is that debugging is difficult when nodes are silently
773 // ignored, and providing at least the SyntaxKind of an ignored node helps
774 // in tracking down the reason it was ignored.
777 List(&'a &'a [Node<'a>]),
778 BracketedList(&'a (&'a Pos<'a>, &'a [Node<'a>], &'a Pos<'a>)),
779 Name(&'a (&'a str, &'a Pos<'a>)),
780 XhpName(&'a (&'a str, &'a Pos<'a>)),
781 Variable(&'a (&'a str, &'a Pos<'a>)),
782 QualifiedName(&'a (&'a [Node<'a>], &'a Pos<'a>)),
783 StringLiteral(&'a (&'a BStr, &'a Pos<'a>)), // For shape keys and const expressions.
784 IntLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
785 FloatingLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
786 BooleanLiteral(&'a (&'a str, &'a Pos<'a>)), // For const expressions.
788 XhpEnumTy(&'a (&'a Ty<'a>, &'a [XhpEnumValue<'a>])),
789 ListItem(&'a (Node<'a>, Node<'a>)),
790 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
791 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".
792 FunParam(&'a FunParamDecl<'a>),
793 Attribute(&'a UserAttributeNode<'a>),
794 FunctionHeader(&'a FunctionHeader<'a>),
795 Constructor(&'a ConstructorNode<'a>),
796 Method(&'a MethodNode<'a>),
797 Property(&'a PropertyNode<'a>),
798 EnumUse(&'a Node<'a>),
799 TraitUse(&'a Node<'a>),
800 XhpClassAttributeDeclaration(&'a XhpClassAttributeDeclarationNode<'a>),
801 XhpClassAttribute(&'a XhpClassAttributeNode<'a>),
802 XhpAttributeUse(&'a Node<'a>),
803 TypeConstant(&'a ShallowTypeconst<'a>),
804 ContextConstraint(&'a (ConstraintKind, Node<'a>)),
805 RequireClause(&'a RequireClause<'a>),
806 ClassishBody(&'a &'a [Node<'a>]),
807 TypeParameter(&'a TypeParameterDecl<'a>),
808 TypeConstraint(&'a (ConstraintKind, Node<'a>)),
809 ShapeFieldSpecifier(&'a ShapeFieldNode<'a>),
810 NamespaceUseClause(&'a NamespaceUseClause<'a>),
811 Expr(&'a nast::Expr<'a>),
812 TypeParameters(&'a &'a [&'a Tparam<'a>]),
813 WhereConstraint(&'a WhereConstraint<'a>),
814 RecordField(&'a (Id<'a>, RecordFieldReq)),
816 // Non-ignored, fixed-width tokens (e.g., keywords, operators, braces, etc.).
817 Token(FixedWidthToken),
820 impl<'a> smart_constructors::NodeType for Node<'a> {
823 fn extract(self) -> Self::R {
827 fn is_abstract(&self) -> bool {
828 self.is_token(TokenKind::Abstract)
829 || matches!(self, Node::Ignored(SK::Token(TokenKind::Abstract)))
831 fn is_name(&self) -> bool {
832 matches!(self, Node::Name(..)) || matches!(self, Node::Ignored(SK::Token(TokenKind::Name)))
834 fn is_qualified_name(&self) -> bool {
835 matches!(self, Node::QualifiedName(..)) || matches!(self, Node::Ignored(SK::QualifiedName))
837 fn is_prefix_unary_expression(&self) -> bool {
838 matches!(self, Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(..))))
839 || matches!(self, Node::Ignored(SK::PrefixUnaryExpression))
841 fn is_scope_resolution_expression(&self) -> bool {
844 Node::Expr(aast::Expr(_, _, aast::Expr_::ClassConst(..)))
845 ) || matches!(self, Node::Ignored(SK::ScopeResolutionExpression))
847 fn is_missing(&self) -> bool {
848 matches!(self, Node::Ignored(SK::Missing))
850 fn is_variable_expression(&self) -> bool {
851 matches!(self, Node::Ignored(SK::VariableExpression))
853 fn is_subscript_expression(&self) -> bool {
854 matches!(self, Node::Ignored(SK::SubscriptExpression))
856 fn is_member_selection_expression(&self) -> bool {
857 matches!(self, Node::Ignored(SK::MemberSelectionExpression))
859 fn is_object_creation_expression(&self) -> bool {
860 matches!(self, Node::Ignored(SK::ObjectCreationExpression))
862 fn is_safe_member_selection_expression(&self) -> bool {
863 matches!(self, Node::Ignored(SK::SafeMemberSelectionExpression))
865 fn is_function_call_expression(&self) -> bool {
866 matches!(self, Node::Ignored(SK::FunctionCallExpression))
868 fn is_list_expression(&self) -> bool {
869 matches!(self, Node::Ignored(SK::ListExpression))
874 fn is_token(self, kind: TokenKind) -> bool {
875 self.token_kind() == Some(kind)
878 fn token_kind(self) -> Option<TokenKind> {
880 Node::Token(token) => Some(token.kind()),
885 fn as_slice(self, b: &'a Bump) -> &'a [Self] {
887 Node::List(&items) | Node::BracketedList(&(_, items, _)) => items,
888 n if n.is_ignored() => &[],
889 n => std::slice::from_ref(b.alloc(n)),
893 fn iter<'b>(&'b self) -> NodeIterHelper<'a, 'b>
898 &Node::List(&items) | Node::BracketedList(&(_, items, _)) => {
899 NodeIterHelper::Vec(items.iter())
901 n if n.is_ignored() => NodeIterHelper::Empty,
902 n => NodeIterHelper::Single(n),
906 // The number of elements which would be yielded by `self.iter()`.
907 // Must return the upper bound returned by NodeIterHelper::size_hint.
908 fn len(&self) -> usize {
910 &Node::List(&items) | Node::BracketedList(&(_, items, _)) => items.len(),
911 n if n.is_ignored() => 0,
916 fn as_visibility(&self) -> Option<aast::Visibility> {
917 match self.token_kind() {
918 Some(TokenKind::Private) => Some(aast::Visibility::Private),
919 Some(TokenKind::Protected) => Some(aast::Visibility::Protected),
920 Some(TokenKind::Public) => Some(aast::Visibility::Public),
925 // If this node is a simple unqualified identifier, return its position and text.
926 fn as_id(&self) -> Option<Id<'a>> {
928 Node::Name(&(name, pos)) | Node::XhpName(&(name, pos)) => Some(Id(pos, name)),
933 // If this node is a Variable token, return its position and text.
934 // As an attempt at error recovery (when the dollar sign is omitted), also
935 // return other unqualified identifiers (i.e., the Name token kind).
936 fn as_variable(&self) -> Option<Id<'a>> {
938 Node::Variable(&(name, pos)) | Node::Name(&(name, pos)) => Some(Id(pos, name)),
943 fn is_ignored(&self) -> bool {
944 matches!(self, Node::Ignored(..))
947 fn is_present(&self) -> bool {
952 struct Attributes<'a> {
953 deprecated: Option<&'a str>,
954 reifiable: Option<&'a Pos<'a>>,
960 enforceable: Option<&'a Pos<'a>>,
961 accept_disposable: bool,
962 dynamically_callable: bool,
963 returns_disposable: bool,
965 ifc_attribute: IfcFunDecl<'a>,
970 support_dynamic_type: bool,
971 module: Option<&'a Module_<'a>>,
975 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> DirectDeclSmartConstructors<'a, 'text, S> {
976 fn add_class(&mut self, name: &'a str, decl: &'a shallow_decl_defs::ShallowClass<'a>) {
977 self.decls.add(name, Decl::Class(decl), self.arena);
979 fn add_fun(&mut self, name: &'a str, decl: &'a typing_defs::FunElt<'a>) {
980 self.decls.add(name, Decl::Fun(decl), self.arena);
982 fn add_typedef(&mut self, name: &'a str, decl: &'a typing_defs::TypedefType<'a>) {
983 self.decls.add(name, Decl::Typedef(decl), self.arena);
985 fn add_const(&mut self, name: &'a str, decl: &'a typing_defs::ConstDecl<'a>) {
986 self.decls.add(name, Decl::Const(decl), self.arena);
988 fn add_record(&mut self, name: &'a str, decl: &'a typing_defs::RecordDefType<'a>) {
989 self.decls.add(name, Decl::Record(decl), self.arena);
993 fn concat(&self, str1: &str, str2: &str) -> &'a str {
994 concat(self.arena, str1, str2)
997 fn token_bytes(&self, token: &CompactToken) -> &'text [u8] {
1000 .sub(token.start_offset(), token.width())
1003 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1004 // the same data. Otherwise, copy the slice into our arena using
1005 // String::from_utf8_lossy_in, and return a reference to the arena str.
1006 fn str_from_utf8(&self, slice: &'text [u8]) -> &'a str {
1007 if let Ok(s) = std::str::from_utf8(slice) {
1008 self.source_text_allocator.alloc(s)
1010 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1014 // Check that the slice is valid UTF-8. If it is, return a &str referencing
1015 // the same data. Otherwise, copy the slice into our arena using
1016 // String::from_utf8_lossy_in, and return a reference to the arena str.
1017 fn str_from_utf8_for_bytes_in_arena(&self, slice: &'a [u8]) -> &'a str {
1018 if let Ok(s) = std::str::from_utf8(slice) {
1021 String::from_utf8_lossy_in(slice, self.arena).into_bump_str()
1027 pos1: impl Into<Option<&'a Pos<'a>>>,
1028 pos2: impl Into<Option<&'a Pos<'a>>>,
1030 match (pos1.into(), pos2.into()) {
1031 (None, None) => Pos::none(),
1032 (Some(pos), None) | (None, Some(pos)) => pos,
1033 (Some(pos1), Some(pos2)) => match (pos1.is_none(), pos2.is_none()) {
1034 (true, true) => Pos::none(),
1035 (true, false) => pos2,
1036 (false, true) => pos1,
1037 (false, false) => Pos::merge_without_checking_filename(self.arena, pos1, pos2),
1042 fn merge_positions(&self, node1: Node<'a>, node2: Node<'a>) -> &'a Pos<'a> {
1043 self.merge(self.get_pos(node1), self.get_pos(node2))
1046 fn pos_from_slice(&self, nodes: &[Node<'a>]) -> &'a Pos<'a> {
1047 nodes.iter().fold(Pos::none(), |acc, &node| {
1048 self.merge(acc, self.get_pos(node))
1052 fn get_pos(&self, node: Node<'a>) -> &'a Pos<'a> {
1053 self.get_pos_opt(node).unwrap_or(Pos::none())
1056 fn get_pos_opt(&self, node: Node<'a>) -> Option<&'a Pos<'a>> {
1057 let pos = match node {
1058 Node::Name(&(_, pos)) | Node::Variable(&(_, pos)) => pos,
1059 Node::Ty(ty) => return ty.get_pos(),
1060 Node::XhpName(&(_, pos)) => pos,
1061 Node::QualifiedName(&(_, pos)) => pos,
1062 Node::IntLiteral(&(_, pos))
1063 | Node::FloatingLiteral(&(_, pos))
1064 | Node::StringLiteral(&(_, pos))
1065 | Node::BooleanLiteral(&(_, pos)) => pos,
1066 Node::ListItem(&(fst, snd)) => self.merge_positions(fst, snd),
1067 Node::List(items) => self.pos_from_slice(&items),
1068 Node::BracketedList(&(first_pos, inner_list, second_pos)) => self.merge(
1070 self.merge(self.pos_from_slice(inner_list), second_pos),
1072 Node::Expr(&aast::Expr(_, pos, _)) => pos,
1073 Node::Token(token) => self.token_pos(token),
1076 if pos.is_none() { None } else { Some(pos) }
1079 fn token_pos(&self, token: FixedWidthToken) -> &'a Pos<'a> {
1080 let start = token.offset();
1081 let end = start + token.width();
1082 let start = self.source_text.offset_to_file_pos_triple(start);
1083 let end = self.source_text.offset_to_file_pos_triple(end);
1084 Pos::from_lnum_bol_cnum(self.arena, self.filename, start, end)
1087 fn node_to_expr(&self, node: Node<'a>) -> Option<&'a nast::Expr<'a>> {
1088 let expr_ = match node {
1089 Node::Expr(expr) => return Some(expr),
1090 Node::IntLiteral(&(s, _)) => aast::Expr_::Int(s),
1091 Node::FloatingLiteral(&(s, _)) => aast::Expr_::Float(s),
1092 Node::StringLiteral(&(s, _)) => aast::Expr_::String(s),
1093 Node::BooleanLiteral((s, _)) => {
1094 if s.eq_ignore_ascii_case("true") {
1100 Node::Token(t) if t.kind() == TokenKind::NullLiteral => aast::Expr_::Null,
1101 Node::Name(..) | Node::QualifiedName(..) => {
1102 aast::Expr_::Id(self.alloc(self.elaborate_const_id(self.expect_name(node)?)))
1106 let pos = self.get_pos(node);
1107 Some(self.alloc(aast::Expr((), pos, expr_)))
1110 fn node_to_non_ret_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1111 self.node_to_ty_(node, false)
1114 fn node_to_ty(&self, node: Node<'a>) -> Option<&'a Ty<'a>> {
1115 self.node_to_ty_(node, true)
1118 fn node_to_ty_(&self, node: Node<'a>, allow_non_ret_ty: bool) -> Option<&'a Ty<'a>> {
1120 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tvoid))) if !allow_non_ret_ty => {
1121 Some(self.alloc(Ty(reason, Ty_::Terr)))
1123 Node::Ty(Ty(reason, Ty_::Tprim(aast::Tprim::Tnoreturn))) if !allow_non_ret_ty => {
1124 Some(self.alloc(Ty(reason, Ty_::Terr)))
1126 Node::Ty(ty) => Some(ty),
1127 Node::Expr(expr) => {
1128 fn expr_to_ty<'a>(arena: &'a Bump, expr: &'a nast::Expr<'a>) -> Option<Ty_<'a>> {
1131 Null => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tnull))),
1132 This => Some(Ty_::Tthis),
1133 True | False => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tbool))),
1134 Int(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tint))),
1135 Float(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tfloat))),
1136 String(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1137 String2(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1138 PrefixedString(_) => Some(Ty_::Tprim(arena.alloc(aast::Tprim::Tstring))),
1139 Unop(&(_op, expr)) => expr_to_ty(arena, expr),
1140 Hole(&(expr, _, _, _)) => expr_to_ty(arena, expr),
1142 ArrayGet(_) | As(_) | Await(_) | Binop(_) | Call(_) | Cast(_)
1143 | ClassConst(_) | ClassGet(_) | Clone(_) | Collection(_) | Darray(_)
1144 | Dollardollar(_) | Efun(_) | Eif(_) | EnumClassLabel(_) | ETSplice(_)
1145 | ExpressionTree(_) | FunctionPointer(_) | FunId(_) | Id(_) | Import(_)
1146 | Is(_) | KeyValCollection(_) | Lfun(_) | List(_) | Lplaceholder(_)
1147 | Lvar(_) | MethodCaller(_) | MethodId(_) | New(_) | ObjGet(_)
1148 | Omitted | Pair(_) | Pipe(_) | ReadonlyExpr(_) | Record(_) | Shape(_)
1149 | SmethodId(_) | Tuple(_) | Upcast(_) | ValCollection(_) | Varray(_)
1150 | Xml(_) | Yield(_) => None,
1154 self.alloc(Reason::witness_from_decl(expr.1)),
1155 expr_to_ty(self.arena, expr)?,
1158 Node::IntLiteral((_, pos)) => Some(self.alloc(Ty(
1159 self.alloc(Reason::witness_from_decl(pos)),
1160 Ty_::Tprim(self.alloc(aast::Tprim::Tint)),
1162 Node::FloatingLiteral((_, pos)) => Some(self.alloc(Ty(
1163 self.alloc(Reason::witness_from_decl(pos)),
1164 Ty_::Tprim(self.alloc(aast::Tprim::Tfloat)),
1166 Node::StringLiteral((_, pos)) => Some(self.alloc(Ty(
1167 self.alloc(Reason::witness_from_decl(pos)),
1168 Ty_::Tprim(self.alloc(aast::Tprim::Tstring)),
1170 Node::BooleanLiteral((_, pos)) => Some(self.alloc(Ty(
1171 self.alloc(Reason::witness_from_decl(pos)),
1172 Ty_::Tprim(self.alloc(aast::Tprim::Tbool)),
1174 Node::Token(t) if t.kind() == TokenKind::Varray => {
1175 let pos = self.token_pos(t);
1176 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1177 let ty_ = Ty_::Tapply(self.alloc((
1178 (self.token_pos(t), naming_special_names::collections::VEC),
1181 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1183 Node::Token(t) if t.kind() == TokenKind::Darray => {
1184 let pos = self.token_pos(t);
1185 let tany = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1186 let ty_ = Ty_::Tapply(self.alloc((
1187 (self.token_pos(t), naming_special_names::collections::DICT),
1188 self.alloc([tany, tany]),
1190 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1192 Node::Token(t) if t.kind() == TokenKind::This => {
1193 Some(self.alloc(Ty(self.alloc(Reason::hint(self.token_pos(t))), Ty_::Tthis)))
1195 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1196 let pos = self.token_pos(t);
1198 self.alloc(Reason::hint(pos)),
1199 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1202 // In coeffects contexts, we get types like `ctx $f` or `$v::C`.
1203 // Node::Variable is used for the `$f` and `$v`, so that we don't
1204 // incorrectly attempt to elaborate them as names.
1205 Node::Variable(&(name, pos)) => Some(self.alloc(Ty(
1206 self.alloc(Reason::hint(pos)),
1207 Ty_::Tapply(self.alloc(((pos, name), &[][..]))),
1210 let Id(pos, name) = self.expect_name(node)?;
1211 let reason = self.alloc(Reason::hint(pos));
1212 let ty_ = if self.is_type_param_in_scope(name) {
1213 // TODO (T69662957) must fill type args of Tgeneric
1214 Ty_::Tgeneric(self.alloc((name, &[])))
1217 "nothing" => Ty_::Tunion(&[]),
1219 if self.opts.everything_sdt {
1220 Ty_::Tsupportdynamic
1225 "dynamic" => Ty_::Tdynamic,
1226 "supportdynamic" => Ty_::Tsupportdynamic,
1227 "varray_or_darray" | "vec_or_dict" => {
1228 let key_type = self.vec_or_dict_key(pos);
1229 let value_type = self.alloc(Ty(self.alloc(Reason::hint(pos)), TANY_));
1230 Ty_::TvecOrDict(self.alloc((key_type, value_type)))
1234 let name = self.elaborate_raw_id(name);
1235 Ty_::Tapply(self.alloc(((pos, name), &[][..])))
1239 Some(self.alloc(Ty(reason, ty_)))
1244 fn to_attributes(&self, node: Node<'a>) -> Attributes<'a> {
1245 let mut attributes = Attributes {
1254 accept_disposable: false,
1255 dynamically_callable: false,
1256 returns_disposable: false,
1258 ifc_attribute: default_ifc_fun_decl(),
1263 support_dynamic_type: false,
1268 let nodes = match node {
1269 Node::List(&nodes) | Node::BracketedList(&(_, nodes, _)) => nodes,
1270 _ => return attributes,
1273 let mut ifc_already_policied = false;
1275 // Iterate in reverse, to match the behavior of OCaml decl in error conditions.
1276 for attribute in nodes.iter().rev() {
1277 if let Node::Attribute(attribute) = attribute {
1278 match attribute.name.1.as_ref() {
1280 attributes.deprecated = attribute
1281 .string_literal_params
1283 .map(|&x| self.str_from_utf8_for_bytes_in_arena(x));
1285 "__Reifiable" => attributes.reifiable = Some(attribute.name.0),
1287 attributes.late_init = true;
1290 attributes.const_ = true;
1293 attributes.lsb = true;
1296 attributes.memoizelsb = true;
1299 attributes.override_ = true;
1301 "__Enforceable" => {
1302 attributes.enforceable = Some(attribute.name.0);
1304 "__AcceptDisposable" => {
1305 attributes.accept_disposable = true;
1307 "__DynamicallyCallable" => {
1308 attributes.dynamically_callable = true;
1310 "__ReturnDisposable" => {
1311 attributes.returns_disposable = true;
1314 attributes.php_std_lib = true;
1317 let string_literal_params = || {
1319 .string_literal_params
1321 .map(|&x| self.str_from_utf8_for_bytes_in_arena(x))
1323 // Take the classname param by default
1324 attributes.ifc_attribute =
1325 IfcFunDecl::FDPolicied(attribute.classname_params.first().map_or_else(
1326 string_literal_params, // default
1327 |&x| Some(x.name.1), // f
1329 ifc_already_policied = true;
1332 if !ifc_already_policied {
1333 attributes.ifc_attribute = IfcFunDecl::FDInferFlows;
1337 attributes.external = true;
1340 attributes.can_call = true;
1342 naming_special_names::user_attributes::VIA_LABEL => {
1343 attributes.via_label = true;
1346 attributes.soft = true;
1348 "__SupportDynamicType" => {
1349 attributes.support_dynamic_type = true;
1352 attributes.module = attribute
1353 .string_literal_params
1355 .map(|&x| self.str_from_utf8_for_bytes_in_arena(x))
1357 let mut chars = x.split('.');
1358 match chars.next() {
1361 let rest = chars.collect::<std::vec::Vec<_>>();
1362 Some(self.alloc(Module_(s, self.alloc(rest))))
1368 attributes.internal = true;
1378 // Limited version of node_to_ty that matches behavior of Decl_utils.infer_const
1379 fn infer_const(&self, name: Node<'a>, node: Node<'a>) -> Option<&'a Ty<'a>> {
1381 Node::StringLiteral(_)
1382 | Node::BooleanLiteral(_)
1383 | Node::IntLiteral(_)
1384 | Node::FloatingLiteral(_)
1385 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uminus, _))))
1386 | Node::Expr(aast::Expr(_, _, aast::Expr_::Unop(&(Uop::Uplus, _))))
1387 | Node::Expr(aast::Expr(_, _, aast::Expr_::String(..))) => self.node_to_ty(node),
1388 Node::Token(t) if t.kind() == TokenKind::NullLiteral => {
1389 let pos = self.token_pos(t);
1391 self.alloc(Reason::witness_from_decl(pos)),
1392 Ty_::Tprim(self.alloc(aast::Tprim::Tnull)),
1395 _ => Some(self.tany_with_pos(self.get_pos(name))),
1399 fn pop_type_params(&mut self, node: Node<'a>) -> &'a [&'a Tparam<'a>] {
1401 Node::TypeParameters(tparams) => {
1402 Rc::make_mut(&mut self.type_parameters).pop().unwrap();
1409 fn ret_from_fun_kind(&self, kind: FunKind, type_: &'a Ty<'a>) -> &'a Ty<'a> {
1410 let pos = type_.get_pos().unwrap_or_else(|| Pos::none());
1412 FunKind::FAsyncGenerator => self.alloc(Ty(
1413 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1414 Ty_::Tapply(self.alloc((
1415 (pos, naming_special_names::classes::ASYNC_GENERATOR),
1416 self.alloc([type_, type_, type_]),
1419 FunKind::FGenerator => self.alloc(Ty(
1420 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1421 Ty_::Tapply(self.alloc((
1422 (pos, naming_special_names::classes::GENERATOR),
1423 self.alloc([type_, type_, type_]),
1426 FunKind::FAsync => self.alloc(Ty(
1427 self.alloc(Reason::RretFunKindFromDecl(self.alloc((pos, kind)))),
1428 Ty_::Tapply(self.alloc((
1429 (pos, naming_special_names::classes::AWAITABLE),
1430 self.alloc([type_]),
1437 fn is_type_param_in_scope(&self, name: &str) -> bool {
1438 self.type_parameters.iter().any(|tps| tps.contains(name))
1441 fn as_fun_implicit_params(
1443 capability: Node<'a>,
1444 default_pos: &'a Pos<'a>,
1445 ) -> &'a FunImplicitParams<'a> {
1446 /* Note: do not simplify intersections, keep empty / singleton intersections
1447 * for coeffect contexts
1449 let capability = match self.node_to_ty(capability) {
1450 Some(ty) => CapTy(ty),
1451 None => CapDefaults(default_pos),
1453 self.alloc(FunImplicitParams { capability })
1459 attributes: Node<'a>,
1460 header: &'a FunctionHeader<'a>,
1462 ) -> Option<(PosId<'a>, &'a Ty<'a>, &'a [ShallowProp<'a>])> {
1463 let id_opt = match (is_method, header.name) {
1464 (true, Node::Token(t)) if t.kind() == TokenKind::Construct => {
1465 let pos = self.token_pos(t);
1466 Some(Id(pos, naming_special_names::members::__CONSTRUCT))
1468 (true, _) => self.expect_name(header.name),
1469 (false, _) => self.elaborate_defined_id(header.name),
1471 let id = id_opt.unwrap_or(Id(self.get_pos(header.name), ""));
1472 let (params, properties, arity) = self.as_fun_params(header.param_list)?;
1473 let f_pos = self.get_pos(header.name);
1474 let implicit_params = self.as_fun_implicit_params(header.capability, f_pos);
1476 let type_ = match header.name {
1477 Node::Token(t) if t.kind() == TokenKind::Construct => {
1478 let pos = self.token_pos(t);
1480 self.alloc(Reason::witness_from_decl(pos)),
1481 Ty_::Tprim(self.alloc(aast::Tprim::Tvoid)),
1485 .node_to_ty(header.ret_hint)
1486 .unwrap_or_else(|| self.tany_with_pos(f_pos)),
1491 .any(|n| n.is_token(TokenKind::Async));
1492 let readonly = header
1495 .any(|n| n.is_token(TokenKind::Readonly));
1497 let fun_kind = if body.iter().any(|node| node.is_token(TokenKind::Yield)) {
1499 FunKind::FAsyncGenerator
1510 let type_ = if !header.ret_hint.is_present() {
1511 self.ret_from_fun_kind(fun_kind, type_)
1515 let attributes = self.to_attributes(attributes);
1516 // TODO(hrust) Put this in a helper. Possibly do this for all flags.
1517 let mut flags = match fun_kind {
1518 FunKind::FSync => FunTypeFlags::empty(),
1519 FunKind::FAsync => FunTypeFlags::ASYNC,
1520 FunKind::FGenerator => FunTypeFlags::GENERATOR,
1521 FunKind::FAsyncGenerator => FunTypeFlags::ASYNC | FunTypeFlags::GENERATOR,
1524 if attributes.returns_disposable {
1525 flags |= FunTypeFlags::RETURN_DISPOSABLE;
1527 if header.readonly_return.is_token(TokenKind::Readonly) {
1528 flags |= FunTypeFlags::RETURNS_READONLY;
1531 flags |= FunTypeFlags::READONLY_THIS
1534 let ifc_decl = attributes.ifc_attribute;
1536 // Pop the type params stack only after creating all inner types.
1537 let tparams = self.pop_type_params(header.type_params);
1539 let where_constraints =
1540 self.slice(header.where_constraints.iter().filter_map(|&x| match x {
1541 Node::WhereConstraint(x) => Some(x),
1545 let (params, tparams, implicit_params, where_constraints) =
1546 self.rewrite_effect_polymorphism(params, tparams, implicit_params, where_constraints);
1548 let ft = self.alloc(FunType {
1554 ret: self.alloc(PossiblyEnforcedTy {
1555 enforced: Enforcement::Unenforced,
1562 let ty = self.alloc(Ty(
1563 self.alloc(Reason::witness_from_decl(id.0)),
1566 Some((id.into(), ty, properties))
1572 ) -> Option<(&'a FunParams<'a>, &'a [ShallowProp<'a>], FunArity<'a>)> {
1574 Node::List(nodes) => {
1575 let mut params = Vec::with_capacity_in(nodes.len(), self.arena);
1576 let mut properties = Vec::new_in(self.arena);
1577 let mut arity = FunArity::Fstandard;
1578 for node in nodes.iter() {
1580 Node::FunParam(&FunParamDecl {
1591 let attributes = self.to_attributes(attributes);
1593 if let Some(visibility) = visibility.as_visibility() {
1594 let name = name.unwrap_or("");
1595 let name = strip_dollar_prefix(name);
1596 let mut flags = PropFlags::empty();
1597 flags.set(PropFlags::CONST, attributes.const_);
1598 flags.set(PropFlags::NEEDS_INIT, self.file_mode != Mode::Mhhi);
1599 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
1600 flags.set(PropFlags::READONLY, readonly);
1601 properties.push(ShallowProp {
1604 type_: self.node_to_ty(hint),
1605 visibility: if attributes.internal
1606 && visibility == aast::Visibility::Public
1608 aast::Visibility::Internal
1616 let type_ = if hint.is_ignored() {
1617 self.tany_with_pos(pos)
1619 self.node_to_ty(hint)?
1621 // These are illegal here--they can only be used on
1622 // parameters in a function type hint (see
1623 // make_closure_type_specifier and unwrap_mutability).
1624 // Unwrap them here anyway for better error recovery.
1625 let type_ = match type_ {
1626 Ty(_, Ty_::Tapply(((_, "\\Mutable"), [t]))) => t,
1627 Ty(_, Ty_::Tapply(((_, "\\OwnedMutable"), [t]))) => t,
1628 Ty(_, Ty_::Tapply(((_, "\\MaybeMutable"), [t]))) => t,
1631 let mut flags = FunParamFlags::empty();
1632 if attributes.accept_disposable {
1633 flags |= FunParamFlags::ACCEPT_DISPOSABLE
1635 if attributes.external {
1636 flags |= FunParamFlags::IFC_EXTERNAL
1638 if attributes.can_call {
1639 flags |= FunParamFlags::IFC_CAN_CALL
1641 if attributes.via_label {
1642 flags |= FunParamFlags::VIA_LABEL
1645 flags |= FunParamFlags::READONLY
1648 ParamMode::FPinout => {
1649 flags |= FunParamFlags::INOUT;
1651 ParamMode::FPnormal => {}
1654 if initializer.is_present() {
1655 flags |= FunParamFlags::HAS_DEFAULT;
1657 let variadic = initializer.is_ignored() && variadic;
1658 let type_ = if variadic {
1660 self.alloc(if name.is_some() {
1661 Reason::RvarParamFromDecl(pos)
1663 Reason::witness_from_decl(pos)
1670 let param = self.alloc(FunParam {
1673 type_: self.alloc(PossiblyEnforcedTy {
1674 enforced: Enforcement::Unenforced,
1679 arity = match arity {
1680 FunArity::Fstandard if variadic => FunArity::Fvariadic(param),
1691 params.into_bump_slice(),
1692 properties.into_bump_slice(),
1696 n if n.is_ignored() => Some((&[], &[], FunArity::Fstandard)),
1701 fn make_shape_field_name(&self, name: Node<'a>) -> Option<ShapeFieldName<'a>> {
1703 Node::StringLiteral(&(s, pos)) => ShapeFieldName::SFlitStr(self.alloc((pos, s))),
1704 // TODO: OCaml decl produces SFlitStr here instead of SFlitInt, so
1705 // we must also. Looks like int literal keys have become a parse
1706 // error--perhaps that's why.
1707 Node::IntLiteral(&(s, pos)) => ShapeFieldName::SFlitStr(self.alloc((pos, s.into()))),
1708 Node::Expr(aast::Expr(
1711 aast::Expr_::ClassConst(&(
1712 aast::ClassId(_, _, aast::ClassId_::CI(&class_name)),
1715 )) => ShapeFieldName::SFclassConst(self.alloc((class_name, const_name))),
1716 Node::Expr(aast::Expr(
1719 aast::Expr_::ClassConst(&(
1720 aast::ClassId(_, pos, aast::ClassId_::CIself),
1723 )) => ShapeFieldName::SFclassConst(self.alloc((
1726 self.classish_name_builder.get_current_classish_name()?.0,
1736 fn make_t_shape_field_name(&mut self, ShapeField(field): &ShapeField<'a>) -> TShapeField<'a> {
1737 TShapeField(match field {
1738 ShapeFieldName::SFlitInt(&(pos, x)) => {
1739 TshapeFieldName::TSFlitInt(self.alloc(PosString(pos, x)))
1741 ShapeFieldName::SFlitStr(&(pos, x)) => {
1742 TshapeFieldName::TSFlitStr(self.alloc(PosByteString(pos, x)))
1744 ShapeFieldName::SFclassConst(&(id, &(pos, x))) => {
1745 TshapeFieldName::TSFclassConst(self.alloc((id.into(), PosString(pos, x))))
1753 type_arguments: Node<'a>,
1754 pos_to_merge: &'a Pos<'a>,
1756 let type_arguments = self.slice(
1759 .filter_map(|&node| self.node_to_ty(node)),
1762 let pos = self.merge(base_ty.0, pos_to_merge);
1764 // OCaml decl creates a capability with a hint pointing to the entire
1765 // type (i.e., pointing to `Rx<(function(): void)>` rather than just
1766 // `(function(): void)`), so we extend the hint position similarly here.
1767 let extend_capability_pos = |implicit_params: &'a FunImplicitParams<'_>| {
1768 let capability = match implicit_params.capability {
1770 let ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), ty.1));
1773 CapDefaults(_) => CapDefaults(pos),
1775 self.alloc(FunImplicitParams {
1781 let ty_ = match (base_ty, type_arguments) {
1782 ((_, name), &[&Ty(_, Ty_::Tfun(f))]) if name == "\\Pure" => {
1783 Ty_::Tfun(self.alloc(FunType {
1784 implicit_params: extend_capability_pos(f.implicit_params),
1788 _ => Ty_::Tapply(self.alloc((base_ty, type_arguments))),
1791 self.hint_ty(pos, ty_)
1794 fn hint_ty(&self, pos: &'a Pos<'a>, ty_: Ty_<'a>) -> Node<'a> {
1795 Node::Ty(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
1798 fn prim_ty(&self, tprim: aast::Tprim, pos: &'a Pos<'a>) -> Node<'a> {
1799 self.hint_ty(pos, Ty_::Tprim(self.alloc(tprim)))
1802 fn tany_with_pos(&self, pos: &'a Pos<'a>) -> &'a Ty<'a> {
1803 self.alloc(Ty(self.alloc(Reason::witness_from_decl(pos)), TANY_))
1806 /// The type used when a `vec_or_dict` typehint is missing its key type argument.
1807 fn vec_or_dict_key(&self, pos: &'a Pos<'a>) -> &'a Ty<'a> {
1809 self.alloc(Reason::RvecOrDictKey(pos)),
1810 Ty_::Tprim(self.alloc(aast::Tprim::Tarraykey)),
1814 fn source_text_at_pos(&self, pos: &'a Pos<'a>) -> &'text [u8] {
1815 let start = pos.start_cnum();
1816 let end = pos.end_cnum();
1817 self.source_text.source_text().sub(start, end - start)
1820 // While we usually can tell whether to allocate a Tapply or Tgeneric based
1821 // on our type_parameters stack, *constraints* on type parameters may
1822 // reference type parameters which we have not parsed yet. When constructing
1823 // a type parameter list, we use this function to rewrite the type of each
1824 // constraint, considering the full list of type parameters to be in scope.
1825 fn convert_tapply_to_tgeneric(&self, ty: &'a Ty<'a>) -> &'a Ty<'a> {
1826 let ty_ = match ty.1 {
1827 Ty_::Tapply(&(id, targs)) => {
1828 let converted_targs = self.slice(
1831 .map(|&targ| self.convert_tapply_to_tgeneric(targ)),
1833 match self.tapply_should_be_tgeneric(ty.0, id) {
1834 Some(name) => Ty_::Tgeneric(self.alloc((name, converted_targs))),
1835 None => Ty_::Tapply(self.alloc((id, converted_targs))),
1838 Ty_::Tlike(ty) => Ty_::Tlike(self.convert_tapply_to_tgeneric(ty)),
1839 Ty_::Toption(ty) => Ty_::Toption(self.convert_tapply_to_tgeneric(ty)),
1840 Ty_::Tfun(fun_type) => {
1841 let convert_param = |param: &'a FunParam<'a>| {
1842 self.alloc(FunParam {
1843 type_: self.alloc(PossiblyEnforcedTy {
1844 enforced: param.type_.enforced,
1845 type_: self.convert_tapply_to_tgeneric(param.type_.type_),
1850 let arity = match fun_type.arity {
1851 FunArity::Fstandard => FunArity::Fstandard,
1852 FunArity::Fvariadic(param) => FunArity::Fvariadic(convert_param(param)),
1854 let params = self.slice(fun_type.params.iter().copied().map(convert_param));
1855 let implicit_params = fun_type.implicit_params;
1856 let ret = self.alloc(PossiblyEnforcedTy {
1857 enforced: fun_type.ret.enforced,
1858 type_: self.convert_tapply_to_tgeneric(fun_type.ret.type_),
1860 Ty_::Tfun(self.alloc(FunType {
1868 Ty_::Tshape(&(kind, fields)) => {
1869 let mut converted_fields = AssocListMut::with_capacity_in(fields.len(), self.arena);
1870 for (&name, ty) in fields.iter() {
1871 converted_fields.insert(
1873 self.alloc(ShapeFieldType {
1874 optional: ty.optional,
1875 ty: self.convert_tapply_to_tgeneric(ty.ty),
1879 Ty_::Tshape(self.alloc((kind, converted_fields.into())))
1881 Ty_::Tdarray(&(tk, tv)) => Ty_::Tdarray(self.alloc((
1882 self.convert_tapply_to_tgeneric(tk),
1883 self.convert_tapply_to_tgeneric(tv),
1885 Ty_::Tvarray(ty) => Ty_::Tvarray(self.convert_tapply_to_tgeneric(ty)),
1886 Ty_::TvarrayOrDarray(&(tk, tv)) => Ty_::TvarrayOrDarray(self.alloc((
1887 self.convert_tapply_to_tgeneric(tk),
1888 self.convert_tapply_to_tgeneric(tv),
1890 Ty_::TvecOrDict(&(tk, tv)) => Ty_::TvecOrDict(self.alloc((
1891 self.convert_tapply_to_tgeneric(tk),
1892 self.convert_tapply_to_tgeneric(tv),
1894 Ty_::Ttuple(tys) => Ty_::Ttuple(
1897 .map(|&targ| self.convert_tapply_to_tgeneric(targ)),
1902 self.alloc(Ty(ty.0, ty_))
1905 // This is the logic for determining if convert_tapply_to_tgeneric should turn
1906 // a Tapply into a Tgeneric
1907 fn tapply_should_be_tgeneric(&self, reason: &'a Reason<'a>, id: PosId<'a>) -> Option<&'a str> {
1908 match reason.pos() {
1909 // If the name contained a namespace delimiter in the original
1910 // source text, then it can't have referred to a type parameter
1911 // (since type parameters cannot be namespaced).
1913 if self.source_text_at_pos(pos).contains(&b'\\') {
1917 None => return None,
1919 // However, the direct decl parser will unconditionally prefix
1920 // the name with the current namespace (as it does for any
1921 // Tapply). We need to remove it.
1922 match id.1.rsplit('\\').next() {
1923 Some(name) if self.is_type_param_in_scope(name) => return Some(name),
1928 fn rewrite_taccess_reasons(&self, ty: &'a Ty<'a>, r: &'a Reason<'a>) -> &'a Ty<'a> {
1929 let ty_ = match ty.1 {
1930 Ty_::Taccess(&TaccessType(ty, id)) => {
1931 Ty_::Taccess(self.alloc(TaccessType(self.rewrite_taccess_reasons(ty, r), id)))
1935 self.alloc(Ty(r, ty_))
1938 fn user_attribute_to_decl(
1940 attr: &UserAttributeNode<'a>,
1941 ) -> &'a shallow_decl_defs::UserAttribute<'a> {
1942 self.alloc(shallow_decl_defs::UserAttribute {
1943 name: attr.name.into(),
1944 classname_params: self.slice(attr.classname_params.iter().map(|p| p.name.1)),
1948 fn namespace_use_kind(use_kind: &Node<'_>) -> Option<NamespaceUseKind> {
1949 match use_kind.token_kind() {
1950 Some(TokenKind::Const) => None,
1951 Some(TokenKind::Function) => None,
1952 Some(TokenKind::Type) => Some(NamespaceUseKind::Type),
1953 Some(TokenKind::Namespace) => Some(NamespaceUseKind::Namespace),
1954 _ if !use_kind.is_present() => Some(NamespaceUseKind::Mixed),
1959 fn has_polymorphic_context(contexts: &[&Ty<'_>]) -> bool {
1960 contexts.iter().any(|&ty| match ty.1 {
1961 Ty_::Tapply((root, &[])) // Hfun_context in the AST
1962 | Ty_::Taccess(TaccessType(Ty(_, Ty_::Tapply((root, &[]))), _)) => root.1.contains('$'),
1963 | Ty_::Taccess(TaccessType(t, _)) => Self::taccess_root_is_generic(t),
1968 fn ctx_generic_for_fun(&self, name: &str) -> &'a str {
1969 bumpalo::format!(in self.arena, "T/[ctx {}]", name).into_bump_str()
1972 fn ctx_generic_for_dependent(&self, name: &str, cst: &str) -> &'a str {
1973 bumpalo::format!(in self.arena, "T/[{}::{}]", name, cst).into_bump_str()
1976 // Note: the reason for the divergence between this and the lowerer is that
1977 // hint Haccess is a flat list, whereas decl ty Taccess is a tree.
1978 fn taccess_root_is_generic(ty: &Ty<'_>) -> bool {
1980 Ty(_, Ty_::Tgeneric((_, &[]))) => true,
1981 Ty(_, Ty_::Taccess(&TaccessType(t, _))) => Self::taccess_root_is_generic(t),
1986 fn ctx_generic_for_generic_taccess_inner(&self, ty: &Ty<'_>, cst: &str) -> std::string::String {
1987 let left = match ty {
1988 Ty(_, Ty_::Tgeneric((name, &[]))) => name.to_string(),
1989 Ty(_, Ty_::Taccess(&TaccessType(ty, cst))) => {
1990 self.ctx_generic_for_generic_taccess_inner(ty, cst.1)
1992 _ => panic!("Unexpected element in Taccess"),
1994 format!("{}::{}", left, cst)
1996 fn ctx_generic_for_generic_taccess(&self, ty: &Ty<'_>, cst: &str) -> &'a str {
1997 bumpalo::format!(in self.arena, "T/[{}]", self.ctx_generic_for_generic_taccess_inner(ty, cst))
2001 fn rewrite_effect_polymorphism(
2003 params: &'a [&'a FunParam<'a>],
2004 tparams: &'a [&'a Tparam<'a>],
2005 implicit_params: &'a FunImplicitParams<'a>,
2006 where_constraints: &'a [&'a WhereConstraint<'a>],
2008 &'a [&'a FunParam<'a>],
2009 &'a [&'a Tparam<'a>],
2010 &'a FunImplicitParams<'a>,
2011 &'a [&'a WhereConstraint<'a>],
2013 let (cap_reason, context_tys) = match implicit_params.capability {
2014 CapTy(&Ty(r, Ty_::Tintersection(tys))) if Self::has_polymorphic_context(tys) => {
2017 CapTy(ty) if Self::has_polymorphic_context(&[ty]) => {
2018 (ty.0, std::slice::from_ref(self.alloc(ty)))
2020 _ => return (params, tparams, implicit_params, where_constraints),
2022 let tp = |name, constraints| {
2024 variance: Variance::Invariant,
2028 reified: aast::ReifyKind::Erased,
2029 user_attributes: &[],
2033 // For a polymorphic context with form `ctx $f` (represented here as
2034 // `Tapply "$f"`), add a type parameter named `Tctx$f`, and rewrite the
2035 // parameter `(function (ts)[_]: t) $f` as `(function (ts)[Tctx$f]: t) $f`
2036 let rewrite_fun_ctx =
2037 |tparams: &mut Vec<'_, &'a Tparam<'a>>, ty: &Ty<'a>, param_name: &str| -> Ty<'a> {
2038 let ft = match ty.1 {
2039 Ty_::Tfun(ft) => ft,
2040 _ => return ty.clone(),
2042 let cap_ty = match ft.implicit_params.capability {
2043 CapTy(&Ty(_, Ty_::Tintersection(&[ty]))) | CapTy(ty) => ty,
2044 _ => return ty.clone(),
2046 let pos = match cap_ty.1 {
2047 Ty_::Tapply(((pos, "_"), _)) => pos,
2048 _ => return ty.clone(),
2050 let name = self.ctx_generic_for_fun(param_name);
2051 let tparam = tp((pos, name), &[]);
2052 tparams.push(tparam);
2053 let cap_ty = self.alloc(Ty(cap_ty.0, Ty_::Tgeneric(self.alloc((name, &[])))));
2054 let ft = self.alloc(FunType {
2055 implicit_params: self.alloc(FunImplicitParams {
2056 capability: CapTy(cap_ty),
2060 Ty(ty.0, Ty_::Tfun(ft))
2063 // For a polymorphic context with form `$g::C`, if we have a function
2064 // parameter `$g` with type `G` (where `G` is not a type parameter),
2065 // - add a type parameter constrained by $g's type: `T/$g as G`
2066 // - replace $g's type hint (`G`) with the new type parameter `T/$g`
2067 // Then, for each polymorphic context with form `$g::C`,
2068 // - add a type parameter `T/[$g::C]`
2069 // - add a where constraint `T/[$g::C] = T$g :: C`
2070 let rewrite_arg_ctx = |
2071 tparams: &mut Vec<'_, &'a Tparam<'a>>,
2072 where_constraints: &mut Vec<'_, &'a WhereConstraint<'a>>,
2074 param_pos: &'a Pos<'a>,
2076 context_reason: &'a Reason<'a>,
2079 let rewritten_ty = match ty.1 {
2080 // If the type hint for this function parameter is a type
2081 // parameter introduced in this function declaration, don't add
2082 // a new type parameter.
2083 Ty_::Tgeneric(&(type_name, _))
2084 if tparams.iter().any(|tp| tp.name.1 == type_name) =>
2088 // Otherwise, if the parameter is `G $g`, create tparam
2089 // `T$g as G` and replace $g's type hint
2091 let id = (param_pos, self.concat("T/", name));
2094 std::slice::from_ref(
2095 self.alloc((ConstraintKind::ConstraintAs, self.alloc(ty.clone()))),
2099 self.alloc(Reason::hint(param_pos)),
2100 Ty_::Tgeneric(self.alloc((id.1, &[]))),
2104 let ty = self.alloc(Ty(context_reason, rewritten_ty.1));
2105 let right = self.alloc(Ty(
2107 Ty_::Taccess(self.alloc(TaccessType(ty, cst))),
2110 context_reason.pos().unwrap_or(Pos::none()),
2111 self.ctx_generic_for_dependent(name, &cst.1),
2113 tparams.push(tp(left_id, &[]));
2114 let left = self.alloc(Ty(
2116 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2118 where_constraints.push(self.alloc(WhereConstraint(
2120 ConstraintKind::ConstraintEq,
2126 let mut tparams = Vec::from_iter_in(tparams.iter().copied(), self.arena);
2127 let mut where_constraints =
2128 Vec::from_iter_in(where_constraints.iter().copied(), self.arena);
2130 // The divergence here from the lowerer comes from using oxidized_by_ref instead of oxidized
2131 let mut ty_by_param: BTreeMap<&str, (Ty<'a>, &'a Pos<'a>)> = params
2133 .filter_map(|param| Some((param.name?, (param.type_.type_.clone(), param.pos))))
2136 for context_ty in context_tys {
2137 match context_ty.1 {
2138 // Hfun_context in the AST.
2139 Ty_::Tapply(((_, name), _)) if name.starts_with('$') => {
2140 if let Some((param_ty, _)) = ty_by_param.get_mut(name) {
2142 Ty_::Tlike(ref mut ty) => match ty {
2143 Ty(r, Ty_::Toption(tinner)) => {
2144 *ty = self.alloc(Ty(
2146 Ty_::Toption(self.alloc(rewrite_fun_ctx(
2154 *ty = self.alloc(rewrite_fun_ctx(&mut tparams, ty, name));
2157 Ty_::Toption(ref mut ty) => {
2158 *ty = self.alloc(rewrite_fun_ctx(&mut tparams, ty, name));
2161 *param_ty = rewrite_fun_ctx(&mut tparams, param_ty, name);
2166 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), _))), cst)) => {
2167 if let Some((param_ty, param_pos)) = ty_by_param.get_mut(name) {
2168 let mut rewrite = |t| {
2171 &mut where_constraints,
2180 Ty_::Tlike(ref mut ty) => match ty {
2181 Ty(r, Ty_::Toption(tinner)) => {
2183 self.alloc(Ty(r, Ty_::Toption(self.alloc(rewrite(tinner)))))
2186 *ty = self.alloc(rewrite(ty));
2189 Ty_::Toption(ref mut ty) => {
2190 *ty = self.alloc(rewrite(ty));
2193 *param_ty = rewrite(param_ty);
2198 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2200 context_ty.0.pos().unwrap_or(Pos::none()),
2201 self.ctx_generic_for_generic_taccess(t, &cst.1),
2203 tparams.push(tp(left_id, &[]));
2204 let left = self.alloc(Ty(
2206 Ty_::Tgeneric(self.alloc((left_id.1, &[]))),
2208 where_constraints.push(self.alloc(WhereConstraint(
2210 ConstraintKind::ConstraintEq,
2218 let params = self.slice(params.iter().copied().map(|param| match param.name {
2220 Some(name) => match ty_by_param.get(name) {
2221 Some((type_, _)) if param.type_.type_ != type_ => self.alloc(FunParam {
2222 type_: self.alloc(PossiblyEnforcedTy {
2223 type_: self.alloc(type_.clone()),
2232 let context_tys = self.slice(context_tys.iter().copied().map(|ty| {
2233 let ty_ = match ty.1 {
2234 Ty_::Tapply(((_, name), &[])) if name.starts_with('$') => {
2235 Ty_::Tgeneric(self.alloc((self.ctx_generic_for_fun(name), &[])))
2237 Ty_::Taccess(&TaccessType(Ty(_, Ty_::Tapply(((_, name), &[]))), cst))
2238 if name.starts_with('$') =>
2240 let name = self.ctx_generic_for_dependent(name, &cst.1);
2241 Ty_::Tgeneric(self.alloc((name, &[])))
2243 Ty_::Taccess(&TaccessType(t, cst)) if Self::taccess_root_is_generic(t) => {
2244 let name = self.ctx_generic_for_generic_taccess(t, &cst.1);
2245 Ty_::Tgeneric(self.alloc((name, &[])))
2249 self.alloc(Ty(ty.0, ty_))
2251 let cap_ty = match context_tys {
2253 _ => self.alloc(Ty(cap_reason, Ty_::Tintersection(context_tys))),
2255 let implicit_params = self.alloc(FunImplicitParams {
2256 capability: CapTy(cap_ty),
2261 tparams.into_bump_slice(),
2263 where_constraints.into_bump_slice(),
2268 enum NodeIterHelper<'a, 'b> {
2270 Single(&'b Node<'a>),
2271 Vec(std::slice::Iter<'b, Node<'a>>),
2274 impl<'a, 'b> Iterator for NodeIterHelper<'a, 'b> {
2275 type Item = &'b Node<'a>;
2277 fn next(&mut self) -> Option<Self::Item> {
2279 NodeIterHelper::Empty => None,
2280 NodeIterHelper::Single(node) => {
2282 *self = NodeIterHelper::Empty;
2285 NodeIterHelper::Vec(ref mut iter) => iter.next(),
2289 // Must return the upper bound returned by Node::len.
2290 fn size_hint(&self) -> (usize, Option<usize>) {
2292 NodeIterHelper::Empty => (0, Some(0)),
2293 NodeIterHelper::Single(_) => (1, Some(1)),
2294 NodeIterHelper::Vec(iter) => iter.size_hint(),
2299 impl<'a, 'b> DoubleEndedIterator for NodeIterHelper<'a, 'b> {
2300 fn next_back(&mut self) -> Option<Self::Item> {
2302 NodeIterHelper::Empty => None,
2303 NodeIterHelper::Single(_) => self.next(),
2304 NodeIterHelper::Vec(ref mut iter) => iter.next_back(),
2309 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>> FlattenOp
2310 for DirectDeclSmartConstructors<'a, 'text, S>
2314 fn flatten(&self, kind: SyntaxKind, lst: std::vec::Vec<Self::S>) -> Self::S {
2318 Node::List(children) => children.len(),
2320 if Self::is_zero(x) {
2328 let mut r = Vec::with_capacity_in(size, self.arena);
2329 for s in lst.into_iter() {
2331 Node::List(children) => r.extend(children.iter().copied()),
2333 if !Self::is_zero(&x) {
2339 match r.into_bump_slice() {
2340 [] => Node::Ignored(kind),
2342 slice => Node::List(self.alloc(slice)),
2346 fn zero(kind: SyntaxKind) -> Self::S {
2350 fn is_zero(s: &Self::S) -> bool {
2352 Node::Token(token) => match token.kind() {
2353 TokenKind::Yield | TokenKind::Required | TokenKind::Lateinit => false,
2356 Node::List(inner) => inner.iter().all(Self::is_zero),
2362 impl<'a, 'text, S: SourceTextAllocator<'text, 'a>>
2363 FlattenSmartConstructors<'a, DirectDeclSmartConstructors<'a, 'text, S>>
2364 for DirectDeclSmartConstructors<'a, 'text, S>
2366 fn make_token(&mut self, token: CompactToken) -> Self::R {
2367 let token_text = |this: &Self| this.str_from_utf8(this.token_bytes(&token));
2368 let token_pos = |this: &Self| {
2371 .offset_to_file_pos_triple(token.start_offset());
2374 .offset_to_file_pos_triple(token.end_offset());
2375 Pos::from_lnum_bol_cnum(this.arena, this.filename, start, end)
2377 let kind = token.kind();
2379 let result = match kind {
2380 TokenKind::Name | TokenKind::XHPClassName => {
2381 let text = token_text(self);
2382 let pos = token_pos(self);
2384 let name = if kind == TokenKind::XHPClassName {
2385 Node::XhpName(self.alloc((text, pos)))
2387 Node::Name(self.alloc((text, pos)))
2390 if self.previous_token_kind == TokenKind::Class
2391 || self.previous_token_kind == TokenKind::Trait
2392 || self.previous_token_kind == TokenKind::Interface
2394 if let Some(current_class_name) = self.elaborate_defined_id(name) {
2395 self.classish_name_builder
2396 .lexed_name_after_classish_keyword(
2398 current_class_name.1,
2400 self.previous_token_kind,
2406 TokenKind::Class => Node::Name(self.alloc((token_text(self), token_pos(self)))),
2407 TokenKind::Variable => Node::Variable(self.alloc((token_text(self), token_pos(self)))),
2408 // There are a few types whose string representations we have to
2409 // grab anyway, so just go ahead and treat them as generic names.
2414 | TokenKind::Classname
2415 | TokenKind::SelfToken => Node::Name(self.alloc((token_text(self), token_pos(self)))),
2416 TokenKind::XHPElementName => {
2417 Node::XhpName(self.alloc((token_text(self), token_pos(self))))
2419 TokenKind::SingleQuotedStringLiteral => match escaper::unescape_single_in(
2420 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2423 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2424 Err(_) => Node::Ignored(SK::Token(kind)),
2426 TokenKind::DoubleQuotedStringLiteral => match escaper::unescape_double_in(
2427 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2430 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2431 Err(_) => Node::Ignored(SK::Token(kind)),
2433 TokenKind::HeredocStringLiteral => match escaper::unescape_heredoc_in(
2434 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2437 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2438 Err(_) => Node::Ignored(SK::Token(kind)),
2440 TokenKind::NowdocStringLiteral => match escaper::unescape_nowdoc_in(
2441 self.str_from_utf8(escaper::unquote_slice(self.token_bytes(&token))),
2444 Ok(text) => Node::StringLiteral(self.alloc((text.into(), token_pos(self)))),
2445 Err(_) => Node::Ignored(SK::Token(kind)),
2447 TokenKind::DecimalLiteral
2448 | TokenKind::OctalLiteral
2449 | TokenKind::HexadecimalLiteral
2450 | TokenKind::BinaryLiteral => {
2451 Node::IntLiteral(self.alloc((token_text(self), token_pos(self))))
2453 TokenKind::FloatingLiteral => {
2454 Node::FloatingLiteral(self.alloc((token_text(self), token_pos(self))))
2456 TokenKind::BooleanLiteral => {
2457 Node::BooleanLiteral(self.alloc((token_text(self), token_pos(self))))
2459 TokenKind::String => self.prim_ty(aast::Tprim::Tstring, token_pos(self)),
2460 TokenKind::Int => self.prim_ty(aast::Tprim::Tint, token_pos(self)),
2461 TokenKind::Float => self.prim_ty(aast::Tprim::Tfloat, token_pos(self)),
2462 // "double" and "boolean" are parse errors--they should be written
2463 // "float" and "bool". The decl-parser treats the incorrect names as
2464 // type names rather than primitives.
2465 TokenKind::Double | TokenKind::Boolean => self.hint_ty(
2467 Ty_::Tapply(self.alloc(((token_pos(self), token_text(self)), &[][..]))),
2469 TokenKind::Num => self.prim_ty(aast::Tprim::Tnum, token_pos(self)),
2470 TokenKind::Bool => self.prim_ty(aast::Tprim::Tbool, token_pos(self)),
2471 TokenKind::Mixed => {
2472 if self.opts.everything_sdt {
2473 Node::Ty(self.alloc(Ty(
2474 self.alloc(Reason::hint(token_pos(self))),
2475 Ty_::Toption(self.alloc(Ty(
2476 self.alloc(Reason::hint(token_pos(self))),
2477 Ty_::Tsupportdynamic,
2481 Node::Ty(self.alloc(Ty(self.alloc(Reason::hint(token_pos(self))), Ty_::Tmixed)))
2484 TokenKind::Void => self.prim_ty(aast::Tprim::Tvoid, token_pos(self)),
2485 TokenKind::Arraykey => self.prim_ty(aast::Tprim::Tarraykey, token_pos(self)),
2486 TokenKind::Noreturn => self.prim_ty(aast::Tprim::Tnoreturn, token_pos(self)),
2487 TokenKind::Resource => self.prim_ty(aast::Tprim::Tresource, token_pos(self)),
2488 TokenKind::NullLiteral
2491 | TokenKind::Backslash
2492 | TokenKind::Construct
2493 | TokenKind::LeftParen
2494 | TokenKind::RightParen
2495 | TokenKind::LeftBracket
2496 | TokenKind::RightBracket
2498 | TokenKind::Question
2501 | TokenKind::Exclamation
2504 | TokenKind::PlusPlus
2505 | TokenKind::MinusMinus
2509 | TokenKind::EqualEqual
2510 | TokenKind::EqualEqualEqual
2511 | TokenKind::StarStar
2512 | TokenKind::AmpersandAmpersand
2514 | TokenKind::LessThan
2515 | TokenKind::LessThanEqual
2516 | TokenKind::GreaterThan
2517 | TokenKind::GreaterThanEqual
2519 | TokenKind::Ampersand
2521 | TokenKind::LessThanLessThan
2522 | TokenKind::GreaterThanGreaterThan
2523 | TokenKind::Percent
2524 | TokenKind::QuestionQuestion
2526 | TokenKind::Abstract
2530 | TokenKind::DotDotDot
2531 | TokenKind::Extends
2533 | TokenKind::Implements
2535 | TokenKind::Interface
2537 | TokenKind::Newtype
2540 | TokenKind::Semicolon
2541 | TokenKind::Private
2542 | TokenKind::Protected
2547 | TokenKind::Lateinit
2548 | TokenKind::RecordDec
2549 | TokenKind::RightBrace
2552 | TokenKind::Function
2553 | TokenKind::Namespace
2555 | TokenKind::Required
2557 | TokenKind::Readonly => Node::Token(FixedWidthToken::new(kind, token.start_offset())),
2558 TokenKind::EndOfFile
2559 | TokenKind::Attribute
2565 | TokenKind::Category
2566 | TokenKind::Children
2568 | TokenKind::Continue
2569 | TokenKind::Default
2577 | TokenKind::Endforeach
2579 | TokenKind::Endswitch
2580 | TokenKind::Endwhile
2582 | TokenKind::Fallthrough
2584 | TokenKind::Finally
2586 | TokenKind::Foreach
2589 | TokenKind::Concurrent
2591 | TokenKind::Include
2592 | TokenKind::Include_once
2593 | TokenKind::Instanceof
2594 | TokenKind::Insteadof
2595 | TokenKind::Integer
2605 | TokenKind::Require
2606 | TokenKind::Require_once
2618 | TokenKind::LeftBrace
2619 | TokenKind::MinusGreaterThan
2621 | TokenKind::LessThanEqualGreaterThan
2622 | TokenKind::ExclamationEqual
2623 | TokenKind::ExclamationEqualEqual
2625 | TokenKind::QuestionAs
2626 | TokenKind::QuestionColon
2627 | TokenKind::QuestionQuestionEqual
2629 | TokenKind::StarStarEqual
2630 | TokenKind::StarEqual
2631 | TokenKind::SlashEqual
2632 | TokenKind::PercentEqual
2633 | TokenKind::PlusEqual
2634 | TokenKind::MinusEqual
2635 | TokenKind::DotEqual
2636 | TokenKind::LessThanLessThanEqual
2637 | TokenKind::GreaterThanGreaterThanEqual
2638 | TokenKind::AmpersandEqual
2639 | TokenKind::CaratEqual
2640 | TokenKind::BarEqual
2642 | TokenKind::ColonColon
2643 | TokenKind::EqualGreaterThan
2644 | TokenKind::EqualEqualGreaterThan
2645 | TokenKind::QuestionMinusGreaterThan
2646 | TokenKind::DollarDollar
2647 | TokenKind::BarGreaterThan
2648 | TokenKind::SlashGreaterThan
2649 | TokenKind::LessThanSlash
2650 | TokenKind::LessThanQuestion
2651 | TokenKind::Backtick
2652 | TokenKind::ErrorToken
2653 | TokenKind::DoubleQuotedStringLiteralHead
2654 | TokenKind::StringLiteralBody
2655 | TokenKind::DoubleQuotedStringLiteralTail
2656 | TokenKind::HeredocStringLiteralHead
2657 | TokenKind::HeredocStringLiteralTail
2658 | TokenKind::XHPCategoryName
2659 | TokenKind::XHPStringLiteral
2660 | TokenKind::XHPBody
2661 | TokenKind::XHPComment
2663 | TokenKind::Hashbang => Node::Ignored(SK::Token(kind)),
2665 self.previous_token_kind = kind;
2669 fn make_missing(&mut self, _: usize) -> Self::R {
2670 Node::Ignored(SK::Missing)
2673 fn make_list(&mut self, items: std::vec::Vec<Self::R>, _: usize) -> Self::R {
2674 if let Some(&yield_) = items
2676 .flat_map(|node| node.iter())
2677 .find(|node| node.is_token(TokenKind::Yield))
2681 let size = items.iter().filter(|node| node.is_present()).count();
2682 let items_iter = items.into_iter();
2683 let mut items = Vec::with_capacity_in(size, self.arena);
2684 for node in items_iter {
2685 if node.is_present() {
2689 let items = items.into_bump_slice();
2690 if items.is_empty() {
2691 Node::Ignored(SK::SyntaxList)
2693 Node::List(self.alloc(items))
2698 fn make_qualified_name(&mut self, parts: Self::R) -> Self::R {
2699 let pos = self.get_pos(parts);
2701 Node::List(nodes) => Node::QualifiedName(self.alloc((nodes, pos))),
2702 node if node.is_ignored() => Node::Ignored(SK::QualifiedName),
2703 node => Node::QualifiedName(
2704 self.alloc((bumpalo::vec![in self.arena; node].into_bump_slice(), pos)),
2709 fn make_simple_type_specifier(&mut self, specifier: Self::R) -> Self::R {
2710 // Return this explicitly because flatten filters out zero nodes, and
2711 // we treat most non-error nodes as zeroes.
2715 fn make_literal_expression(&mut self, expression: Self::R) -> Self::R {
2719 fn make_simple_initializer(&mut self, equals: Self::R, expr: Self::R) -> Self::R {
2720 // If the expr is Ignored, bubble up the assignment operator so that we
2721 // can tell that *some* initializer was here. Useful for class
2722 // properties, where we need to enforce that properties without default
2723 // values are initialized in the constructor.
2724 if expr.is_ignored() { equals } else { expr }
2727 fn make_anonymous_function(
2729 _attribute_spec: Self::R,
2730 _async_keyword: Self::R,
2731 _function_keyword: Self::R,
2732 _left_paren: Self::R,
2733 _parameters: Self::R,
2734 _right_paren: Self::R,
2737 _readonly_return: Self::R,
2742 // do not allow Yield to bubble up
2743 Node::Ignored(SK::AnonymousFunction)
2746 fn make_lambda_expression(
2748 _attribute_spec: Self::R,
2750 _signature: Self::R,
2754 // do not allow Yield to bubble up
2755 Node::Ignored(SK::LambdaExpression)
2758 fn make_awaitable_creation_expression(
2760 _attribute_spec: Self::R,
2762 _compound_statement: Self::R,
2764 // do not allow Yield to bubble up
2765 Node::Ignored(SK::AwaitableCreationExpression)
2768 fn make_element_initializer(
2774 Node::ListItem(self.alloc((key, value)))
2777 fn make_prefix_unary_expression(&mut self, op: Self::R, value: Self::R) -> Self::R {
2778 let pos = self.merge_positions(op, value);
2779 let op = match op.token_kind() {
2780 Some(TokenKind::Tilde) => Uop::Utild,
2781 Some(TokenKind::Exclamation) => Uop::Unot,
2782 Some(TokenKind::Plus) => Uop::Uplus,
2783 Some(TokenKind::Minus) => Uop::Uminus,
2784 Some(TokenKind::PlusPlus) => Uop::Uincr,
2785 Some(TokenKind::MinusMinus) => Uop::Udecr,
2786 Some(TokenKind::At) => Uop::Usilence,
2787 _ => return Node::Ignored(SK::PrefixUnaryExpression),
2789 let value = match self.node_to_expr(value) {
2790 Some(value) => value,
2791 None => return Node::Ignored(SK::PrefixUnaryExpression),
2793 Node::Expr(self.alloc(aast::Expr(
2796 aast::Expr_::Unop(self.alloc((op, value))),
2800 fn make_postfix_unary_expression(&mut self, value: Self::R, op: Self::R) -> Self::R {
2801 let pos = self.merge_positions(value, op);
2802 let op = match op.token_kind() {
2803 Some(TokenKind::PlusPlus) => Uop::Upincr,
2804 Some(TokenKind::MinusMinus) => Uop::Updecr,
2805 _ => return Node::Ignored(SK::PostfixUnaryExpression),
2807 let value = match self.node_to_expr(value) {
2808 Some(value) => value,
2809 None => return Node::Ignored(SK::PostfixUnaryExpression),
2811 Node::Expr(self.alloc(aast::Expr(
2814 aast::Expr_::Unop(self.alloc((op, value))),
2818 fn make_binary_expression(&mut self, lhs: Self::R, op_node: Self::R, rhs: Self::R) -> Self::R {
2819 let op = match op_node.token_kind() {
2820 Some(TokenKind::Plus) => Bop::Plus,
2821 Some(TokenKind::Minus) => Bop::Minus,
2822 Some(TokenKind::Star) => Bop::Star,
2823 Some(TokenKind::Slash) => Bop::Slash,
2824 Some(TokenKind::Equal) => Bop::Eq(None),
2825 Some(TokenKind::EqualEqual) => Bop::Eqeq,
2826 Some(TokenKind::EqualEqualEqual) => Bop::Eqeqeq,
2827 Some(TokenKind::StarStar) => Bop::Starstar,
2828 Some(TokenKind::AmpersandAmpersand) => Bop::Ampamp,
2829 Some(TokenKind::BarBar) => Bop::Barbar,
2830 Some(TokenKind::LessThan) => Bop::Lt,
2831 Some(TokenKind::LessThanEqual) => Bop::Lte,
2832 Some(TokenKind::LessThanLessThan) => Bop::Ltlt,
2833 Some(TokenKind::GreaterThan) => Bop::Gt,
2834 Some(TokenKind::GreaterThanEqual) => Bop::Gte,
2835 Some(TokenKind::GreaterThanGreaterThan) => Bop::Gtgt,
2836 Some(TokenKind::Dot) => Bop::Dot,
2837 Some(TokenKind::Ampersand) => Bop::Amp,
2838 Some(TokenKind::Bar) => Bop::Bar,
2839 Some(TokenKind::Percent) => Bop::Percent,
2840 Some(TokenKind::QuestionQuestion) => Bop::QuestionQuestion,
2841 _ => return Node::Ignored(SK::BinaryExpression),
2844 match (&op, rhs.is_token(TokenKind::Yield)) {
2845 (Bop::Eq(_), true) => return rhs,
2849 let pos = self.merge(self.merge_positions(lhs, op_node), self.get_pos(rhs));
2851 let lhs = match self.node_to_expr(lhs) {
2853 None => return Node::Ignored(SK::BinaryExpression),
2855 let rhs = match self.node_to_expr(rhs) {
2857 None => return Node::Ignored(SK::BinaryExpression),
2860 Node::Expr(self.alloc(aast::Expr(
2863 aast::Expr_::Binop(self.alloc((op, lhs, rhs))),
2867 fn make_parenthesized_expression(
2876 fn make_list_item(&mut self, item: Self::R, sep: Self::R) -> Self::R {
2877 match (item.is_ignored(), sep.is_ignored()) {
2878 (true, true) => Node::Ignored(SK::ListItem),
2879 (false, true) => item,
2880 (true, false) => sep,
2881 (false, false) => Node::ListItem(self.alloc((item, sep))),
2885 fn make_type_arguments(
2889 greater_than: Self::R,
2891 Node::BracketedList(self.alloc((
2892 self.get_pos(less_than),
2893 arguments.as_slice(self.arena),
2894 self.get_pos(greater_than),
2898 fn make_generic_type_specifier(
2900 class_type: Self::R,
2901 type_arguments: Self::R,
2903 let class_id = match self.expect_name(class_type) {
2905 None => return Node::Ignored(SK::GenericTypeSpecifier),
2907 match class_id.1.trim_start_matches("\\") {
2908 "varray_or_darray" | "vec_or_dict" => {
2909 let id_pos = class_id.0;
2910 let pos = self.merge(id_pos, self.get_pos(type_arguments));
2911 let type_arguments = type_arguments.as_slice(self.arena);
2912 let ty_ = match type_arguments {
2913 [tk, tv] => Ty_::TvecOrDict(
2915 self.node_to_ty(*tk)
2916 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
2917 self.node_to_ty(*tv)
2918 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
2921 [tv] => Ty_::TvecOrDict(
2923 self.vec_or_dict_key(pos),
2924 self.node_to_ty(*tv)
2925 .unwrap_or_else(|| self.tany_with_pos(id_pos)),
2930 self.hint_ty(pos, ty_)
2933 let Id(pos, class_type) = class_id;
2934 match class_type.rsplit('\\').next() {
2935 Some(name) if self.is_type_param_in_scope(name) => {
2936 let pos = self.merge(pos, self.get_pos(type_arguments));
2937 let type_arguments = self.slice(
2940 .filter_map(|&node| self.node_to_ty(node)),
2942 let ty_ = Ty_::Tgeneric(self.alloc((name, type_arguments)));
2943 self.hint_ty(pos, ty_)
2946 let class_type = self.elaborate_raw_id(class_type);
2950 self.get_pos(type_arguments),
2958 fn make_record_declaration(
2960 attribute_spec: Self::R,
2962 record_keyword: Self::R,
2964 _extends_keyword: Self::R,
2965 extends_opt: Self::R,
2966 _left_brace: Self::R,
2968 right_brace: Self::R,
2970 let name = match self.elaborate_defined_id(name) {
2972 None => return Node::Ignored(SK::RecordDeclaration),
2974 let parsed_attributes = self.to_attributes(attribute_spec);
2977 self.alloc(typing_defs::RecordDefType {
2978 module: self.alloc(parsed_attributes.module),
2981 .expect_name(extends_opt)
2982 .map(|id| self.elaborate_id(id).into()),
2983 fields: self.slice(fields.iter().filter_map(|node| match node {
2984 Node::RecordField(&(id, req)) => Some((id.into(), req)),
2987 abstract_: modifier.is_token(TokenKind::Abstract),
2988 pos: self.pos_from_slice(&[attribute_spec, modifier, record_keyword, right_brace]),
2991 Node::Ignored(SK::RecordDeclaration)
2994 fn make_record_field(
2998 initializer: Self::R,
2999 _semicolon: Self::R,
3001 let name = match self.expect_name(name) {
3003 None => return Node::Ignored(SK::RecordField),
3005 let field_req = if initializer.is_ignored() {
3006 RecordFieldReq::ValueRequired
3008 RecordFieldReq::HasDefaultValue
3010 Node::RecordField(self.alloc((name, field_req)))
3013 fn make_alias_declaration(
3015 attributes: Self::R,
3018 generic_params: Self::R,
3019 constraint: Self::R,
3021 aliased_type: Self::R,
3022 _semicolon: Self::R,
3024 if name.is_ignored() {
3025 return Node::Ignored(SK::AliasDeclaration);
3027 let Id(pos, name) = match self.elaborate_defined_id(name) {
3029 None => return Node::Ignored(SK::AliasDeclaration),
3031 let ty = match self.node_to_ty(aliased_type) {
3033 None => return Node::Ignored(SK::AliasDeclaration),
3035 let constraint = match constraint {
3036 Node::TypeConstraint(&(_kind, hint)) => self.node_to_ty(hint),
3039 // Pop the type params stack only after creating all inner types.
3040 let tparams = self.pop_type_params(generic_params);
3041 let parsed_attributes = self.to_attributes(attributes);
3042 let typedef = self.alloc(TypedefType {
3043 module: self.alloc(parsed_attributes.module),
3045 vis: if parsed_attributes.internal {
3046 aast::TypedefVisibility::Tinternal
3048 match keyword.token_kind() {
3049 Some(TokenKind::Type) => aast::TypedefVisibility::Transparent,
3050 Some(TokenKind::Newtype) => aast::TypedefVisibility::Opaque,
3051 _ => aast::TypedefVisibility::Transparent,
3060 self.add_typedef(name, typedef);
3062 Node::Ignored(SK::AliasDeclaration)
3065 fn make_context_alias_declaration(
3067 attributes: Self::R,
3070 generic_params: Self::R,
3071 constraint: Self::R,
3074 _semicolon: Self::R,
3076 if name.is_ignored() {
3077 return Node::Ignored(SK::ContextAliasDeclaration);
3079 let Id(pos, name) = match self.elaborate_defined_id(name) {
3081 None => return Node::Ignored(SK::ContextAliasDeclaration),
3083 let ty = match self.node_to_ty(ctx_list) {
3085 None => self.alloc(Ty(
3086 self.alloc(Reason::hint(pos)),
3087 Ty_::Tapply(self.alloc(((pos, "\\HH\\Contexts\\defaults"), &[]))),
3091 // lowerer ensures there is only one as constraint
3092 let mut as_constraint = None;
3093 for c in constraint.iter() {
3094 if let Node::ContextConstraint(&(kind, hint)) = c {
3095 let ty = self.node_to_ty(hint);
3097 ConstraintKind::ConstraintAs => as_constraint = ty,
3102 // Pop the type params stack only after creating all inner types.
3103 let tparams = self.pop_type_params(generic_params);
3104 let parsed_attributes = self.to_attributes(attributes);
3105 let typedef = self.alloc(TypedefType {
3106 module: self.alloc(parsed_attributes.module),
3108 vis: if parsed_attributes.internal {
3109 aast::TypedefVisibility::Tinternal
3111 aast::TypedefVisibility::Opaque
3114 constraint: as_constraint,
3119 self.add_typedef(name, typedef);
3121 Node::Ignored(SK::ContextAliasDeclaration)
3124 fn make_type_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3125 let kind = match kind.token_kind() {
3126 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3127 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3128 _ => return Node::Ignored(SK::TypeConstraint),
3130 Node::TypeConstraint(self.alloc((kind, value)))
3133 fn make_context_constraint(&mut self, kind: Self::R, value: Self::R) -> Self::R {
3134 let kind = match kind.token_kind() {
3135 Some(TokenKind::As) => ConstraintKind::ConstraintAs,
3136 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3137 _ => return Node::Ignored(SK::ContextConstraint),
3139 Node::ContextConstraint(self.alloc((kind, value)))
3142 fn make_type_parameter(
3144 user_attributes: Self::R,
3148 tparam_params: Self::R,
3149 constraints: Self::R,
3151 let user_attributes = match user_attributes {
3152 Node::BracketedList((_, attributes, _)) => {
3153 self.slice(attributes.into_iter().filter_map(|x| match x {
3154 Node::Attribute(a) => Some(*a),
3161 let constraints = self.slice(constraints.iter().filter_map(|node| match node {
3162 Node::TypeConstraint(&constraint) => Some(constraint),
3166 // TODO(T70068435) Once we add support for constraints on higher-kinded types
3167 // (in particular, constraints on nested type parameters), we need to ensure
3168 // that we correctly handle the scoping of nested type parameters.
3169 // This includes making sure that the call to convert_type_appl_to_generic
3170 // in make_type_parameters handles nested constraints.
3171 // For now, we just make sure that the nested type parameters that make_type_parameters
3172 // added to the global list of in-scope type parameters are removed immediately:
3173 self.pop_type_params(tparam_params);
3175 let tparam_params = match tparam_params {
3176 Node::TypeParameters(¶ms) => params,
3180 Node::TypeParameter(self.alloc(TypeParameterDecl {
3182 variance: match variance.token_kind() {
3183 Some(TokenKind::Minus) => Variance::Contravariant,
3184 Some(TokenKind::Plus) => Variance::Covariant,
3185 _ => Variance::Invariant,
3187 reified: if reify.is_token(TokenKind::Reify) {
3188 if user_attributes.iter().any(|node| node.name.1 == "__Soft") {
3189 aast::ReifyKind::SoftReified
3191 aast::ReifyKind::Reified
3194 aast::ReifyKind::Erased
3202 fn make_type_parameters(&mut self, _lt: Self::R, tparams: Self::R, _gt: Self::R) -> Self::R {
3203 let size = tparams.len();
3204 let mut tparams_with_name = Vec::with_capacity_in(size, self.arena);
3205 let mut tparam_names = MultiSetMut::with_capacity_in(size, self.arena);
3206 for node in tparams.iter() {
3208 &Node::TypeParameter(decl) => {
3209 let name = match decl.name.as_id() {
3211 None => return Node::Ignored(SK::TypeParameters),
3213 tparam_names.insert(name.1);
3214 tparams_with_name.push((decl, name));
3219 Rc::make_mut(&mut self.type_parameters).push(tparam_names.into());
3220 let mut tparams = Vec::with_capacity_in(tparams_with_name.len(), self.arena);
3221 for (decl, name) in tparams_with_name.into_iter() {
3222 let &TypeParameterDecl {
3230 let constraints = self.slice(constraints.iter().filter_map(|constraint| {
3231 let &(kind, ty) = constraint;
3232 let ty = self.node_to_ty(ty)?;
3233 let ty = self.convert_tapply_to_tgeneric(ty);
3237 let user_attributes = self.slice(
3241 .map(|x| self.user_attribute_to_decl(x)),
3243 tparams.push(self.alloc(Tparam {
3249 tparams: tparam_params,
3252 Node::TypeParameters(self.alloc(tparams.into_bump_slice()))
3255 fn make_parameter_declaration(
3257 attributes: Self::R,
3258 visibility: Self::R,
3263 initializer: Self::R,
3265 let (variadic, pos, name) = match name {
3266 Node::ListItem(&(ellipsis, id)) => {
3267 let Id(pos, name) = match id.as_variable() {
3269 None => return Node::Ignored(SK::ParameterDeclaration),
3271 let variadic = ellipsis.is_token(TokenKind::DotDotDot);
3272 (variadic, pos, Some(name))
3275 let Id(pos, name) = match name.as_variable() {
3277 None => return Node::Ignored(SK::ParameterDeclaration),
3279 (false, pos, Some(name))
3282 let kind = if inout.is_token(TokenKind::Inout) {
3287 let is_readonly = readonly.is_token(TokenKind::Readonly);
3288 let hint = if self.opts.interpret_soft_types_as_like_types {
3289 let attributes = self.to_attributes(attributes);
3290 if attributes.soft {
3292 Node::Ty(ty) => self.hint_ty(self.get_pos(hint), Ty_::Tlike(ty)),
3301 Node::FunParam(self.alloc(FunParamDecl {
3305 readonly: is_readonly,
3314 fn make_variadic_parameter(&mut self, _: Self::R, hint: Self::R, ellipsis: Self::R) -> Self::R {
3316 self.alloc(FunParamDecl {
3317 attributes: Node::Ignored(SK::Missing),
3318 visibility: Node::Ignored(SK::Missing),
3319 kind: ParamMode::FPnormal,
3324 .unwrap_or_else(|| self.get_pos(ellipsis)),
3327 initializer: Node::Ignored(SK::Missing),
3332 fn make_function_declaration(
3334 attributes: Self::R,
3338 let parsed_attributes = self.to_attributes(attributes);
3340 Node::FunctionHeader(header) => {
3341 let is_method = false;
3342 let ((pos, name), type_, _) =
3343 match self.function_to_ty(is_method, attributes, header, body) {
3345 None => return Node::Ignored(SK::FunctionDeclaration),
3347 let deprecated = parsed_attributes.deprecated.map(|msg| {
3348 let mut s = String::new_in(self.arena);
3349 s.push_str("The function ");
3350 s.push_str(name.trim_start_matches("\\"));
3351 s.push_str(" is deprecated: ");
3355 let fun_elt = self.alloc(FunElt {
3356 module: self.alloc(parsed_attributes.module),
3357 internal: parsed_attributes.internal,
3361 php_std_lib: parsed_attributes.php_std_lib,
3362 support_dynamic_type: self.opts.everything_sdt
3363 || parsed_attributes.support_dynamic_type,
3365 self.add_fun(name, fun_elt);
3366 Node::Ignored(SK::FunctionDeclaration)
3368 _ => Node::Ignored(SK::FunctionDeclaration),
3374 left_bracket: Self::R,
3376 right_bracket: Self::R,
3378 let tys = self.slice(tys.iter().filter_map(|ty| match ty {
3379 Node::ListItem(&(ty, _)) | &ty => {
3380 // A wildcard is used for the context of a closure type on a
3381 // parameter of a function with a function context (e.g.,
3382 // `function f((function ()[_]: void) $f)[ctx $f]: void {}`).
3383 if let Some(Id(pos, "_")) = self.expect_name(ty) {
3384 return Some(self.alloc(Ty(
3385 self.alloc(Reason::hint(pos)),
3386 Ty_::Tapply(self.alloc(((pos, "_"), &[]))),
3389 let ty = self.node_to_ty(ty)?;
3391 // Only three forms of type can appear here in a valid program:
3392 // - function contexts (`ctx $f`)
3393 // - value-dependent paths (`$v::C`)
3394 // - built-in contexts (`rx`, `cipp_of<EntFoo>`)
3395 // The first and last will be represented with `Tapply`,
3396 // but function contexts will use a variable name
3397 // (containing a `$`). Built-in contexts are always in the
3398 // \HH\Contexts namespace, so we rewrite those names here.
3399 Ty_::Tapply(&((pos, name), targs)) if !name.starts_with('$') => {
3400 // The name will have been elaborated in the current
3401 // namespace, but we actually want it to be in the
3402 // \HH\Contexts namespace. Grab the last component of
3403 // the name, and rewrite it in the correct namespace.
3404 // Note that this makes it impossible to express names
3405 // in any sub-namespace of \HH\Contexts (e.g.,
3406 // "Unsafe\\cipp" will be rewritten as
3407 // "\\HH\\Contexts\\cipp" rather than
3408 // "\\HH\\Contexts\\Unsafe\\cipp").
3409 let name = match name.trim_end_matches('\\').split('\\').next_back() {
3411 if let Some(first_char) = ctxname.chars().nth(0) {
3412 if first_char.is_lowercase() {
3413 self.concat("\\HH\\Contexts\\", ctxname)
3423 Some(self.alloc(Ty(ty.0, Ty_::Tapply(self.alloc(((pos, name), targs))))))
3429 /* Like in as_fun_implicit_params, we keep the intersection as is: we do not simplify
3430 * empty or singleton intersections.
3432 let pos = self.merge_positions(left_bracket, right_bracket);
3433 self.hint_ty(pos, Ty_::Tintersection(tys))
3436 fn make_function_ctx_type_specifier(
3438 ctx_keyword: Self::R,
3441 match variable.as_variable() {
3442 Some(Id(pos, name)) => {
3443 Node::Variable(self.alloc((name, self.merge(pos, self.get_pos(ctx_keyword)))))
3445 None => Node::Ignored(SK::FunctionCtxTypeSpecifier),
3449 fn make_function_declaration_header(
3454 type_params: Self::R,
3455 left_paren: Self::R,
3456 param_list: Self::R,
3457 _right_paren: Self::R,
3458 capability: Self::R,
3460 readonly_return: Self::R,
3462 where_constraints: Self::R,
3464 // Use the position of the left paren if the name is missing.
3465 let name = if name.is_ignored() { left_paren } else { name };
3466 Node::FunctionHeader(self.alloc(FunctionHeader {
3478 fn make_yield_expression(&mut self, keyword: Self::R, _operand: Self::R) -> Self::R {
3479 assert!(keyword.token_kind() == Some(TokenKind::Yield));
3483 fn make_const_declaration(
3486 const_keyword: Self::R,
3495 .classish_name_builder
3496 .get_current_classish_name()
3499 let ty = self.node_to_ty(hint);
3501 self.alloc(self.slice(consts.iter().filter_map(|cst| match cst {
3502 Node::ConstInitializer(&(name, initializer, refs)) => {
3503 let id = name.as_id()?;
3504 let modifiers = read_member_modifiers(modifiers.iter());
3505 let abstract_ = if modifiers.is_abstract {
3506 ClassConstKind::CCAbstract(!initializer.is_ignored())
3508 ClassConstKind::CCConcrete
3511 .or_else(|| self.infer_const(name, initializer))
3512 .unwrap_or_else(|| tany());
3513 Some(Node::Const(self.alloc(
3514 shallow_decl_defs::ShallowClassConst {
3527 Node::List(consts) => {
3528 // This case always returns Node::Ignored,
3529 // but has the side effect of calling self.add_const
3531 // Note: given "const int X=1,Y=2;", the legacy decl-parser
3532 // allows both decls, and it gives them both an identical text-span -
3533 // from start of "const" to end of semicolon. This is a bug but
3534 // the code here preserves it.
3535 let pos = self.merge_positions(const_keyword, semicolon);
3536 for cst in consts.iter() {
3538 Node::ConstInitializer(&(name, initializer, _refs)) => {
3539 if let Some(Id(id_pos, id)) = self.elaborate_defined_id(name) {
3542 .or_else(|| self.infer_const(name, initializer))
3543 .unwrap_or_else(|| self.tany_with_pos(id_pos));
3544 self.add_const(id, self.alloc(ConstDecl { pos, type_: ty }));
3550 Node::Ignored(SK::ConstDeclaration)
3552 _ => Node::Ignored(SK::ConstDeclaration),
3556 fn begin_constant_declarator(&mut self) {
3557 self.start_accumulating_const_refs();
3560 fn make_constant_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
3561 // The "X=1" part of either a member const "class C {const int X=1;}" or a top-level const "const int X=1;"
3562 // Note: the the declarator itself doesn't yet know whether a type was provided by the user;
3563 // that's only known in the parent, make_const_declaration
3564 let refs = self.stop_accumulating_const_refs();
3565 if name.is_ignored() {
3566 Node::Ignored(SK::ConstantDeclarator)
3568 Node::ConstInitializer(self.alloc((name, initializer, refs)))
3572 fn make_namespace_declaration(&mut self, _name: Self::R, body: Self::R) -> Self::R {
3573 if let Node::Ignored(SK::NamespaceBody) = body {
3574 Rc::make_mut(&mut self.namespace_builder).pop_namespace();
3576 Node::Ignored(SK::NamespaceDeclaration)
3579 fn make_namespace_declaration_header(&mut self, _keyword: Self::R, name: Self::R) -> Self::R {
3580 let name = self.expect_name(name).map(|Id(_, name)| name);
3581 // if this is header of semicolon-style (one with NamespaceEmptyBody) namespace, we should pop
3582 // the previous namespace first, but we don't have the body yet. We'll fix it retroactively in
3583 // make_namespace_empty_body
3584 Rc::make_mut(&mut self.namespace_builder).push_namespace(name);
3585 Node::Ignored(SK::NamespaceDeclarationHeader)
3588 fn make_namespace_body(
3590 _left_brace: Self::R,
3591 _declarations: Self::R,
3592 _right_brace: Self::R,
3594 Node::Ignored(SK::NamespaceBody)
3597 fn make_namespace_empty_body(&mut self, _semicolon: Self::R) -> Self::R {
3598 Rc::make_mut(&mut self.namespace_builder).pop_previous_namespace();
3599 Node::Ignored(SK::NamespaceEmptyBody)
3602 fn make_namespace_use_declaration(
3605 namespace_use_kind: Self::R,
3607 _semicolon: Self::R,
3609 if let Some(import_kind) = Self::namespace_use_kind(&namespace_use_kind) {
3610 for clause in clauses.iter() {
3611 if let Node::NamespaceUseClause(nuc) = clause {
3612 Rc::make_mut(&mut self.namespace_builder).add_import(
3620 Node::Ignored(SK::NamespaceUseDeclaration)
3623 fn make_namespace_group_use_declaration(
3628 _left_brace: Self::R,
3630 _right_brace: Self::R,
3631 _semicolon: Self::R,
3633 let Id(_, prefix) = match self.expect_name(prefix) {
3635 None => return Node::Ignored(SK::NamespaceGroupUseDeclaration),
3637 for clause in clauses.iter() {
3638 if let Node::NamespaceUseClause(nuc) = clause {
3639 let mut id = String::new_in(self.arena);
3640 id.push_str(prefix);
3641 id.push_str(nuc.id.1);
3642 Rc::make_mut(&mut self.namespace_builder).add_import(
3649 Node::Ignored(SK::NamespaceGroupUseDeclaration)
3652 fn make_namespace_use_clause(
3654 clause_kind: Self::R,
3657 aliased_name: Self::R,
3659 let id = match self.expect_name(name) {
3661 None => return Node::Ignored(SK::NamespaceUseClause),
3663 let as_ = if as_.is_token(TokenKind::As) {
3664 match aliased_name.as_id() {
3665 Some(name) => Some(name.1),
3666 None => return Node::Ignored(SK::NamespaceUseClause),
3671 if let Some(kind) = Self::namespace_use_kind(&clause_kind) {
3672 Node::NamespaceUseClause(self.alloc(NamespaceUseClause { kind, id, as_ }))
3674 Node::Ignored(SK::NamespaceUseClause)
3678 fn make_where_clause(&mut self, _: Self::R, where_constraints: Self::R) -> Self::R {
3682 fn make_where_constraint(
3686 right_type: Self::R,
3688 Node::WhereConstraint(self.alloc(WhereConstraint(
3689 self.node_to_ty(left_type).unwrap_or_else(|| tany()),
3690 match operator.token_kind() {
3691 Some(TokenKind::Equal) => ConstraintKind::ConstraintEq,
3692 Some(TokenKind::Super) => ConstraintKind::ConstraintSuper,
3693 _ => ConstraintKind::ConstraintAs,
3695 self.node_to_ty(right_type).unwrap_or_else(|| tany()),
3699 fn make_classish_declaration(
3701 attributes: Self::R,
3703 xhp_keyword: Self::R,
3704 class_keyword: Self::R,
3707 _extends_keyword: Self::R,
3709 _implements_keyword: Self::R,
3710 implements: Self::R,
3711 where_clause: Self::R,
3714 let raw_name = match self.expect_name(name) {
3715 Some(Id(_, name)) => name,
3716 None => return Node::Ignored(SK::ClassishDeclaration),
3718 let Id(pos, name) = match self.elaborate_defined_id(name) {
3720 None => return Node::Ignored(SK::ClassishDeclaration),
3722 let is_xhp = raw_name.starts_with(':') || xhp_keyword.is_present();
3724 let mut class_kind = match class_keyword.token_kind() {
3725 Some(TokenKind::Interface) => ClassishKind::Cinterface,
3726 Some(TokenKind::Trait) => ClassishKind::Ctrait,
3727 _ => ClassishKind::Cclass(&Abstraction::Concrete),
3729 let mut final_ = false;
3731 for modifier in modifiers.iter() {
3732 match modifier.token_kind() {
3733 Some(TokenKind::Abstract) => {
3734 class_kind = ClassishKind::Cclass(&Abstraction::Abstract)
3736 Some(TokenKind::Final) => final_ = true,
3741 let where_constraints = self.slice(where_clause.iter().filter_map(|&x| match x {
3742 Node::WhereConstraint(x) => Some(x),
3746 let body = match body {
3747 Node::ClassishBody(body) => body,
3748 _ => return Node::Ignored(SK::ClassishDeclaration),
3751 let mut uses_len = 0;
3752 let mut xhp_attr_uses_len = 0;
3753 let mut xhp_enum_values = SMap::empty();
3754 let mut req_extends_len = 0;
3755 let mut req_implements_len = 0;
3756 let mut consts_len = 0;
3757 let mut typeconsts_len = 0;
3758 let mut props_len = 0;
3759 let mut sprops_len = 0;
3760 let mut static_methods_len = 0;
3761 let mut methods_len = 0;
3763 let mut user_attributes_len = 0;
3764 for attribute in attributes.iter() {
3766 &Node::Attribute(..) => user_attributes_len += 1,
3771 for element in body.iter().copied() {
3773 Node::TraitUse(names) => uses_len += names.len(),
3774 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
3776 xhp_attr_uses_decls,
3777 xhp_attr_enum_values,
3779 props_len += xhp_attr_decls.len();
3780 xhp_attr_uses_len += xhp_attr_uses_decls.len();
3782 for (name, values) in xhp_attr_enum_values {
3783 xhp_enum_values = xhp_enum_values.add(self.arena, name, *values);
3786 Node::TypeConstant(..) => typeconsts_len += 1,
3787 Node::RequireClause(require) => match require.require_type.token_kind() {
3788 Some(TokenKind::Extends) => req_extends_len += 1,
3789 Some(TokenKind::Implements) => req_implements_len += 1,
3792 Node::List(consts @ [Node::Const(..), ..]) => consts_len += consts.len(),
3793 Node::Property(&PropertyNode { decls, is_static }) => {
3795 sprops_len += decls.len()
3797 props_len += decls.len()
3800 Node::Constructor(&ConstructorNode { properties, .. }) => {
3801 props_len += properties.len()
3803 Node::Method(&MethodNode { is_static, .. }) => {
3805 static_methods_len += 1
3814 let mut constructor = None;
3816 let mut uses = Vec::with_capacity_in(uses_len, self.arena);
3817 let mut xhp_attr_uses = Vec::with_capacity_in(xhp_attr_uses_len, self.arena);
3818 let mut req_extends = Vec::with_capacity_in(req_extends_len, self.arena);
3819 let mut req_implements = Vec::with_capacity_in(req_implements_len, self.arena);
3820 let mut consts = Vec::with_capacity_in(consts_len, self.arena);
3821 let mut typeconsts = Vec::with_capacity_in(typeconsts_len, self.arena);
3822 let mut props = Vec::with_capacity_in(props_len, self.arena);
3823 let mut sprops = Vec::with_capacity_in(sprops_len, self.arena);
3824 let mut static_methods = Vec::with_capacity_in(static_methods_len, self.arena);
3825 let mut methods = Vec::with_capacity_in(methods_len, self.arena);
3827 let mut user_attributes = Vec::with_capacity_in(user_attributes_len, self.arena);
3828 for attribute in attributes.iter() {
3830 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(&attr)),
3834 // Match ordering of attributes produced by the OCaml decl parser (even
3835 // though it's the reverse of the syntactic ordering).
3836 user_attributes.reverse();
3838 // xhp props go after regular props, regardless of their order in file
3839 let mut xhp_props = vec![];
3841 for element in body.iter().copied() {
3843 Node::TraitUse(names) => {
3844 uses.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
3846 Node::XhpClassAttributeDeclaration(&XhpClassAttributeDeclarationNode {
3848 xhp_attr_uses_decls,
3851 xhp_props.extend(xhp_attr_decls);
3852 xhp_attr_uses.extend(
3855 .filter_map(|&node| self.node_to_ty(node)),
3858 Node::TypeConstant(constant) => typeconsts.push(constant),
3859 Node::RequireClause(require) => match require.require_type.token_kind() {
3860 Some(TokenKind::Extends) => {
3861 req_extends.extend(self.node_to_ty(require.name).iter())
3863 Some(TokenKind::Implements) => {
3864 req_implements.extend(self.node_to_ty(require.name).iter())
3868 Node::List(&const_nodes @ [Node::Const(..), ..]) => {
3869 for node in const_nodes {
3870 if let &Node::Const(decl) = node {
3875 Node::Property(&PropertyNode { decls, is_static }) => {
3876 for property in decls {
3878 sprops.push(property)
3880 props.push(property)
3884 Node::Constructor(&ConstructorNode { method, properties }) => {
3885 constructor = Some(method);
3886 for property in properties {
3887 props.push(property)
3890 Node::Method(&MethodNode { method, is_static }) => {
3892 static_methods.push(method);
3894 methods.push(method);
3897 _ => {} // It's not our job to report errors here.
3901 props.extend(xhp_props.into_iter());
3903 let class_attributes = self.to_attributes(attributes);
3904 if class_attributes.const_ {
3905 for prop in props.iter_mut() {
3906 if !prop.flags.contains(PropFlags::CONST) {
3907 *prop = self.alloc(ShallowProp {
3908 flags: prop.flags | PropFlags::CONST,
3915 let uses = uses.into_bump_slice();
3916 let xhp_attr_uses = xhp_attr_uses.into_bump_slice();
3917 let req_extends = req_extends.into_bump_slice();
3918 let req_implements = req_implements.into_bump_slice();
3919 let consts = consts.into_bump_slice();
3920 let typeconsts = typeconsts.into_bump_slice();
3921 let props = props.into_bump_slice();
3922 let sprops = sprops.into_bump_slice();
3923 let static_methods = static_methods.into_bump_slice();
3924 let methods = methods.into_bump_slice();
3925 let user_attributes = user_attributes.into_bump_slice();
3926 let extends = self.slice(extends.iter().filter_map(|&node| self.node_to_ty(node)));
3927 let implements = self.slice(implements.iter().filter_map(|&node| self.node_to_ty(node)));
3928 let support_dynamic_type =
3929 self.opts.everything_sdt || class_attributes.support_dynamic_type;
3930 // Pop the type params stack only after creating all inner types.
3931 let tparams = self.pop_type_params(tparams);
3932 let module = class_attributes.module;
3934 let cls = self.alloc(shallow_decl_defs::ShallowClass {
3935 mode: self.file_mode,
3938 has_xhp_keyword: xhp_keyword.is_token(TokenKind::XHP),
3940 module: self.alloc(module),
3951 support_dynamic_type,
3962 self.add_class(name, cls);
3964 self.classish_name_builder.parsed_classish_declaration();
3966 Node::Ignored(SK::ClassishDeclaration)
3969 fn make_property_declaration(
3974 declarators: Self::R,
3975 _semicolon: Self::R,
3977 let (attrs, modifiers, hint) = (attrs, modifiers, hint);
3978 let modifiers = read_member_modifiers(modifiers.iter());
3979 let declarators = self.slice(declarators.iter().filter_map(
3980 |declarator| match declarator {
3981 Node::ListItem(&(name, initializer)) => {
3982 let attributes = self.to_attributes(attrs);
3983 let Id(pos, name) = name.as_variable()?;
3984 let name = if modifiers.is_static {
3987 strip_dollar_prefix(name)
3989 let ty = self.node_to_non_ret_ty(hint);
3990 let ty = if self.opts.interpret_soft_types_as_like_types {
3991 if attributes.soft {
3994 self.alloc(Reason::hint(self.get_pos(hint))),
4004 let needs_init = if self.file_mode == Mode::Mhhi {
4007 initializer.is_ignored()
4009 let mut flags = PropFlags::empty();
4010 flags.set(PropFlags::CONST, attributes.const_);
4011 flags.set(PropFlags::LATEINIT, attributes.late_init);
4012 flags.set(PropFlags::LSB, attributes.lsb);
4013 flags.set(PropFlags::NEEDS_INIT, needs_init);
4014 flags.set(PropFlags::ABSTRACT, modifiers.is_abstract);
4015 flags.set(PropFlags::READONLY, modifiers.is_readonly);
4016 flags.set(PropFlags::PHP_STD_LIB, attributes.php_std_lib);
4021 visibility: if attributes.internal
4022 && modifiers.visibility == aast::Visibility::Public
4024 aast::Visibility::Internal
4026 modifiers.visibility
4034 Node::Property(self.alloc(PropertyNode {
4036 is_static: modifiers.is_static,
4040 fn make_xhp_class_attribute_declaration(
4043 attributes: Self::R,
4044 _semicolon: Self::R,
4046 let mut xhp_attr_enum_values = Vec::new_in(self.arena);
4048 let xhp_attr_decls = self.slice(attributes.iter().filter_map(|node| {
4049 let node = match node {
4050 Node::XhpClassAttribute(x) => x,
4053 let Id(pos, name) = node.name;
4054 let name = prefix_colon(self.arena, name);
4056 let (type_, enum_values) = match node.hint {
4057 Node::XhpEnumTy((ty, values)) => (Some(*ty), Some(values)),
4058 _ => (self.node_to_ty(node.hint), None),
4060 if let Some(enum_values) = enum_values {
4061 xhp_attr_enum_values.push((name, *enum_values));
4064 let type_ = if node.nullable && node.tag.is_none() {
4065 type_.and_then(|x| match x {
4067 Ty(_, Ty_::Toption(_)) | Ty(_, Ty_::Tmixed) => type_,
4069 _ => self.node_to_ty(self.hint_ty(x.get_pos()?, Ty_::Toption(x))),
4075 let mut flags = PropFlags::empty();
4076 flags.set(PropFlags::NEEDS_INIT, node.needs_init);
4079 visibility: aast::Visibility::Public,
4081 xhp_attr: Some(shallow_decl_defs::XhpAttr {
4083 has_default: !node.needs_init,
4089 let xhp_attr_uses_decls = self.slice(attributes.iter().filter_map(|x| match x {
4090 Node::XhpAttributeUse(&name) => Some(name),
4094 Node::XhpClassAttributeDeclaration(self.alloc(XhpClassAttributeDeclarationNode {
4095 xhp_attr_enum_values: xhp_attr_enum_values.into_bump_slice(),
4097 xhp_attr_uses_decls,
4101 /// Handle XHP attribute enum declarations.
4103 /// class :foo implements XHPChild {
4105 /// enum {'big', 'small'} size; // this line
4107 fn make_xhp_enum_type(
4109 enum_keyword: Self::R,
4110 _left_brace: Self::R,
4111 xhp_enum_values: Self::R,
4112 right_brace: Self::R,
4114 // Infer the type hint from the first value.
4115 // TODO: T88207956 consider all the values.
4116 let ty = xhp_enum_values
4119 .and_then(|node| self.node_to_ty(*node))
4120 .and_then(|node_ty| {
4121 let pos = self.merge_positions(enum_keyword, right_brace);
4122 let ty_ = node_ty.1;
4123 Some(self.alloc(Ty(self.alloc(Reason::hint(pos)), ty_)))
4126 let mut values = Vec::new_in(self.arena);
4127 for node in xhp_enum_values.iter() {
4128 // XHP enum values may only be string or int literals.
4130 Node::IntLiteral(&(s, _)) => {
4131 let i = s.parse::<isize>().unwrap_or(0);
4132 values.push(XhpEnumValue::XEVInt(i));
4134 Node::StringLiteral(&(s, _)) => {
4135 let owned_str = std::string::String::from_utf8_lossy(s);
4136 values.push(XhpEnumValue::XEVString(self.arena.alloc_str(&owned_str)));
4143 Some(ty) => Node::XhpEnumTy(self.alloc((&ty, values.into_bump_slice()))),
4144 None => Node::Ignored(SK::XHPEnumType),
4148 fn make_xhp_class_attribute(
4152 initializer: Self::R,
4155 let name = match name.as_id() {
4157 None => return Node::Ignored(SK::XHPClassAttribute),
4159 Node::XhpClassAttribute(self.alloc(XhpClassAttributeNode {
4162 needs_init: !initializer.is_present(),
4163 tag: match tag.token_kind() {
4164 Some(TokenKind::Required) => Some(XhpAttrTag::Required),
4165 Some(TokenKind::Lateinit) => Some(XhpAttrTag::Lateinit),
4168 nullable: initializer.is_token(TokenKind::NullLiteral) || !initializer.is_present(),
4172 fn make_xhp_simple_class_attribute(&mut self, name: Self::R) -> Self::R {
4173 Node::XhpAttributeUse(self.alloc(name))
4176 fn make_property_declarator(&mut self, name: Self::R, initializer: Self::R) -> Self::R {
4177 Node::ListItem(self.alloc((name, initializer)))
4180 fn make_methodish_declaration(
4187 let header = match header {
4188 Node::FunctionHeader(header) => header,
4189 _ => return Node::Ignored(SK::MethodishDeclaration),
4191 // If we don't have a body, use the closing token. A closing token of
4192 // '}' indicates a regular function, while a closing token of ';'
4193 // indicates an abstract function.
4194 let body = if body.is_ignored() { closer } else { body };
4195 let modifiers = read_member_modifiers(header.modifiers.iter());
4196 let is_constructor = header.name.is_token(TokenKind::Construct);
4197 let is_method = true;
4198 let (id, ty, properties) = match self.function_to_ty(is_method, attrs, header, body) {
4199 Some(tuple) => tuple,
4200 None => return Node::Ignored(SK::MethodishDeclaration),
4202 let attributes = self.to_attributes(attrs);
4203 let deprecated = attributes.deprecated.map(|msg| {
4204 let mut s = String::new_in(self.arena);
4205 s.push_str("The method ");
4207 s.push_str(" is deprecated: ");
4211 let mut flags = MethodFlags::empty();
4213 MethodFlags::ABSTRACT,
4214 self.classish_name_builder.in_interface() || modifiers.is_abstract,
4216 flags.set(MethodFlags::FINAL, modifiers.is_final);
4217 flags.set(MethodFlags::OVERRIDE, attributes.override_);
4219 MethodFlags::DYNAMICALLYCALLABLE,
4220 attributes.dynamically_callable,
4222 flags.set(MethodFlags::PHP_STD_LIB, attributes.php_std_lib);
4223 let visibility = match modifiers.visibility {
4224 aast::Visibility::Public => {
4225 if attributes.internal {
4226 aast::Visibility::Internal
4228 aast::Visibility::Public
4231 _ => modifiers.visibility,
4234 let mut user_attributes = Vec::new_in(self.arena);
4235 if !self.omit_user_attributes_irrelevant_to_typechecking {
4236 for attribute in attrs.iter() {
4238 Node::Attribute(attr) => {
4239 user_attributes.push(self.user_attribute_to_decl(attr))
4244 // Match ordering of attributes produced by the OCaml decl parser (even
4245 // though it's the reverse of the syntactic ordering).
4246 user_attributes.reverse();
4248 let user_attributes = user_attributes.into_bump_slice();
4250 let method = self.alloc(ShallowMethod {
4256 attributes: user_attributes,
4259 Node::Constructor(self.alloc(ConstructorNode { method, properties }))
4261 Node::Method(self.alloc(MethodNode {
4263 is_static: modifiers.is_static,
4268 fn make_classish_body(
4270 _left_brace: Self::R,
4272 _right_brace: Self::R,
4274 Node::ClassishBody(self.alloc(elements.as_slice(self.arena)))
4277 fn make_enum_declaration(
4279 attributes: Self::R,
4284 constraint: Self::R,
4285 _left_brace: Self::R,
4286 use_clauses: Self::R,
4287 enumerators: Self::R,
4288 _right_brace: Self::R,
4290 let id = match self.elaborate_defined_id(name) {
4292 None => return Node::Ignored(SK::EnumDeclaration),
4294 let hint = match self.node_to_ty(extends) {
4296 None => return Node::Ignored(SK::EnumDeclaration),
4298 let extends = match self.node_to_ty(self.make_apply(
4299 (self.get_pos(name), "\\HH\\BuiltinEnum"),
4304 None => return Node::Ignored(SK::EnumDeclaration),
4307 let consts = self.slice(enumerators.iter().filter_map(|node| match node {
4308 &Node::Const(const_) => Some(const_),
4311 let mut user_attributes = Vec::with_capacity_in(attributes.len(), self.arena);
4312 for attribute in attributes.iter() {
4314 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4318 // Match ordering of attributes produced by the OCaml decl parser (even
4319 // though it's the reverse of the syntactic ordering).
4320 user_attributes.reverse();
4321 let user_attributes = user_attributes.into_bump_slice();
4323 let constraint = match constraint {
4324 Node::TypeConstraint(&(_kind, ty)) => self.node_to_ty(ty),
4328 let mut includes_len = 0;
4329 for element in use_clauses.iter() {
4331 Node::EnumUse(names) => includes_len += names.len(),
4335 let mut includes = Vec::with_capacity_in(includes_len, self.arena);
4336 for element in use_clauses.iter() {
4338 Node::EnumUse(names) => {
4339 includes.extend(names.iter().filter_map(|&name| self.node_to_ty(name)))
4344 let includes = includes.into_bump_slice();
4346 let parsed_attributes = self.to_attributes(attributes);
4348 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4349 mode: self.file_mode,
4352 has_xhp_keyword: false,
4353 kind: ClassishKind::Cenum,
4354 module: self.alloc(parsed_attributes.module),
4357 where_constraints: &[],
4358 extends: bumpalo::vec![in self.arena; extends].into_bump_slice(),
4361 xhp_enum_values: SMap::empty(),
4363 req_implements: &[],
4365 support_dynamic_type: false,
4371 static_methods: &[],
4374 enum_type: Some(self.alloc(EnumType {
4380 self.add_class(key, cls);
4382 self.classish_name_builder.parsed_classish_declaration();
4384 Node::Ignored(SK::EnumDeclaration)
4387 fn make_enum_use(&mut self, _keyword: Self::R, names: Self::R, _semicolon: Self::R) -> Self::R {
4388 Node::EnumUse(self.alloc(names))
4391 fn begin_enumerator(&mut self) {
4392 self.start_accumulating_const_refs();
4400 _semicolon: Self::R,
4402 let refs = self.stop_accumulating_const_refs();
4403 let id = match self.expect_name(name) {
4405 None => return Node::Ignored(SyntaxKind::Enumerator),
4409 self.alloc(ShallowClassConst {
4410 abstract_: ClassConstKind::CCConcrete,
4413 .infer_const(name, value)
4414 .unwrap_or_else(|| self.tany_with_pos(id.0)),
4420 fn make_enum_class_declaration(
4422 attributes: Self::R,
4424 _enum_keyword: Self::R,
4425 _class_keyword: Self::R,
4429 _extends_keyword: Self::R,
4430 extends_list: Self::R,
4431 _left_brace: Self::R,
4433 _right_brace: Self::R,
4435 let name = match self.elaborate_defined_id(name) {
4437 None => return Node::Ignored(SyntaxKind::EnumClassDeclaration),
4441 .unwrap_or_else(|| self.tany_with_pos(name.0));
4443 let mut is_abstract = false;
4444 for modifier in modifiers.iter() {
4445 match modifier.token_kind() {
4446 Some(TokenKind::Abstract) => is_abstract = true,
4451 let class_kind = if is_abstract {
4452 ClassishKind::CenumClass(&Abstraction::Abstract)
4454 ClassishKind::CenumClass(&Abstraction::Concrete)
4457 let builtin_enum_class_ty = {
4459 let enum_class_ty_ = Ty_::Tapply(self.alloc((name.into(), &[])));
4460 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4461 let elt_ty_ = Ty_::Tapply(self.alloc((
4462 (pos, "\\HH\\MemberOf"),
4463 bumpalo::vec![in self.arena; enum_class_ty, base].into_bump_slice(),
4465 let elt_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), elt_ty_));
4466 let builtin_enum_ty_ = if is_abstract {
4467 Ty_::Tapply(self.alloc(((pos, "\\HH\\BuiltinAbstractEnumClass"), &[])))
4469 Ty_::Tapply(self.alloc((
4470 (pos, "\\HH\\BuiltinEnumClass"),
4471 std::slice::from_ref(self.alloc(elt_ty)),
4474 self.alloc(Ty(self.alloc(Reason::hint(pos)), builtin_enum_ty_))
4477 let consts = self.slice(elements.iter().filter_map(|node| match node {
4478 &Node::Const(const_) => Some(const_),
4482 let mut extends = Vec::with_capacity_in(extends_list.len() + 1, self.arena);
4483 extends.push(builtin_enum_class_ty);
4484 extends.extend(extends_list.iter().filter_map(|&n| self.node_to_ty(n)));
4485 let extends = extends.into_bump_slice();
4486 let includes = &extends[1..];
4488 let mut user_attributes = Vec::with_capacity_in(attributes.len() + 1, self.arena);
4489 for attribute in attributes.iter() {
4491 Node::Attribute(attr) => user_attributes.push(self.user_attribute_to_decl(attr)),
4495 user_attributes.push(self.alloc(shallow_decl_defs::UserAttribute {
4496 name: (name.0, "__EnumClass"),
4497 classname_params: &[],
4499 // Match ordering of attributes produced by the OCaml decl parser (even
4500 // though it's the reverse of the syntactic ordering).
4501 user_attributes.reverse();
4502 let user_attributes = user_attributes.into_bump_slice();
4504 let cls = self.alloc(shallow_decl_defs::ShallowClass {
4505 mode: self.file_mode,
4508 has_xhp_keyword: false,
4510 module: &None, // TODO: grab module from attributes
4513 where_constraints: &[],
4517 xhp_enum_values: SMap::empty(),
4519 req_implements: &[],
4521 support_dynamic_type: false,
4527 static_methods: &[],
4530 enum_type: Some(self.alloc(EnumType {
4536 self.add_class(name.1, cls);
4538 self.classish_name_builder.parsed_classish_declaration();
4540 Node::Ignored(SyntaxKind::EnumClassDeclaration)
4543 fn begin_enum_class_enumerator(&mut self) {
4544 self.start_accumulating_const_refs();
4547 fn make_enum_class_enumerator(
4552 _initializer: Self::R,
4553 _semicolon: Self::R,
4555 let refs = self.stop_accumulating_const_refs();
4556 let name = match self.expect_name(name) {
4558 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4561 let has_abstract_keyword = modifiers
4563 .any(|node| node.is_token(TokenKind::Abstract));
4564 let abstract_ = if has_abstract_keyword {
4565 /* default values not allowed atm */
4566 ClassConstKind::CCAbstract(false)
4568 ClassConstKind::CCConcrete
4572 .unwrap_or_else(|| self.tany_with_pos(name.0));
4573 let class_name = match self.classish_name_builder.get_current_classish_name() {
4575 None => return Node::Ignored(SyntaxKind::EnumClassEnumerator),
4577 let enum_class_ty_ = Ty_::Tapply(self.alloc(((pos, class_name.0), &[])));
4578 let enum_class_ty = self.alloc(Ty(self.alloc(Reason::hint(pos)), enum_class_ty_));
4579 let type_ = Ty_::Tapply(self.alloc((
4580 (pos, "\\HH\\MemberOf"),
4581 bumpalo::vec![in self.arena; enum_class_ty, type_].into_bump_slice(),
4583 let type_ = self.alloc(Ty(self.alloc(Reason::hint(pos)), type_));
4584 Node::Const(self.alloc(ShallowClassConst {
4592 fn make_tuple_type_specifier(
4594 left_paren: Self::R,
4596 right_paren: Self::R,
4598 // We don't need to include the tys list in this position merging
4599 // because by definition it's already contained by the two brackets.
4600 let pos = self.merge_positions(left_paren, right_paren);
4601 let tys = self.slice(tys.iter().filter_map(|&node| self.node_to_ty(node)));
4602 self.hint_ty(pos, Ty_::Ttuple(tys))
4605 fn make_tuple_type_explicit_specifier(
4608 _left_angle: Self::R,
4610 right_angle: Self::R,
4612 let id = (self.get_pos(keyword), "\\tuple");
4613 // This is an error--tuple syntax is (A, B), not tuple<A, B>.
4614 // OCaml decl makes a Tapply rather than a Ttuple here.
4615 self.make_apply(id, types, self.get_pos(right_angle))
4618 fn make_intersection_type_specifier(
4620 left_paren: Self::R,
4622 right_paren: Self::R,
4624 let pos = self.merge_positions(left_paren, right_paren);
4625 let tys = self.slice(tys.iter().filter_map(|x| match x {
4626 Node::ListItem(&(ty, _ampersand)) => self.node_to_ty(ty),
4627 &x => self.node_to_ty(x),
4629 self.hint_ty(pos, Ty_::Tintersection(tys))
4632 fn make_union_type_specifier(
4634 left_paren: Self::R,
4636 right_paren: Self::R,
4638 let pos = self.merge_positions(left_paren, right_paren);
4639 let tys = self.slice(tys.iter().filter_map(|x| match x {
4640 Node::ListItem(&(ty, _bar)) => self.node_to_ty(ty),
4641 &x => self.node_to_ty(x),
4643 self.hint_ty(pos, Ty_::Tunion(tys))
4646 fn make_shape_type_specifier(
4654 let fields = fields;
4655 let fields_iter = fields.iter();
4656 let mut fields = AssocListMut::new_in(self.arena);
4657 for node in fields_iter {
4658 if let &Node::ShapeFieldSpecifier(&ShapeFieldNode { name, type_ }) = node {
4659 fields.insert(self.make_t_shape_field_name(name), type_)
4662 let kind = match open.token_kind() {
4663 Some(TokenKind::DotDotDot) => ShapeKind::OpenShape,
4664 _ => ShapeKind::ClosedShape,
4666 let pos = self.merge_positions(shape, rparen);
4667 self.hint_ty(pos, Ty_::Tshape(self.alloc((kind, fields.into()))))
4670 fn make_classname_type_specifier(
4675 _trailing_comma: Self::R,
4678 let id = match classname.as_id() {
4680 None => return Node::Ignored(SK::ClassnameTypeSpecifier),
4682 if gt.is_ignored() {
4683 self.prim_ty(aast::Tprim::Tstring, id.0)
4686 (id.0, self.elaborate_raw_id(id.1)),
4688 self.merge_positions(classname, gt),
4693 fn make_scope_resolution_expression(
4695 class_name: Self::R,
4699 let pos = self.merge_positions(class_name, value);
4700 let Id(class_name_pos, class_name_str) = match self.expect_name(class_name) {
4701 Some(id) => self.elaborate_id(id),
4702 None => return Node::Ignored(SK::ScopeResolutionExpression),
4704 let class_id = self.alloc(aast::ClassId(
4708 Node::Name(("self", _)) => aast::ClassId_::CIself,
4709 _ => aast::ClassId_::CI(self.alloc(Id(class_name_pos, class_name_str))),
4712 let value_id = match self.expect_name(value) {
4714 None => return Node::Ignored(SK::ScopeResolutionExpression),
4716 self.accumulate_const_ref(class_id, &value_id);
4717 Node::Expr(self.alloc(aast::Expr(
4720 nast::Expr_::ClassConst(self.alloc((class_id, self.alloc((value_id.0, value_id.1))))),
4724 fn make_field_specifier(
4726 question_token: Self::R,
4731 let optional = question_token.is_present();
4732 let ty = match self.node_to_ty(type_) {
4734 None => return Node::Ignored(SK::FieldSpecifier),
4736 let name = match self.make_shape_field_name(name) {
4738 None => return Node::Ignored(SK::FieldSpecifier),
4740 Node::ShapeFieldSpecifier(self.alloc(ShapeFieldNode {
4741 name: self.alloc(ShapeField(name)),
4742 type_: self.alloc(ShapeFieldType { optional, ty }),
4746 fn make_field_initializer(&mut self, key: Self::R, _arrow: Self::R, value: Self::R) -> Self::R {
4747 Node::ListItem(self.alloc((key, value)))
4750 fn make_varray_type_specifier(
4752 varray_keyword: Self::R,
4753 _less_than: Self::R,
4755 _trailing_comma: Self::R,
4756 greater_than: Self::R,
4758 let tparam = match self.node_to_ty(tparam) {
4760 None => self.tany_with_pos(self.get_pos(varray_keyword)),
4763 self.merge_positions(varray_keyword, greater_than),
4764 Ty_::Tapply(self.alloc((
4766 self.get_pos(varray_keyword),
4767 naming_special_names::collections::VEC,
4769 self.alloc([tparam]),
4774 fn make_darray_type_specifier(
4777 _less_than: Self::R,
4780 value_type: Self::R,
4781 _trailing_comma: Self::R,
4782 greater_than: Self::R,
4784 let pos = self.merge_positions(darray, greater_than);
4785 let key_type = self.node_to_ty(key_type).unwrap_or(TANY);
4786 let value_type = self.node_to_ty(value_type).unwrap_or(TANY);
4789 Ty_::Tapply(self.alloc((
4791 self.get_pos(darray),
4792 naming_special_names::collections::DICT,
4794 self.alloc([key_type, value_type]),
4799 fn make_old_attribute_specification(
4806 Node::List(nodes) => {
4807 Node::BracketedList(self.alloc((self.get_pos(ltlt), nodes, self.get_pos(gtgt))))
4809 _ => Node::Ignored(SK::OldAttributeSpecification),
4813 fn make_constructor_call(
4816 _left_paren: Self::R,
4818 _right_paren: Self::R,
4820 let unqualified_name = match self.expect_name(name) {
4822 None => return Node::Ignored(SK::ConstructorCall),
4824 let name = if unqualified_name.1.starts_with("__") {
4827 match self.expect_name(name) {
4828 Some(name) => self.elaborate_id(name),
4829 None => return Node::Ignored(SK::ConstructorCall),
4832 let classname_params = self.slice(args.iter().filter_map(|node| match node {
4833 Node::Expr(aast::Expr(
4836 aast::Expr_::ClassConst(&(
4837 aast::ClassId(_, _, aast::ClassId_::CI(&Id(pos, class_name))),
4841 let name = self.elaborate_id(Id(pos, class_name));
4842 Some(ClassNameParam { name, full_pos })
4847 let string_literal_params = if match name.1 {
4848 "__Deprecated" | "__Cipp" | "__CippLocal" | "__Policied" | "__Module" => true,
4851 fn fold_string_concat<'a>(expr: &nast::Expr<'a>, acc: &mut Vec<'a, u8>) {
4853 &aast::Expr(_, _, aast::Expr_::String(val)) => acc.extend_from_slice(val),
4854 &aast::Expr(_, _, aast::Expr_::Binop(&(Bop::Dot, e1, e2))) => {
4855 fold_string_concat(&e1, acc);
4856 fold_string_concat(&e2, acc);
4862 self.slice(args.iter().filter_map(|expr| match expr {
4863 Node::StringLiteral((x, _)) => Some(*x),
4864 Node::Expr(e @ aast::Expr(_, _, aast::Expr_::Binop(_))) => {
4865 let mut acc = Vec::new_in(self.arena);
4866 fold_string_concat(e, &mut acc);
4867 Some(acc.into_bump_slice().into())
4875 Node::Attribute(self.alloc(UserAttributeNode {
4878 string_literal_params,
4886 _semicolon: Self::R,
4888 Node::TraitUse(self.alloc(names))
4891 fn make_trait_use_conflict_resolution(
4895 _left_brace: Self::R,
4897 _right_brace: Self::R,
4899 Node::TraitUse(self.alloc(names))
4902 fn make_require_clause(
4905 require_type: Self::R,
4907 _semicolon: Self::R,
4909 Node::RequireClause(self.alloc(RequireClause { require_type, name }))
4912 fn make_nullable_type_specifier(&mut self, question_mark: Self::R, hint: Self::R) -> Self::R {
4913 let pos = self.merge_positions(question_mark, hint);
4914 let ty = match self.node_to_ty(hint) {
4916 None => return Node::Ignored(SK::NullableTypeSpecifier),
4918 self.hint_ty(pos, Ty_::Toption(ty))
4921 fn make_like_type_specifier(&mut self, tilde: Self::R, hint: Self::R) -> Self::R {
4922 let pos = self.merge_positions(tilde, hint);
4923 let ty = match self.node_to_ty(hint) {
4925 None => return Node::Ignored(SK::LikeTypeSpecifier),
4927 self.hint_ty(pos, Ty_::Tlike(ty))
4930 fn make_closure_type_specifier(
4932 outer_left_paren: Self::R,
4933 readonly_keyword: Self::R,
4934 _function_keyword: Self::R,
4935 _inner_left_paren: Self::R,
4936 parameter_list: Self::R,
4937 _inner_right_paren: Self::R,
4938 capability: Self::R,
4940 readonly_ret: Self::R,
4941 return_type: Self::R,
4942 outer_right_paren: Self::R,
4944 let make_param = |fp: &'a FunParamDecl<'a>| -> &'a FunParam<'a> {
4945 let mut flags = FunParamFlags::empty();
4948 ParamMode::FPinout => {
4949 flags |= FunParamFlags::INOUT;
4951 ParamMode::FPnormal => {}
4955 flags |= FunParamFlags::READONLY;
4958 self.alloc(FunParam {
4959 pos: self.get_pos(fp.hint),
4961 type_: self.alloc(PossiblyEnforcedTy {
4962 enforced: Enforcement::Unenforced,
4963 type_: self.node_to_ty(fp.hint).unwrap_or_else(|| tany()),
4969 let arity = parameter_list
4971 .find_map(|&node| match node {
4972 Node::FunParam(fp) if fp.variadic => Some(FunArity::Fvariadic(make_param(fp))),
4975 .unwrap_or(FunArity::Fstandard);
4977 let params = self.slice(parameter_list.iter().filter_map(|&node| match node {
4978 Node::FunParam(fp) if !fp.variadic => Some(make_param(fp)),
4982 let ret = match self.node_to_ty(return_type) {
4984 None => return Node::Ignored(SK::ClosureTypeSpecifier),
4986 let pos = self.merge_positions(outer_left_paren, outer_right_paren);
4987 let implicit_params = self.as_fun_implicit_params(capability, pos);
4989 let mut flags = FunTypeFlags::empty();
4990 if readonly_ret.is_token(TokenKind::Readonly) {
4991 flags |= FunTypeFlags::RETURNS_READONLY;
4993 if readonly_keyword.is_token(TokenKind::Readonly) {
4994 flags |= FunTypeFlags::READONLY_THIS;
4999 Ty_::Tfun(self.alloc(FunType {
5002 where_constraints: &[],
5005 ret: self.alloc(PossiblyEnforcedTy {
5006 enforced: Enforcement::Unenforced,
5010 ifc_decl: default_ifc_fun_decl(),
5015 fn make_closure_parameter_type_specifier(
5021 let kind = if inout.is_token(TokenKind::Inout) {
5026 Node::FunParam(self.alloc(FunParamDecl {
5027 attributes: Node::Ignored(SK::Missing),
5028 visibility: Node::Ignored(SK::Missing),
5031 readonly: readonly.is_token(TokenKind::Readonly),
5032 pos: self.get_pos(hint),
5035 initializer: Node::Ignored(SK::Missing),
5039 fn make_type_const_declaration(
5041 attributes: Self::R,
5043 _const_keyword: Self::R,
5044 _type_keyword: Self::R,
5046 _type_parameters: Self::R,
5047 as_constraint: Self::R,
5050 _semicolon: Self::R,
5052 let attributes = self.to_attributes(attributes);
5053 let has_abstract_keyword = modifiers
5055 .any(|node| node.is_token(TokenKind::Abstract));
5056 let as_constraint = match as_constraint {
5057 Node::TypeConstraint(innards) => self.node_to_ty(innards.1),
5060 let type_ = self.node_to_ty(type_);
5061 let kind = if has_abstract_keyword {
5062 // Abstract type constant:
5063 // abstract const type T [as X] [super Y] [= Z];
5064 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5066 super_constraint: None,
5070 if let Some(t) = type_ {
5071 // Concrete type constant:
5072 // const type T = Z;
5073 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type: t }))
5075 // concrete or type constant requires a value
5076 return Node::Ignored(SK::TypeConstDeclaration);
5079 let name = match name.as_id() {
5081 None => return Node::Ignored(SK::TypeConstDeclaration),
5083 Node::TypeConstant(self.alloc(ShallowTypeconst {
5086 enforceable: match attributes.enforceable {
5087 Some(pos) => (pos, true),
5088 None => (Pos::none(), false),
5090 reifiable: attributes.reifiable,
5095 fn make_context_const_declaration(
5098 _const_keyword: Self::R,
5099 _ctx_keyword: Self::R,
5101 _type_parameters: Self::R,
5102 constraints: Self::R,
5105 _semicolon: Self::R,
5107 let name = match name.as_id() {
5109 None => return Node::Ignored(SK::TypeConstDeclaration),
5111 let has_abstract_keyword = modifiers
5113 .any(|node| node.is_token(TokenKind::Abstract));
5114 let context = self.node_to_ty(ctx_list);
5116 // note: lowerer ensures that there's at most 1 constraint of each kind
5117 let mut as_constraint = None;
5118 let mut super_constraint = None;
5119 for c in constraints.iter() {
5120 if let Node::ContextConstraint(&(kind, hint)) = c {
5121 let ty = self.node_to_ty(hint);
5123 ConstraintKind::ConstraintSuper => super_constraint = ty,
5124 ConstraintKind::ConstraintAs => as_constraint = ty,
5129 let kind = if has_abstract_keyword {
5130 Typeconst::TCAbstract(self.alloc(AbstractTypeconst {
5136 if let Some(tc_type) = context {
5137 Typeconst::TCConcrete(self.alloc(ConcreteTypeconst { tc_type }))
5139 /* Concrete type const must have a value */
5140 return Node::Ignored(SK::TypeConstDeclaration);
5143 Node::TypeConstant(self.alloc(ShallowTypeconst {
5146 enforceable: (Pos::none(), false),
5152 fn make_decorated_expression(&mut self, decorator: Self::R, expr: Self::R) -> Self::R {
5153 Node::ListItem(self.alloc((decorator, expr)))
5156 fn make_type_constant(
5159 _coloncolon: Self::R,
5160 constant_name: Self::R,
5162 let id = match self.expect_name(constant_name) {
5164 None => return Node::Ignored(SK::TypeConstant),
5166 let pos = self.merge_positions(ty, constant_name);
5167 let ty = match (ty, self.classish_name_builder.get_current_classish_name()) {
5168 (Node::Name(("self", self_pos)), Some((name, class_name_pos))) => {
5169 // In classes, we modify the position when rewriting the
5170 // `self` keyword to point to the class name. In traits,
5171 // we don't (because traits are not types). We indicate
5172 // that the position shouldn't be rewritten with the
5174 let id_pos = if class_name_pos.is_none() {
5179 let reason = self.alloc(Reason::hint(self_pos));
5180 let ty_ = Ty_::Tapply(self.alloc(((id_pos, name), &[][..])));
5181 self.alloc(Ty(reason, ty_))
5183 _ => match self.node_to_ty(ty) {
5185 None => return Node::Ignored(SK::TypeConstant),
5188 let reason = self.alloc(Reason::hint(pos));
5189 // The reason-rewriting here is only necessary to match the
5190 // behavior of OCaml decl (which flattens and then unflattens
5191 // Haccess hints, losing some position information).
5192 let ty = self.rewrite_taccess_reasons(ty, reason);
5193 Node::Ty(self.alloc(Ty(
5195 Ty_::Taccess(self.alloc(TaccessType(ty, id.into()))),
5199 fn make_soft_type_specifier(&mut self, at_token: Self::R, hint: Self::R) -> Self::R {
5200 let pos = self.merge_positions(at_token, hint);
5201 let hint = match self.node_to_ty(hint) {
5203 None => return Node::Ignored(SK::SoftTypeSpecifier),
5205 // Use the type of the hint as-is (i.e., throw away the knowledge that
5206 // we had a soft type specifier here--the typechecker does not use it).
5207 // Replace its Reason with one including the position of the `@` token.
5210 if self.opts.interpret_soft_types_as_like_types {
5218 // A type specifier preceded by an attribute list. At the time of writing,
5219 // only the <<__Soft>> attribute is permitted here.
5220 fn make_attributized_specifier(&mut self, attributes: Self::R, hint: Self::R) -> Self::R {
5222 Node::BracketedList((
5224 [Node::Attribute(UserAttributeNode {
5225 name: Id(_, "__Soft"),
5230 let attributes_pos = self.merge(*ltlt_pos, *gtgt_pos);
5231 let hint_pos = self.get_pos(hint);
5232 // Use the type of the hint as-is (i.e., throw away the
5233 // knowledge that we had a soft type specifier here--the
5234 // typechecker does not use it). Replace its Reason with one
5235 // including the position of the attribute list.
5236 let hint = match self.node_to_ty(hint) {
5238 None => return Node::Ignored(SK::AttributizedSpecifier),
5242 self.merge(attributes_pos, hint_pos),
5243 if self.opts.interpret_soft_types_as_like_types {
5254 fn make_vector_type_specifier(
5257 _left_angle: Self::R,
5259 _trailing_comma: Self::R,
5260 right_angle: Self::R,
5262 let id = match self.expect_name(vec) {
5264 None => return Node::Ignored(SK::VectorTypeSpecifier),
5266 let id = (id.0, self.elaborate_raw_id(id.1));
5267 self.make_apply(id, hint, self.get_pos(right_angle))
5270 fn make_dictionary_type_specifier(
5273 _left_angle: Self::R,
5274 type_arguments: Self::R,
5275 right_angle: Self::R,
5277 let id = match self.expect_name(dict) {
5279 None => return Node::Ignored(SK::DictionaryTypeSpecifier),
5281 let id = (id.0, self.elaborate_raw_id(id.1));
5282 self.make_apply(id, type_arguments, self.get_pos(right_angle))
5285 fn make_keyset_type_specifier(
5288 _left_angle: Self::R,
5290 _trailing_comma: Self::R,
5291 right_angle: Self::R,
5293 let id = match self.expect_name(keyset) {
5295 None => return Node::Ignored(SK::KeysetTypeSpecifier),
5297 let id = (id.0, self.elaborate_raw_id(id.1));
5298 self.make_apply(id, hint, self.get_pos(right_angle))
5301 fn make_variable_expression(&mut self, _expression: Self::R) -> Self::R {
5302 Node::Ignored(SK::VariableExpression)
5305 fn make_subscript_expression(
5308 _left_bracket: Self::R,
5310 _right_bracket: Self::R,
5312 Node::Ignored(SK::SubscriptExpression)
5315 fn make_member_selection_expression(
5321 Node::Ignored(SK::MemberSelectionExpression)
5324 fn make_object_creation_expression(
5326 _new_keyword: Self::R,
5329 Node::Ignored(SK::ObjectCreationExpression)
5332 fn make_safe_member_selection_expression(
5338 Node::Ignored(SK::SafeMemberSelectionExpression)
5341 fn make_function_call_expression(
5344 _type_args: Self::R,
5345 _enum_class_label: Self::R,
5346 _left_paren: Self::R,
5347 _argument_list: Self::R,
5348 _right_paren: Self::R,
5350 Node::Ignored(SK::FunctionCallExpression)
5353 fn make_list_expression(
5356 _left_paren: Self::R,
5358 _right_paren: Self::R,
5360 Node::Ignored(SK::ListExpression)