1 // Copyright (c) 2019, Facebook, Inc.
2 // All rights reserved.
4 // This source code is licensed under the MIT license found in the
5 // LICENSE file in the "hack" directory of this source tree.
7 use crate::{desugar_expression_tree::desugar, modifier};
8 use bstr::{BStr, BString, ByteSlice, B};
11 use hh_autoimport_rust as hh_autoimport;
12 use itertools::Either;
13 use lint_rust::LintError;
14 use naming_special_names_rust::{
15 classes as special_classes, literal, rx, special_functions, special_idents,
16 typehints as special_typehints, user_attributes as special_attrs,
18 use ocamlrep::rc::RcOc;
21 aast_visitor::{AstParams, Node, Visitor},
24 doc_comment::DocComment,
25 errors::{Error as HHError, Naming, NastCheck},
27 global_options::GlobalOptions,
28 namespace_env::Env as NamespaceEnv,
32 use parser_core_types::{
33 indexed_source_text::IndexedSourceText,
34 lexable_token::LexablePositionedToken,
35 source_text::SourceText,
36 syntax::{SyntaxValueType, SyntaxValueWithKind},
38 positioned_token::{PositionedToken, TokenFactory as PositionedTokenFactory},
39 positioned_value::PositionedValue,
41 syntax_variant_generated::{SyntaxVariant::*, *},
43 syntax_error, syntax_kind,
44 syntax_trait::SyntaxTrait,
45 token_factory::TokenMutator,
46 token_kind::TokenKind as TK,
48 use regex::bytes::Regex;
49 use stack_limit::StackLimit;
51 cell::{Ref, RefCell, RefMut},
58 fn unescape_single(s: &str) -> std::result::Result<BString, escaper::InvalidString> {
59 Ok(escaper::unescape_single(s)?.into())
62 fn unescape_nowdoc(s: &str) -> std::result::Result<BString, escaper::InvalidString> {
63 Ok(escaper::unescape_nowdoc(s)?.into())
66 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
67 pub enum LiftedAwaitKind {
72 type LiftedAwaitExprs = Vec<(Option<ast::Lid>, ast::Expr)>;
74 #[derive(Debug, Clone)]
75 pub struct LiftedAwaits {
76 pub awaits: LiftedAwaitExprs,
77 lift_kind: LiftedAwaitKind,
80 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
81 pub enum ExprLocation {
87 RightOfAssignmentInUsingStatement,
92 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
93 pub enum SuspensionKind {
98 #[derive(Copy, Clone, Eq, PartialEq)]
108 suspension_kind: SuspensionKind,
110 constrs: Vec<ast::WhereConstraintHint>,
111 type_parameters: Vec<ast::Tparam>,
112 parameters: Vec<ast::FunParam>,
113 contexts: Option<ast::Contexts>,
114 unsafe_contexts: Option<ast::Contexts>,
115 return_type: Option<ast::Hint>,
119 fn make_empty<TF: Clone>(env: &Env<TF>) -> Self {
121 suspension_kind: SuspensionKind::SKSync,
122 name: ast::Id(env.mk_none_pos(), String::from("<ANONYMOUS>")),
124 type_parameters: vec![],
127 unsafe_contexts: None,
135 pub cls_reified_generics: HashSet<String>,
136 pub in_static_method: bool,
137 pub parent_maybe_reified: bool,
138 /// This provides a generic mechanism to delay raising parsing errors;
139 /// since we're moving FFP errors away from CST to a stage after lowering
140 /// _and_ want to prioritize errors before lowering, the lowering errors
141 /// must be merely stored when the lowerer runs (until check for FFP runs (on AST)
142 /// and raised _after_ FFP error checking (unless we run the lowerer twice,
143 /// which would be expensive).
144 pub lowpri_errors: Vec<(Pos, String)>,
145 /// hh_errors captures errors after parsing, naming, nast, etc.
146 pub hh_errors: Vec<HHError>,
147 pub lint_errors: Vec<LintError>,
148 pub doc_comments: Vec<Option<DocComment>>,
150 pub local_id_counter: isize,
152 // TODO(hrust): this check is to avoid crash in Ocaml.
153 // Remove it after all Ocaml callers are eliminated.
154 pub exp_recursion_depth: usize,
157 const EXP_RECUSION_LIMIT: usize = 30_000;
159 #[derive(Debug, Clone)]
160 pub struct Env<'a, TF> {
162 pub keep_errors: bool,
164 /// Show errors even in quick mode. Does not override keep_errors. Hotfix
165 /// until we can properly set up saved states to surface parse errors during
166 /// typechecking properly.
167 pub show_all_errors: bool,
169 file_mode: file_info::Mode,
170 disable_lowering_parsing_error: bool,
171 pub top_level_statements: bool, /* Whether we are (still) considering TLSs*/
173 // Cache none pos, lazy_static doesn't allow Rc.
175 pub empty_ns_env: RcOc<NamespaceEnv>,
177 pub saw_yield: bool, /* Information flowing back up */
178 pub lifted_awaits: Option<LiftedAwaits>,
179 pub tmp_var_counter: isize,
181 pub indexed_source_text: &'a IndexedSourceText<'a>,
182 pub parser_options: &'a GlobalOptions,
183 pub stack_limit: Option<&'a StackLimit>,
185 pub token_factory: TF,
188 state: Rc<RefCell<State>>,
191 impl<'a, TF: Clone> Env<'a, TF> {
196 show_all_errors: bool,
198 disable_lowering_parsing_error: bool,
199 mode: file_info::Mode,
200 indexed_source_text: &'a IndexedSourceText<'a>,
201 parser_options: &'a GlobalOptions,
202 use_default_namespace: bool,
203 stack_limit: Option<&'a StackLimit>,
207 // (hrust) Ported from namespace_env.ml
208 let empty_ns_env = if use_default_namespace {
209 let mut nsenv = NamespaceEnv::empty(vec![], false, false);
210 nsenv.is_codegen = codegen;
211 nsenv.disable_xhp_element_mangling = parser_options.po_disable_xhp_element_mangling;
214 let mut ns_uses = hh_autoimport::NAMESPACES_MAP.clone();
216 .po_auto_namespace_map
219 &ns_uses.insert(k.into(), v.into());
223 class_uses: hh_autoimport::TYPES_MAP.clone(),
224 fun_uses: hh_autoimport::FUNCS_MAP.clone(),
225 const_uses: hh_autoimport::CONSTS_MAP.clone(),
226 record_def_uses: s_map::SMap::new(),
228 auto_ns_map: parser_options.po_auto_namespace_map.clone(),
230 disable_xhp_element_mangling: parser_options.po_disable_xhp_element_mangling,
240 disable_lowering_parsing_error,
242 top_level_statements: true,
248 pos_none: Pos::make_none(),
249 empty_ns_env: RcOc::new(empty_ns_env),
254 state: Rc::new(RefCell::new(State {
255 cls_reified_generics: HashSet::new(),
256 in_static_method: false,
257 parent_maybe_reified: false,
258 lowpri_errors: vec![],
259 doc_comments: vec![],
263 exp_recursion_depth: 0,
268 fn file_mode(&self) -> file_info::Mode {
272 fn should_surface_error(&self) -> bool {
273 (!self.quick_mode || self.show_all_errors) && self.keep_errors
276 fn is_typechecker(&self) -> bool {
280 fn codegen(&self) -> bool {
284 fn source_text(&self) -> &SourceText<'a> {
285 self.indexed_source_text.source_text()
288 fn fail_open(&self) -> bool {
292 fn cls_reified_generics(&mut self) -> RefMut<HashSet<String>> {
293 RefMut::map(self.state.borrow_mut(), |s| &mut s.cls_reified_generics)
296 fn in_static_method(&mut self) -> RefMut<bool> {
297 RefMut::map(self.state.borrow_mut(), |s| &mut s.in_static_method)
300 fn parent_maybe_reified(&mut self) -> RefMut<bool> {
301 RefMut::map(self.state.borrow_mut(), |s| &mut s.parent_maybe_reified)
304 pub fn lowpri_errors(&mut self) -> RefMut<Vec<(Pos, String)>> {
305 RefMut::map(self.state.borrow_mut(), |s| &mut s.lowpri_errors)
308 pub fn hh_errors(&mut self) -> RefMut<Vec<HHError>> {
309 RefMut::map(self.state.borrow_mut(), |s| &mut s.hh_errors)
312 pub fn lint_errors(&mut self) -> RefMut<Vec<LintError>> {
313 RefMut::map(self.state.borrow_mut(), |s| &mut s.lint_errors)
316 fn top_docblock(&self) -> Ref<Option<DocComment>> {
317 Ref::map(self.state.borrow(), |s| {
318 s.doc_comments.last().unwrap_or(&None)
322 fn exp_recursion_depth(&self) -> RefMut<usize> {
323 RefMut::map(self.state.borrow_mut(), |s| &mut s.exp_recursion_depth)
326 fn next_local_id(&self) -> isize {
327 let mut id = RefMut::map(self.state.borrow_mut(), |s| &mut s.local_id_counter);
332 fn push_docblock(&mut self, doc_comment: Option<DocComment>) {
333 RefMut::map(self.state.borrow_mut(), |s| &mut s.doc_comments).push(doc_comment)
336 fn pop_docblock(&mut self) {
337 RefMut::map(self.state.borrow_mut(), |s| &mut s.doc_comments).pop();
340 fn make_tmp_var_name(&mut self) -> String {
341 let name = String::from(special_idents::TMP_VAR_PREFIX) + &self.tmp_var_counter.to_string();
342 self.tmp_var_counter += 1;
346 fn mk_none_pos(&self) -> Pos {
347 self.pos_none.clone()
350 fn clone_and_unset_toplevel_if_toplevel<'b, 'c>(
351 e: &'b mut Env<'c, TF>,
352 ) -> impl AsMut<Env<'c, TF>> + 'b {
353 if e.top_level_statements {
354 let mut cloned = e.clone();
355 cloned.top_level_statements = false;
362 fn check_stack_limit(&self) {
365 .map(|limit| limit.panic_if_exceeded());
369 impl<'a, TF> AsMut<Env<'a, TF>> for Env<'a, TF> {
370 fn as_mut(&mut self) -> &mut Env<'a, TF> {
381 kind: syntax_kind::SyntaxKind,
386 type Result<T> = std::result::Result<T, Error>;
387 type S<'arena, T, V> = &'arena Syntax<'arena, T, V>;
389 trait Lowerer<'a, T, V, TF>
391 TF: TokenMutator<Token = T> + Clone + 'a,
392 T: 'a + LexablePositionedToken + Copy,
393 V: 'a + SyntaxValueWithKind + SyntaxValueType<T>,
394 Syntax<'a, T, V>: SyntaxTrait,
396 fn p_pos(node: S<'a, T, V>, env: &Env<TF>) -> Pos {
397 node.position_exclusive(env.indexed_source_text)
398 .unwrap_or_else(|| env.mk_none_pos())
401 fn raise_parsing_error(node: S<'a, T, V>, env: &mut Env<'a, TF>, msg: &str) {
402 Self::raise_parsing_error_(Either::Left(node), env, msg)
405 fn raise_parsing_error_pos(pos: &Pos, env: &mut Env<'a, TF>, msg: &str) {
406 Self::raise_parsing_error_(Either::Right(pos), env, msg)
409 fn raise_parsing_error_(
410 node_or_pos: Either<S<'a, T, V>, &Pos>,
411 env: &mut Env<'a, TF>,
414 if env.should_surface_error() {
415 let pos = node_or_pos.either(|node| Self::p_pos(node, env), |pos| pos.clone());
416 env.lowpri_errors().push((pos, String::from(msg)))
417 } else if env.codegen() {
418 let pos = node_or_pos.either(
420 node.position_exclusive(env.indexed_source_text)
421 .unwrap_or_else(|| env.mk_none_pos())
425 env.lowpri_errors().push((pos, String::from(msg)))
429 fn raise_hh_error(env: &mut Env<'a, TF>, err: HHError) {
430 env.hh_errors().push(err);
433 fn raise_lint_error(env: &mut Env<'a, TF>, err: LintError) {
434 env.lint_errors().push(err);
437 fn failwith<N>(msg: impl Into<String>) -> Result<N> {
438 Err(Error::Failwith(msg.into()))
441 fn text(node: S<'a, T, V>, env: &Env<TF>) -> String {
442 String::from(node.text(env.source_text()))
445 fn text_str<'b>(node: S<'a, T, V>, env: &'b Env<TF>) -> &'b str {
446 node.text(env.source_text())
449 fn lowering_error(env: &mut Env<'a, TF>, pos: &Pos, text: &str, syntax_kind: &str) {
450 if env.is_typechecker()
451 && !env.disable_lowering_parsing_error
452 && env.lowpri_errors().is_empty()
454 Self::raise_parsing_error_pos(
457 &syntax_error::lowering_parsing_error(text, syntax_kind),
462 fn missing_syntax_<N>(
466 env: &mut Env<'a, TF>,
468 let pos = Self::p_pos(node, env);
469 let text = Self::text(node, env);
470 Self::lowering_error(env, &pos, &text, expecting);
471 if let Some(x) = fallback {
476 Err(Error::MissingSyntax {
477 expecting: String::from(expecting),
478 pos: Self::p_pos(node, env),
484 fn missing_syntax<N>(expecting: &str, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<N> {
485 Self::missing_syntax_(None, expecting, node, env)
488 fn is_num_octal_lit(s: &str) -> bool {
489 !s.chars().any(|c| c == '8' || c == '9')
492 fn mp_optional<F, R>(p: F, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<Option<R>>
494 F: FnOnce(S<'a, T, V>, &mut Env<'a, TF>) -> Result<R>,
496 match &node.children {
498 _ => p(node, env).map(Some),
502 fn pos_qualified_name(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Sid> {
503 if let QualifiedName(c) = &node.children {
504 if let SyntaxList(l) = &c.parts.children {
505 let p = Self::p_pos(node, env);
506 let mut s = String::with_capacity(node.width());
510 s += Self::text_str(&li.item, env);
511 s += Self::text_str(&li.separator, env);
513 _ => s += Self::text_str(&i, env),
516 return Ok(ast::Id(p, s));
519 Self::missing_syntax("qualified name", node, env)
522 fn pos_name(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Sid> {
523 Self::pos_name_(node, env, None)
526 fn lid_from_pos_name(pos: Pos, name: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Lid> {
527 let name = Self::pos_name(name, env)?;
528 Ok(ast::Lid::new(pos, name.1))
531 fn lid_from_name(name: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Lid> {
532 let name = Self::pos_name(name, env)?;
533 Ok(ast::Lid::new(name.0, name.1))
536 fn p_pstring(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Pstring> {
537 Self::p_pstring_(node, env, None)
542 env: &mut Env<'a, TF>,
543 drop_prefix: Option<char>,
544 ) -> Result<ast::Pstring> {
545 let ast::Id(p, id) = Self::pos_name_(node, env, drop_prefix)?;
549 fn drop_prefix(s: &str, prefix: char) -> &str {
550 if s.len() > 0 && s.chars().nth(0) == Some(prefix) {
559 env: &mut Env<'a, TF>,
560 drop_prefix: Option<char>,
561 ) -> Result<ast::Sid> {
562 match &node.children {
563 QualifiedName(_) => Self::pos_qualified_name(node, env),
564 SimpleTypeSpecifier(c) => Self::pos_name_(&c.specifier, env, drop_prefix),
566 let mut name = node.text(env.indexed_source_text.source_text());
567 if let Some(prefix) = drop_prefix {
568 name = Self::drop_prefix(name, prefix);
570 let p = Self::p_pos(node, env);
571 Ok(ast::Id(p, String::from(name)))
578 env: &mut Env<'a, TF>,
583 F: Fn(&str) -> std::result::Result<BString, InvalidString>,
585 if let Some('b') = content.chars().nth(0) {
586 content = content.get(1..).unwrap();
589 let len = content.len();
590 let no_quotes_result = extract_unquoted_string(content, 0, len);
591 match no_quotes_result {
593 let result = unescaper(&no_quotes);
597 Self::raise_parsing_error(
600 &format!("Malformed string literal <<{}>>", &no_quotes),
607 Self::raise_parsing_error(
610 &format!("Malformed string literal <<{}>>", &content),
617 fn unesc_dbl(s: &str) -> std::result::Result<BString, InvalidString> {
618 let unesc_s = unescape_double(s)?;
619 if unesc_s == B("''") || unesc_s == B("\"\"") {
620 Ok(BString::from(""))
626 // TODO: return Cow<[u8]>
627 fn unesc_xhp(s: &[u8]) -> Vec<u8> {
629 static ref WHITESPACE: Regex = Regex::new("[\x20\t\n\r\x0c]+").unwrap();
631 WHITESPACE.replace_all(s, &b" "[..]).into_owned()
634 fn unesc_xhp_attr(s: &[u8]) -> Vec<u8> {
635 // TODO: change unesc_dbl to &[u8] -> BString
636 let r = Self::get_quoted_content(s);
637 let r = unsafe { std::str::from_utf8_unchecked(r) };
638 Self::unesc_dbl(r).unwrap().into()
641 fn get_quoted_content(s: &[u8]) -> &[u8] {
643 static ref QUOTED: Regex = Regex::new(r#"^[\x20\t\n\r\x0c]*"((?:.|\n)*)""#).unwrap();
647 .map_or(None, |c| c.get(1))
648 .map_or(s, |m| m.as_bytes())
651 fn token_kind(node: S<'a, T, V>) -> Option<TK> {
652 match &node.children {
653 Token(t) => Some(t.kind()),
658 fn check_valid_reified_hint(env: &mut Env<'a, TF>, node: S<'a, T, V>, hint: &ast::Hint) {
659 struct Checker<F: FnMut(&String)>(F);
660 impl<'ast, F: FnMut(&String)> Visitor<'ast> for Checker<F> {
661 type P = AstParams<(), ()>;
663 fn object(&mut self) -> &mut dyn Visitor<'ast, P = Self::P> {
667 fn visit_hint(&mut self, c: &mut (), h: &ast::Hint) -> std::result::Result<(), ()> {
669 ast::Hint_::Happly(id, _) => {
672 ast::Hint_::Haccess(_, ids) => {
673 ids.iter().for_each(|id| self.0(&id.1));
681 if *env.in_static_method() {
682 let f = |id: &String| {
683 Self::fail_if_invalid_reified_generic(node, env, id);
685 let mut visitor = Checker(f);
686 visitor.visit_hint(&mut (), hint).unwrap();
690 fn p_closure_parameter(
692 env: &mut Env<'a, TF>,
693 ) -> Result<(ast::Hint, Option<ast::ParamKind>)> {
694 match &node.children {
695 ClosureParameterTypeSpecifier(c) => {
696 let kind = Self::mp_optional(Self::p_param_kind, &c.call_convention, env)?;
697 let hint = Self::p_hint(&c.type_, env)?;
700 _ => Self::missing_syntax("closure parameter", node, env),
704 fn mp_shape_expression_field<F, R>(
707 env: &mut Env<'a, TF>,
708 ) -> Result<(ast::ShapeFieldName, R)>
710 F: Fn(S<'a, T, V>, &mut Env<'a, TF>) -> Result<R>,
712 match &node.children {
713 FieldInitializer(c) => {
714 let name = Self::p_shape_field_name(&c.name, env)?;
715 let value = f(&c.value, env)?;
718 _ => Self::missing_syntax("shape field", node, env),
722 fn p_shape_field_name(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::ShapeFieldName> {
723 use ast::ShapeFieldName::*;
724 let is_valid_shape_literal = |t: &T| {
725 let is_str = t.kind() == TK::SingleQuotedStringLiteral
726 || t.kind() == TK::DoubleQuotedStringLiteral;
727 let text = t.text(env.source_text());
728 let is_empty = text == "\'\'" || text == "\"\"";
731 if let LiteralExpression(c) = &node.children {
732 if let Token(t) = &c.expression.children {
733 if is_valid_shape_literal(t) {
734 let ast::Id(p, n) = Self::pos_name(node, env)?;
735 let unescp = if t.kind() == TK::SingleQuotedStringLiteral {
740 let str_ = Self::mk_str(node, env, unescp, &n);
741 if let Some(_) = ocaml_helper::int_of_string_opt(&str_.as_bytes()) {
742 Self::raise_parsing_error(
745 &syntax_error::shape_field_int_like_string,
748 return Ok(SFlitStr((p, str_)));
752 match &node.children {
753 ScopeResolutionExpression(c) => Ok(SFclassConst(
754 Self::pos_name(&c.qualifier, env)?,
755 Self::p_pstring(&c.name, env)?,
758 Self::raise_parsing_error(node, env, &syntax_error::invalid_shape_field_name);
759 let ast::Id(p, n) = Self::pos_name(node, env)?;
760 Ok(SFlitStr((p, Self::mk_str(node, env, Self::unesc_dbl, &n))))
765 fn p_shape_field(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::ShapeFieldInfo> {
766 match &node.children {
767 FieldSpecifier(c) => {
768 let optional = !c.question.is_missing();
769 let name = Self::p_shape_field_name(&c.name, env)?;
770 let hint = Self::p_hint(&c.type_, env)?;
771 Ok(ast::ShapeFieldInfo {
778 let (name, hint) = Self::mp_shape_expression_field(Self::p_hint, node, env)?;
779 Ok(ast::ShapeFieldInfo {
788 fn p_targ(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Targ> {
789 Ok(ast::Targ((), Self::p_hint(node, env)?))
792 fn p_hint_(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Hint_> {
794 let unary = |kw, ty, env: &mut Env<'a, TF>| {
796 Self::pos_name(kw, env)?,
797 Self::could_map(Self::p_hint, ty, env)?,
800 let binary = |kw, key, ty, env: &mut Env<'a, TF>| {
801 let kw = Self::pos_name(kw, env)?;
802 let key = Self::p_hint(key, env)?;
805 Self::map_flatten_(&Self::p_hint, ty, env, vec![key])?,
809 match &node.children {
810 Token(token) if token.kind() == TK::Variable => {
811 let ast::Id(_pos, name) = Self::pos_name(node, env)?;
814 /* Dirty hack; CastExpression can have type represented by token */
815 Token(_) | SimpleTypeSpecifier(_) | QualifiedName(_) => {
816 let ast::Id(pos, name) = Self::pos_name(node, env)?;
817 let mut suggest = |name: &str, canonical: &str| {
818 Self::raise_parsing_error(
821 &syntax_error::invalid_typehint_alias(name, canonical),
824 if "integer".eq_ignore_ascii_case(&name) {
825 suggest(&name, special_typehints::INT);
826 } else if "boolean".eq_ignore_ascii_case(&name) {
827 suggest(&name, special_typehints::BOOL);
828 } else if "double".eq_ignore_ascii_case(&name) {
829 suggest(&name, special_typehints::FLOAT);
830 } else if "real".eq_ignore_ascii_case(&name) {
831 suggest(&name, special_typehints::FLOAT);
833 Ok(Happly(ast::Id(pos, name), vec![]))
835 ShapeTypeSpecifier(c) => {
836 let allows_unknown_fields = !c.ellipsis.is_missing();
837 /* if last element lacks a separator and ellipsis is present, error */
838 if allows_unknown_fields {
839 if let SyntaxList(items) = &c.fields.children {
840 if let Some(ListItem(item)) = items.last().map(|i| &i.children) {
841 if item.separator.is_missing() {
842 Self::raise_parsing_error(
845 &syntax_error::shape_type_ellipsis_without_trailing_comma,
852 let field_map = Self::could_map(Self::p_shape_field, &c.fields, env)?;
853 // TODO:(shiqicao) improve perf
854 // 1. replace HashSet by fnv hash map or something faster,
855 // 2. move `set` to Env to avoid allocation,
856 let mut set: HashSet<&BStr> = HashSet::with_capacity(field_map.len());
857 for f in field_map.iter() {
858 if !set.insert(f.name.get_name()) {
859 Self::raise_hh_error(
861 Naming::fd_name_already_bound(f.name.get_pos().clone()),
866 Ok(Hshape(ast::NastShapeInfo {
867 allows_unknown_fields,
871 TupleTypeSpecifier(c) => Ok(Htuple(Self::could_map(Self::p_hint, &c.types, env)?)),
872 UnionTypeSpecifier(c) => Ok(Hunion(Self::could_map(&Self::p_hint, &c.types, env)?)),
873 IntersectionTypeSpecifier(c) => Ok(Hintersection(Self::could_map(
878 KeysetTypeSpecifier(c) => Ok(Happly(
879 Self::pos_name(&c.keyword, env)?,
880 Self::could_map(Self::p_hint, &c.type_, env)?,
882 VectorTypeSpecifier(c) => unary(&c.keyword, &c.type_, env),
883 ClassnameTypeSpecifier(c) => unary(&c.keyword, &c.type_, env),
884 TupleTypeExplicitSpecifier(c) => unary(&c.keyword, &c.types, env),
885 VarrayTypeSpecifier(c) => unary(&c.keyword, &c.type_, env),
886 DarrayTypeSpecifier(c) => binary(&c.keyword, &c.key, &c.value, env),
887 DictionaryTypeSpecifier(c) => unary(&c.keyword, &c.members, env),
888 GenericTypeSpecifier(c) => {
889 let name = Self::pos_name(&c.class_type, env)?;
890 let args = &c.argument_list;
891 let type_args = match &args.children {
892 TypeArguments(c) => Self::could_map(Self::p_hint, &c.types, env)?,
893 _ => Self::missing_syntax("generic type arguments", args, env)?,
896 let process = |name: ast::Sid, mut args: Vec<ast::Hint>| -> ast::Hint_ {
898 let eq = |s| name.1.eq_ignore_ascii_case(s);
899 if (args[0].1.is_hfun()
902 || eq(rx::RX_SHALLOW)
904 || (args[0].1.is_happly()
906 || eq(rx::MAYBE_MUTABLE)
907 || eq(rx::OWNED_MUTABLE)))
909 return *args.pop().unwrap().1;
914 Ok(process(name, type_args))
916 Ok(Happly(name, type_args))
919 NullableTypeSpecifier(c) => Ok(Hoption(Self::p_hint(&c.type_, env)?)),
920 LikeTypeSpecifier(c) => Ok(Hlike(Self::p_hint(&c.type_, env)?)),
921 SoftTypeSpecifier(c) => Ok(Hsoft(Self::p_hint(&c.type_, env)?)),
922 ClosureTypeSpecifier(c) => {
923 let (param_list, variadic_hints): (Vec<S<'a, T, V>>, Vec<S<'a, T, V>>) = c
925 .syntax_node_to_list_skip_separator()
926 .partition(|n| match &n.children {
927 VariadicParameter(_) => false,
930 let (type_hints, kinds) = param_list
932 .map(|p| Self::p_closure_parameter(p, env))
933 .collect::<std::result::Result<Vec<_>, _>>()?
936 let variadic_hints = variadic_hints
938 .map(|v| match &v.children {
939 VariadicParameter(c) => {
940 if c.type_.is_missing() {
941 Self::raise_parsing_error(
944 "Cannot use ... without a typehint",
947 Ok(Some(Self::p_hint(&c.type_, env)?))
949 _ => panic!("expect variadic parameter"),
951 .collect::<std::result::Result<Vec<_>, _>>()?;
952 if variadic_hints.len() > 1 {
953 return Self::failwith(format!(
954 "{} variadic parameters found. There should be no more than one.",
955 variadic_hints.len().to_string()
958 let (ctxs, _) = Self::p_contexts(&c.contexts, env)?;
959 Ok(Hfun(ast::HintFun {
960 reactive_kind: ast::FuncReactive::FNonreactive,
961 param_tys: type_hints,
963 param_mutability: vec![],
964 variadic_ty: variadic_hints.into_iter().next().unwrap_or(None),
966 return_ty: Self::p_hint(&c.return_type, env)?,
967 is_mutable_return: true,
970 AttributizedSpecifier(c) => {
971 let attrs = Self::p_user_attribute(&c.attribute_spec, env)?;
972 let hint = Self::p_hint(&c.type_, env)?;
973 if attrs.iter().any(|attr| attr.name.1 != special_attrs::SOFT) {
974 Self::raise_parsing_error(node, env, &syntax_error::only_soft_allowed);
976 Ok(*Self::soften_hint(&attrs, hint).1)
978 FunctionCtxTypeSpecifier(c) => {
979 let ast::Id(_p, n) = Self::pos_name(&c.variable, env)?;
983 let child = Self::pos_name(&c.right_type, env)?;
984 match Self::p_hint_(&c.left_type, env)? {
985 Haccess(root, mut cs) => {
987 Ok(Haccess(root, cs))
990 let pos = Self::p_pos(&c.left_type, env);
991 let root = ast::Hint::new(pos, Hvar(n));
992 Ok(Haccess(root, vec![child]))
994 Happly(ty, param) => {
995 if param.is_empty() {
996 let root = ast::Hint::new(ty.0.clone(), Happly(ty, param));
997 Ok(Haccess(root, vec![child]))
999 Self::missing_syntax("type constant base", node, env)
1002 _ => Self::missing_syntax("type constant base", node, env),
1005 ReifiedTypeArgument(_) => {
1006 Self::raise_parsing_error(node, env, &syntax_error::invalid_reified);
1007 Self::missing_syntax("refied type", node, env)
1009 _ => Self::missing_syntax("type hint", node, env),
1013 fn p_hint(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Hint> {
1014 let hint_ = Self::p_hint_(node, env)?;
1015 let pos = Self::p_pos(node, env);
1016 let hint = ast::Hint::new(pos, hint_);
1017 Self::check_valid_reified_hint(env, node, &hint);
1021 fn p_simple_initializer(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Expr> {
1022 match &node.children {
1023 SimpleInitializer(c) => Self::p_expr(&c.value, env),
1024 _ => Self::missing_syntax("simple initializer", node, env),
1028 fn p_member(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<(ast::Expr, ast::Expr)> {
1029 match &node.children {
1030 ElementInitializer(c) => Ok((Self::p_expr(&c.key, env)?, Self::p_expr(&c.value, env)?)),
1031 _ => Self::missing_syntax("darray intrinsic expression element", node, env),
1035 fn expand_type_args(ty: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<Vec<ast::Hint>> {
1036 match &ty.children {
1037 TypeArguments(c) => Self::could_map(Self::p_hint, &c.types, env),
1042 fn p_afield(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Afield> {
1043 match &node.children {
1044 ElementInitializer(c) => Ok(ast::Afield::AFkvalue(
1045 Self::p_expr(&c.key, env)?,
1046 Self::p_expr(&c.value, env)?,
1048 _ => Ok(ast::Afield::AFvalue(Self::p_expr(node, env)?)),
1052 fn check_intrinsic_type_arg_varity(
1054 env: &mut Env<'a, TF>,
1055 tys: Vec<ast::Hint>,
1056 ) -> Option<ast::CollectionTarg> {
1057 let count = tys.len();
1058 let mut tys = tys.into_iter();
1060 2 => Some(ast::CollectionTarg::CollectionTKV(
1061 ast::Targ((), tys.next().unwrap()),
1062 ast::Targ((), tys.next().unwrap()),
1064 1 => Some(ast::CollectionTarg::CollectionTV(ast::Targ(
1066 tys.next().unwrap(),
1070 Self::raise_parsing_error(
1073 &syntax_error::collection_intrinsic_many_typeargs,
1080 fn p_import_flavor(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::ImportFlavor> {
1081 use ast::ImportFlavor::*;
1082 match Self::token_kind(node) {
1083 Some(TK::Include) => Ok(Include),
1084 Some(TK::Require) => Ok(Require),
1085 Some(TK::Include_once) => Ok(IncludeOnce),
1086 Some(TK::Require_once) => Ok(RequireOnce),
1087 _ => Self::missing_syntax("import flavor", node, env),
1091 fn p_null_flavor(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::OgNullFlavor> {
1092 use ast::OgNullFlavor::*;
1093 match Self::token_kind(node) {
1094 Some(TK::QuestionMinusGreaterThan) => Ok(OGNullsafe),
1095 Some(TK::MinusGreaterThan) => Ok(OGNullthrows),
1096 _ => Self::missing_syntax("null flavor", node, env),
1100 fn wrap_unescaper<F>(unescaper: F, s: &str) -> Result<BString>
1102 F: FnOnce(&str) -> std::result::Result<BString, InvalidString>,
1104 unescaper(s).map_err(|e| Error::Failwith(e.msg))
1107 fn fail_if_invalid_class_creation(
1109 env: &mut Env<'a, TF>,
1110 id: impl AsRef<str>,
1112 let id = id.as_ref();
1113 let is_in_static_method = *env.in_static_method();
1114 if is_in_static_method
1115 && ((id == special_classes::SELF && !env.cls_reified_generics().is_empty())
1116 || (id == special_classes::PARENT && *env.parent_maybe_reified()))
1118 Self::raise_parsing_error(node, env, &syntax_error::static_method_reified_obj_creation);
1122 fn fail_if_invalid_reified_generic(
1124 env: &mut Env<'a, TF>,
1125 id: impl AsRef<str>,
1127 let is_in_static_method = *env.in_static_method();
1128 if is_in_static_method && env.cls_reified_generics().contains(id.as_ref()) {
1129 Self::raise_parsing_error(
1132 &syntax_error::cls_reified_generic_in_static_method,
1137 fn rfind(s: &[u8], mut i: usize, c: u8) -> Result<usize> {
1139 return Self::failwith("index out of range");
1148 Self::failwith("char not found")
1152 nodes: &'a [Syntax<'a, T, V>],
1153 env: &mut Env<'a, TF>,
1154 ) -> Result<(TokenOp, TokenOp)> {
1156 let is_qoute = |c| c == b'\"' || c == b'`';
1157 let start_is_qoute = |s: &[u8]| {
1158 (s.len() > 0 && is_qoute(s[0])) || (s.len() > 1 && (s[0] == b'b' && s[1] == b'\"'))
1160 let last_is_qoute = |s: &[u8]| s.len() > 0 && is_qoute(s[s.len() - 1]);
1161 let is_heredoc = |s: &[u8]| (s.len() > 3 && &s[0..3] == b"<<<");
1162 let mut nodes = nodes.iter();
1163 let first = nodes.next();
1164 match first.map(|n| &n.children) {
1167 Self::raise_parsing_error(first.unwrap(), env, "Malformed String2 SyntaxList");
1169 let text = t.text_raw(env.source_text());
1170 if start_is_qoute(text) {
1171 let first_token_op = match text[0] {
1172 b'b' if text.len() > 2 => LeftTrim(2),
1173 _ if is_qoute(text[0]) && text.len() > 1 => LeftTrim(1),
1176 if let Some(Token(t)) = nodes.last().map(|n| &n.children) {
1177 let last_text = t.text_raw(env.source_text());
1178 if last_is_qoute(last_text) {
1179 let last_taken_op = match last_text.len() {
1180 n if n > 1 => RightTrim(1),
1183 return Ok((first_token_op, last_taken_op));
1187 Ok((first_token_op, Noop))
1188 } else if is_heredoc(text) {
1189 let trim_size = text
1191 .position(|c| *c == b'\n')
1192 .ok_or_else(|| Error::Failwith(String::from("newline not found")))?
1194 let first_token_op = match trim_size {
1195 _ if trim_size == text.len() => Skip,
1196 _ => LeftTrim(trim_size),
1198 if let Some(Token(t)) = nodes.last().map(|n| &n.children) {
1199 let text = t.text_raw(env.source_text());
1200 let len = text.len();
1202 let n = Self::rfind(text, len - 2, b'\n')?;
1203 let last_token_op = match n {
1205 _ => RightTrim(len - n),
1207 return Ok((first_token_op, last_token_op));
1211 Ok((first_token_op, Noop))
1216 _ => Ok((Noop, Noop)),
1220 fn process_token_op(
1221 env: &mut Env<'a, TF>,
1224 ) -> Result<Option<S<'a, T, V>>> {
1227 LeftTrim(n) => match &node.children {
1229 let token = env.token_factory.trim_left(t, n);
1230 let node = env.arena.alloc(<Syntax<'a, T, V>>::make_token(token));
1233 _ => Self::failwith("Token expected"),
1235 RightTrim(n) => match &node.children {
1237 let token = env.token_factory.trim_right(t, n);
1238 let node = env.arena.alloc(<Syntax<'a, T, V>>::make_token(token));
1241 _ => Self::failwith("Token expected"),
1247 fn p_string2(nodes: &'a [Syntax<'a, T, V>], env: &mut Env<'a, TF>) -> Result<Vec<ast::Expr>> {
1249 let (head_op, tail_op) = Self::prep_string2(nodes, env)?;
1250 let mut result = Vec::with_capacity(nodes.len());
1252 let last = nodes.len() - 1;
1256 _ if i == last => tail_op,
1265 let node = Self::process_token_op(env, op, &nodes[i])?;
1266 let node = node.unwrap_or(&nodes[i]);
1268 if Self::token_kind(node) == Some(TK::Dollar) && i < last {
1269 if let EmbeddedBracedExpression(_) = &nodes[i + 1].children {
1270 Self::raise_parsing_error(
1273 &syntax_error::outside_dollar_str_interp,
1276 result.push(Self::p_expr_with_loc(
1277 ExprLocation::InDoubleQuotedString,
1286 result.push(Self::p_expr_with_loc(
1287 ExprLocation::InDoubleQuotedString,
1296 fn p_expr_l(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<Vec<ast::Expr>> {
1297 let p_expr = |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::Expr> {
1298 Self::p_expr_with_loc(ExprLocation::TopLevel, n, e)
1300 Self::could_map(p_expr, node, env)
1303 fn p_expr(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Expr> {
1304 Self::p_expr_with_loc(ExprLocation::TopLevel, node, env)
1308 location: ExprLocation,
1310 env: &mut Env<'a, TF>,
1311 ) -> Result<ast::Expr> {
1312 Self::p_expr_impl(location, node, env, None)
1316 location: ExprLocation,
1318 env: &mut Env<'a, TF>,
1319 parent_pos: Option<Pos>,
1320 ) -> Result<ast::Expr> {
1321 match &node.children {
1322 BracedExpression(c) => {
1323 // Either a dynamic method lookup on a dynamic value:
1324 // $foo->{$meth_name}();
1325 // or an XHP splice.
1326 // <p id={$id}>hello</p>;
1327 // In both cases, unwrap, consistent with parentheses.
1328 Self::p_expr_impl(location, &c.expression, env, parent_pos)
1330 ParenthesizedExpression(c) => {
1331 Self::p_expr_impl(location, &c.expression, env, parent_pos)
1333 ETSpliceExpression(c) => {
1334 let pos = Self::p_pos(node, env);
1336 let inner_pos = Self::p_pos(&c.expression, env);
1337 let inner_expr_ = Self::p_expr_impl_(location, &c.expression, env, parent_pos)?;
1338 let inner_expr = ast::Expr::new(inner_pos, inner_expr_);
1341 ast::Expr_::ETSplice(Box::new(inner_expr)),
1345 let pos = Self::p_pos(node, env);
1346 let expr_ = Self::p_expr_impl_(location, node, env, parent_pos)?;
1347 Ok(ast::Expr::new(pos, expr_))
1353 location: ExprLocation,
1354 parent: S<'a, T, V>,
1356 env: &mut Env<'a, TF>,
1357 ) -> Result<ast::Expr_> {
1358 match &expr.children {
1360 let s = expr.text(env.indexed_source_text.source_text());
1361 let check_lint_err = |e: &mut Env<'a, TF>, s: &str, expected: &str| {
1362 if !e.codegen() && s != expected {
1363 Self::raise_lint_error(
1365 LintError::lowercase_constant(Self::p_pos(expr, e), s),
1369 match (location, Self::token_kind(expr)) {
1370 (ExprLocation::InDoubleQuotedString, _) if env.codegen() => {
1371 Ok(E_::String(Self::mk_str(expr, env, Self::unesc_dbl, s)))
1373 (_, Some(TK::OctalLiteral))
1374 if env.is_typechecker() && !Self::is_num_octal_lit(s) =>
1376 Self::raise_parsing_error(
1379 &syntax_error::invalid_octal_integer,
1381 Self::missing_syntax("octal", expr, env)
1383 (_, Some(TK::DecimalLiteral))
1384 | (_, Some(TK::OctalLiteral))
1385 | (_, Some(TK::HexadecimalLiteral))
1386 | (_, Some(TK::BinaryLiteral)) => Ok(E_::Int(s.replace("_", ""))),
1387 (_, Some(TK::FloatingLiteral)) => Ok(E_::Float(String::from(s))),
1388 (_, Some(TK::SingleQuotedStringLiteral)) => {
1389 Ok(E_::String(Self::mk_str(expr, env, unescape_single, s)))
1391 (_, Some(TK::DoubleQuotedStringLiteral)) => {
1392 Ok(E_::String(Self::mk_str(expr, env, unescape_double, s)))
1394 (_, Some(TK::HeredocStringLiteral)) => {
1395 Ok(E_::String(Self::mk_str(expr, env, unescape_heredoc, s)))
1397 (_, Some(TK::NowdocStringLiteral)) => {
1398 Ok(E_::String(Self::mk_str(expr, env, unescape_nowdoc, s)))
1400 (_, Some(TK::NullLiteral)) => {
1401 check_lint_err(env, s, literal::NULL);
1404 (_, Some(TK::BooleanLiteral)) => {
1405 if s.eq_ignore_ascii_case(literal::FALSE) {
1406 check_lint_err(env, s, literal::FALSE);
1408 } else if s.eq_ignore_ascii_case(literal::TRUE) {
1409 check_lint_err(env, s, literal::TRUE);
1412 Self::missing_syntax(&format!("boolean (not: {})", s), expr, env)
1415 _ => Self::missing_syntax("literal", expr, env),
1418 SyntaxList(ts) => Ok(E_::String2(Self::p_string2(ts, env)?)),
1419 _ => Self::missing_syntax("literal expressoin", expr, env),
1423 fn p_expr_with_loc_(
1424 location: ExprLocation,
1426 env: &mut Env<'a, TF>,
1427 ) -> Result<ast::Expr_> {
1428 Self::p_expr_impl_(location, node, env, None)
1432 location: ExprLocation,
1434 env: &mut Env<'a, TF>,
1435 parent_pos: Option<Pos>,
1436 ) -> Result<ast::Expr_> {
1437 if *env.exp_recursion_depth() >= EXP_RECUSION_LIMIT {
1438 Err(Error::Failwith("Expression recursion limit reached".into()))
1440 *env.exp_recursion_depth() += 1;
1441 let r = Self::p_expr_impl__(location, node, env, parent_pos);
1442 *env.exp_recursion_depth() -= 1;
1448 location: ExprLocation,
1450 env: &mut Env<'a, TF>,
1451 parent_pos: Option<Pos>,
1452 ) -> Result<ast::Expr_> {
1453 env.check_stack_limit();
1455 let split_args_vararg = |
1456 arg_list_node: S<'a, T, V>,
1457 e: &mut Env<'a, TF>,
1458 | -> Result<(Vec<ast::Expr>, Option<ast::Expr>)> {
1459 let mut arg_list: Vec<_> = arg_list_node.syntax_node_to_list_skip_separator().collect();
1460 if let Some(last_arg) = arg_list.last() {
1461 if let DecoratedExpression(c) = &last_arg.children {
1462 if Self::token_kind(&c.decorator) == Some(TK::DotDotDot) {
1463 let _ = arg_list.pop();
1464 let args: std::result::Result<Vec<_>, _> =
1465 arg_list.iter().map(|a| Self::p_expr(a, e)).collect();
1467 let vararg = Self::p_expr(&c.expression, e)?;
1468 return Ok((args, Some(vararg)));
1472 Ok((Self::could_map(Self::p_expr, arg_list_node, e)?, None))
1474 let mk_lid = |p: Pos, s: String| ast::Lid(p, (0, s));
1475 let mk_name_lid = |name: S<'a, T, V>, env: &mut Env<'a, TF>| {
1476 let name = Self::pos_name(name, env)?;
1477 Ok(mk_lid(name.0, name.1))
1480 |name: S<'a, T, V>, env: &mut Env<'a, TF>| Ok(E_::mk_lvar(mk_name_lid(name, env)?));
1481 let mk_id_expr = |name: ast::Sid| E::new(name.0.clone(), E_::mk_id(name));
1482 let p_intri_expr = |kw, ty, v, e: &mut Env<'a, TF>| {
1483 let hints = Self::expand_type_args(ty, e)?;
1484 let hints = Self::check_intrinsic_type_arg_varity(node, e, hints);
1485 Ok(E_::mk_collection(
1486 Self::pos_name(kw, e)?,
1488 Self::could_map(Self::p_afield, v, e)?,
1491 let p_special_call =
1492 |recv: S<'a, T, V>, args: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::Expr_> {
1493 let pos_if_has_parens = match &recv.children {
1494 ParenthesizedExpression(_) => Some(Self::p_pos(recv, e)),
1497 let recv = Self::p_expr(recv, e)?;
1498 let recv = match (&recv.1, pos_if_has_parens) {
1499 (E_::ObjGet(t), Some(ref _p)) => {
1500 let (a, b, c, _false) = &**t;
1503 E_::mk_obj_get(a.clone(), b.clone(), c.clone(), true),
1506 (E_::ClassGet(c), Some(ref _p)) => {
1507 let (a, b, _false) = &**c;
1508 E::new(recv.0.clone(), E_::mk_class_get(a.clone(), b.clone(), true))
1512 let (args, varargs) = split_args_vararg(args, e)?;
1513 Ok(E_::mk_call(recv, vec![], args, varargs))
1519 e: &mut Env<'a, TF>,
1520 | -> Result<ast::Expr_> {
1521 if recv.is_object_creation_expression() && !e.codegen() {
1522 Self::raise_parsing_error(recv, e, &syntax_error::invalid_constructor_method_call);
1524 let recv = Self::p_expr(recv, e)?;
1525 let name = Self::p_expr_with_loc(ExprLocation::MemberSelect, name, e)?;
1526 let op = Self::p_null_flavor(op, e)?;
1527 Ok(E_::mk_obj_get(recv, name, op, false))
1529 let pos = match parent_pos {
1530 None => Self::p_pos(node, env),
1533 match &node.children {
1534 LambdaExpression(c) => {
1535 let suspension_kind = Self::mk_suspension_kind(&c.async_);
1536 let (params, (ctxs, unsafe_ctxs), ret) = match &c.signature.children {
1537 LambdaSignature(c) => (
1538 Self::could_map(Self::p_fun_param, &c.parameters, env)?,
1539 Self::p_contexts(&c.contexts, env)?,
1540 Self::mp_optional(Self::p_hint, &c.type_, env)?,
1543 let ast::Id(p, n) = Self::pos_name(&c.signature, env)?;
1545 vec![ast::FunParam {
1546 annotation: p.clone(),
1547 type_hint: ast::TypeHint((), None),
1553 user_attributes: vec![],
1560 _ => Self::missing_syntax("lambda signature", &c.signature, env)?,
1562 let (body, yield_) = if !c.body.is_compound_statement() {
1563 Self::mp_yielding(Self::p_function_body, &c.body, env)?
1565 let mut env1 = Env::clone_and_unset_toplevel_if_toplevel(env);
1566 Self::mp_yielding(&Self::p_function_body, &c.body, env1.as_mut())?
1568 let external = c.body.is_external();
1569 let fun = ast::Fun_ {
1572 mode: env.file_mode(),
1573 ret: ast::TypeHint((), ret),
1574 name: ast::Id(pos, String::from(";anonymous")),
1576 where_constraints: vec![],
1577 body: ast::FuncBody {
1581 fun_kind: Self::mk_fun_kind(suspension_kind, yield_),
1582 variadic: Self::determine_variadicity(¶ms),
1586 user_attributes: Self::p_user_attributes(&c.attribute_spec, env)?,
1587 file_attributes: vec![],
1589 namespace: Self::mk_empty_ns_env(env),
1593 Ok(E_::mk_lfun(fun, vec![]))
1595 BracedExpression(c) => Self::p_expr_with_loc_(location, &c.expression, env),
1596 EmbeddedBracedExpression(c) => {
1597 Self::p_expr_impl_(location, &c.expression, env, Some(pos))
1599 ParenthesizedExpression(c) => Self::p_expr_with_loc_(location, &c.expression, env),
1600 DictionaryIntrinsicExpression(c) => {
1601 p_intri_expr(&c.keyword, &c.explicit_type, &c.members, env)
1603 KeysetIntrinsicExpression(c) => {
1604 p_intri_expr(&c.keyword, &c.explicit_type, &c.members, env)
1606 VectorIntrinsicExpression(c) => {
1607 p_intri_expr(&c.keyword, &c.explicit_type, &c.members, env)
1609 CollectionLiteralExpression(c) => {
1610 let (collection_name, hints) = match &c.name.children {
1611 SimpleTypeSpecifier(c) => (Self::pos_name(&c.specifier, env)?, None),
1612 GenericTypeSpecifier(c) => {
1613 let hints = Self::expand_type_args(&c.argument_list, env)?;
1614 let hints = Self::check_intrinsic_type_arg_varity(node, env, hints);
1615 (Self::pos_name(&c.class_type, env)?, hints)
1617 _ => (Self::pos_name(&c.name, env)?, None),
1619 Ok(E_::mk_collection(
1622 Self::could_map(Self::p_afield, &c.initializers, env)?,
1625 VarrayIntrinsicExpression(c) => {
1626 let hints = Self::expand_type_args(&c.explicit_type, env)?;
1627 let hints = Self::check_intrinsic_type_arg_varity(node, env, hints);
1628 let targ = match hints {
1629 Some(ast::CollectionTarg::CollectionTV(ty)) => Some(ty),
1631 _ => Self::missing_syntax("VarrayIntrinsicExpression type args", node, env)?,
1635 Self::could_map(Self::p_expr, &c.members, env)?,
1638 DarrayIntrinsicExpression(c) => {
1639 let hints = Self::expand_type_args(&c.explicit_type, env)?;
1640 let hints = Self::check_intrinsic_type_arg_varity(node, env, hints);
1642 Some(ast::CollectionTarg::CollectionTKV(tk, tv)) => Ok(E_::mk_darray(
1644 Self::could_map(Self::p_member, &c.members, env)?,
1646 None => Ok(E_::mk_darray(
1648 Self::could_map(Self::p_member, &c.members, env)?,
1650 _ => Self::missing_syntax("DarrayIntrinsicExpression type args", node, env),
1653 ListExpression(c) => {
1654 /* TODO: Or tie in with other intrinsics and post-process to List */
1655 let p_binder_or_ignore =
1656 |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::Expr> {
1658 Missing => Ok(E::new(e.mk_none_pos(), E_::Omitted)),
1659 _ => Self::p_expr(n, e),
1662 Ok(E_::List(Self::could_map(
1663 &p_binder_or_ignore,
1668 EvalExpression(c) => p_special_call(&c.keyword, &c.argument, env),
1669 IssetExpression(c) => p_special_call(&c.keyword, &c.argument_list, env),
1670 TupleExpression(c) => p_special_call(&c.keyword, &c.items, env),
1671 FunctionCallExpression(c) => {
1672 let recv = &c.receiver;
1673 let args = &c.argument_list;
1674 let get_hhas_adata = || {
1675 if Self::text_str(recv, env) == "__hhas_adata" {
1676 if let SyntaxList(l) = &args.children {
1677 if let Some(li) = l.first() {
1678 if let ListItem(i) = &li.children {
1679 if let LiteralExpression(le) = &i.item.children {
1680 let expr = &le.expression;
1681 if Self::token_kind(expr) == Some(TK::NowdocStringLiteral) {
1691 match get_hhas_adata() {
1693 let literal_expression_pos = Self::p_pos(expr, env);
1694 let s = extract_unquoted_string(Self::text_str(expr, env), 0, expr.width())
1695 .map_err(|e| Error::Failwith(e.msg))?;
1697 Self::p_expr(recv, env)?,
1699 vec![E::new(literal_expression_pos, E_::String(s.into()))],
1704 let targs = match (&recv.children, &c.type_args.children) {
1705 (_, TypeArguments(c)) => Self::could_map(Self::p_targ, &c.types, env)?,
1706 /* TODO might not be needed */
1707 (GenericTypeSpecifier(c), _) => match &c.argument_list.children {
1708 TypeArguments(c) => Self::could_map(Self::p_targ, &c.types, env)?,
1714 /* preserve parens on receiver of call expression
1715 to allow distinguishing between
1716 ($a->b)() // invoke on callable property
1717 $a->b() // method call */
1718 let pos_if_has_parens = match &recv.children {
1719 ParenthesizedExpression(_) => Some(Self::p_pos(recv, env)),
1722 let recv = Self::p_expr(recv, env)?;
1723 let recv = match (&recv.1, pos_if_has_parens) {
1724 (E_::ObjGet(t), Some(ref _p)) => {
1725 let (a, b, c, _false) = &**t;
1728 E_::mk_obj_get(a.clone(), b.clone(), c.clone(), true),
1731 (E_::ClassGet(c), Some(ref _p)) => {
1732 let (a, b, _false) = &**c;
1733 E::new(recv.0.clone(), E_::mk_class_get(a.clone(), b.clone(), true))
1737 let (args, varargs) = split_args_vararg(args, env)?;
1738 Ok(E_::mk_call(recv, targs, args, varargs))
1742 FunctionPointerExpression(c) => {
1743 let targs = match &c.type_args.children {
1744 TypeArguments(c) => Self::could_map(Self::p_targ, &c.types, env)?,
1748 let recv = Self::p_expr(&c.receiver, env)?;
1751 aast::Expr_::Id(id) => Ok(E_::mk_function_pointer(
1752 aast::FunctionPtrId::FPId(*(id.to_owned())),
1755 aast::Expr_::ClassConst(c) => {
1756 if let aast::ClassId_::CIexpr(aast::Expr(_, aast::Expr_::Id(_))) = (c.0).1 {
1757 Ok(E_::mk_function_pointer(
1758 aast::FunctionPtrId::FPClassConst(c.0.to_owned(), c.1.to_owned()),
1762 Self::raise_parsing_error(
1765 &syntax_error::function_pointer_bad_recv,
1767 Self::missing_syntax("function or static method", node, env)
1771 Self::raise_parsing_error(
1774 &syntax_error::function_pointer_bad_recv,
1776 Self::missing_syntax("function or static method", node, env)
1780 QualifiedName(_) => match location {
1781 ExprLocation::InDoubleQuotedString => {
1782 let ast::Id(_, n) = Self::pos_qualified_name(node, env)?;
1783 Ok(E_::String(n.into()))
1785 _ => Ok(E_::mk_id(Self::pos_qualified_name(node, env)?)),
1787 VariableExpression(c) => Ok(E_::mk_lvar(Self::lid_from_pos_name(
1792 PipeVariableExpression(_) => Ok(E_::mk_lvar(mk_lid(
1794 special_idents::DOLLAR_DOLLAR.into(),
1796 InclusionExpression(c) => Ok(E_::mk_import(
1797 Self::p_import_flavor(&c.require, env)?,
1798 Self::p_expr(&c.filename, env)?,
1800 MemberSelectionExpression(c) => p_obj_get(&c.object, &c.operator, &c.name, env),
1801 SafeMemberSelectionExpression(c) => p_obj_get(&c.object, &c.operator, &c.name, env),
1802 EmbeddedMemberSelectionExpression(c) => p_obj_get(&c.object, &c.operator, &c.name, env),
1803 PrefixUnaryExpression(_) | PostfixUnaryExpression(_) | DecoratedExpression(_) => {
1804 let (operand, op, postfix) = match &node.children {
1805 PrefixUnaryExpression(c) => (&c.operand, &c.operator, false),
1806 PostfixUnaryExpression(c) => (&c.operand, &c.operator, true),
1807 DecoratedExpression(c) => (&c.expression, &c.decorator, false),
1808 _ => Self::missing_syntax("unary exppr", node, env)?,
1812 * FFP does not destinguish between ++$i and $i++ on the level of token
1813 * kind annotation. Prevent duplication by switching on `postfix` for
1814 * the two operatores for which AST /does/ differentiate between
1818 let mk_unop = |op, e| Ok(E_::mk_unop(op, e));
1819 let op_kind = Self::token_kind(op);
1820 if let Some(TK::At) = op_kind {
1821 if env.parser_options.po_disallow_silence {
1822 Self::raise_parsing_error(op, env, &syntax_error::no_silence);
1825 let expr = Self::p_expr(operand, env)?;
1826 mk_unop(Usilence, expr)
1829 Self::p_expr_impl(ExprLocation::TopLevel, operand, env, Some(pos))?;
1833 let expr = Self::p_expr(operand, env)?;
1835 Some(TK::PlusPlus) if postfix => mk_unop(Upincr, expr),
1836 Some(TK::MinusMinus) if postfix => mk_unop(Updecr, expr),
1837 Some(TK::PlusPlus) => mk_unop(Uincr, expr),
1838 Some(TK::MinusMinus) => mk_unop(Udecr, expr),
1839 Some(TK::Exclamation) => mk_unop(Unot, expr),
1840 Some(TK::Tilde) => mk_unop(Utild, expr),
1841 Some(TK::Plus) => mk_unop(Uplus, expr),
1842 Some(TK::Minus) => mk_unop(Uminus, expr),
1843 Some(TK::Inout) => Ok(E_::mk_callconv(ast::ParamKind::Pinout, expr)),
1844 Some(TK::Await) => Self::lift_await(pos, expr, env, location),
1845 Some(TK::Clone) => Ok(E_::mk_clone(expr)),
1846 Some(TK::Print) => Ok(E_::mk_call(
1849 E_::mk_id(ast::Id(pos, special_functions::ECHO.into())),
1855 Some(TK::Dollar) => {
1856 Self::raise_parsing_error(
1859 &syntax_error::invalid_variable_name,
1863 _ => Self::missing_syntax("unary operator", node, env),
1867 BinaryExpression(c) => {
1868 use ExprLocation::*;
1869 let rlocation = if Self::token_kind(&c.operator) == Some(TK::Equal) {
1871 AsStatement => RightOfAssignment,
1872 UsingStatement => RightOfAssignmentInUsingStatement,
1878 let bop_ast_node = Self::p_bop(
1881 Self::p_expr(&c.left_operand, env)?,
1882 Self::p_expr_with_loc(rlocation, &c.right_operand, env)?,
1885 if let Some((ast::Bop::Eq(_), lhs, _)) = bop_ast_node.as_binop() {
1886 Self::check_lvalue(lhs, env);
1891 use ExprLocation::*;
1892 match (location, t.kind()) {
1893 (MemberSelect, TK::Variable) => mk_lvar(node, env),
1894 (InDoubleQuotedString, TK::HeredocStringLiteral)
1895 | (InDoubleQuotedString, TK::HeredocStringLiteralHead)
1896 | (InDoubleQuotedString, TK::HeredocStringLiteralTail) => Ok(E_::String(
1897 Self::wrap_unescaper(unescape_heredoc, Self::text_str(node, env))?,
1899 (InDoubleQuotedString, _) => Ok(E_::String(Self::wrap_unescaper(
1901 Self::text_str(node, env),
1906 | (UsingStatement, _)
1907 | (RightOfAssignment, _)
1908 | (RightOfAssignmentInUsingStatement, _)
1909 | (RightOfReturn, _) => Ok(E_::mk_id(Self::pos_name(node, env)?)),
1912 YieldExpression(c) => {
1913 use ExprLocation::*;
1914 env.saw_yield = true;
1915 if location != AsStatement
1916 && location != RightOfAssignment
1917 && location != RightOfAssignmentInUsingStatement
1919 Self::raise_parsing_error(node, env, &syntax_error::invalid_yield);
1921 if Self::text_str(&c.operand, env) == "break" {
1923 } else if c.operand.is_missing() {
1924 Ok(E_::mk_yield(ast::Afield::AFvalue(E::new(pos, E_::Null))))
1926 Ok(E_::mk_yield(Self::p_afield(&c.operand, env)?))
1929 DefineExpression(c) => {
1930 let name = Self::pos_name(&c.keyword, env)?;
1935 .syntax_node_to_list_skip_separator()
1936 .map(|x| Self::p_expr(x, env))
1937 .collect::<std::result::Result<Vec<_>, _>>()?,
1941 ScopeResolutionExpression(c) => {
1942 let qual = Self::p_expr(&c.qualifier, env)?;
1943 if let E_::Id(id) = &qual.1 {
1944 Self::fail_if_invalid_reified_generic(node, env, &id.1);
1946 match &c.name.children {
1947 Token(token) if token.kind() == TK::Variable => {
1948 let ast::Id(p, name) = Self::pos_name(&c.name, env)?;
1949 Ok(E_::mk_class_get(
1950 ast::ClassId(pos, ast::ClassId_::CIexpr(qual)),
1951 ast::ClassGetExpr::CGstring((p, name)),
1956 let E(p, expr_) = Self::p_expr(&c.name, env)?;
1958 E_::String(id) => Ok(E_::mk_class_const(
1959 ast::ClassId(pos, ast::ClassId_::CIexpr(qual)),
1962 String::from_utf8(id.into())
1963 .map_err(|e| Error::Failwith(e.to_string()))?,
1967 let ast::Id(p, n) = *id;
1968 Ok(E_::mk_class_const(
1969 ast::ClassId(pos, ast::ClassId_::CIexpr(qual)),
1974 let ast::Lid(p, (_, n)) = *id;
1975 Ok(E_::mk_class_get(
1976 ast::ClassId(pos, ast::ClassId_::CIexpr(qual)),
1977 ast::ClassGetExpr::CGstring((p, n)),
1981 _ => Ok(E_::mk_class_get(
1982 ast::ClassId(pos, ast::ClassId_::CIexpr(qual)),
1983 ast::ClassGetExpr::CGexpr(E(p, expr_)),
1990 CastExpression(c) => Ok(E_::mk_cast(
1991 Self::p_hint(&c.type_, env)?,
1992 Self::p_expr(&c.operand, env)?,
1994 PrefixedCodeExpression(c) => {
1995 let src_expr = Self::p_expr(&c.expression, env)?;
1996 let hint = Self::p_hint(&c.prefix, env)?;
1997 let desugared_expr = desugar(&hint, &src_expr, env);
1998 Ok(E_::mk_expression_tree(ast::ExpressionTree {
2004 ConditionalExpression(c) => {
2005 let alter = Self::p_expr(&c.alternative, env)?;
2006 let consequence = Self::mp_optional(Self::p_expr, &c.consequence, env)?;
2007 let condition = Self::p_expr(&c.test, env)?;
2008 Ok(E_::mk_eif(condition, consequence, alter))
2010 SubscriptExpression(c) => Ok(E_::mk_array_get(
2011 Self::p_expr(&c.receiver, env)?,
2012 Self::mp_optional(Self::p_expr, &c.index, env)?,
2014 EmbeddedSubscriptExpression(c) => Ok(E_::mk_array_get(
2015 Self::p_expr(&c.receiver, env)?,
2016 Self::mp_optional(|n, e| Self::p_expr_with_loc(location, n, e), &c.index, env)?,
2018 ShapeExpression(c) => Ok(E_::Shape(Self::could_map(
2019 |n: S<'a, T, V>, e: &mut Env<'a, TF>| {
2020 Self::mp_shape_expression_field(&Self::p_expr, n, e)
2025 ObjectCreationExpression(c) => Self::p_expr_impl_(location, &c.object, env, Some(pos)),
2026 ConstructorCall(c) => {
2027 let (args, varargs) = split_args_vararg(&c.argument_list, env)?;
2028 let (e, hl) = match &c.type_.children {
2029 GenericTypeSpecifier(c) => {
2030 let name = Self::pos_name(&c.class_type, env)?;
2031 let hints = match &c.argument_list.children {
2032 TypeArguments(c) => Self::could_map(Self::p_targ, &c.types, env)?,
2033 _ => Self::missing_syntax(
2034 "generic type arguments",
2039 (mk_id_expr(name), hints)
2041 SimpleTypeSpecifier(_) => {
2042 let name = Self::pos_name(&c.type_, env)?;
2043 (mk_id_expr(name), vec![])
2045 _ => (Self::p_expr(&c.type_, env)?, vec![]),
2047 if let E_::Id(name) = &e.1 {
2048 Self::fail_if_invalid_reified_generic(node, env, &name.1);
2049 Self::fail_if_invalid_class_creation(node, env, &name.1);
2052 ast::ClassId(pos.clone(), ast::ClassId_::CIexpr(e)),
2059 GenericTypeSpecifier(c) => {
2060 if !c.argument_list.is_missing() {
2061 Self::raise_parsing_error(
2064 &syntax_error::targs_not_allowed,
2067 Ok(E_::mk_id(Self::pos_name(&c.class_type, env)?))
2069 RecordCreationExpression(c) => {
2070 let id = Self::pos_name(&c.type_, env)?;
2073 Self::could_map(Self::p_member, &c.members, env)?,
2076 LiteralExpression(c) => Self::p_expr_lit(location, node, &c.expression, env),
2077 PrefixedStringExpression(c) => {
2078 /* Temporarily allow only`re`- prefixed strings */
2079 let name_text = Self::text(&c.name, env);
2080 if name_text != "re" {
2081 Self::raise_parsing_error(node, env, &syntax_error::non_re_prefix);
2083 Ok(E_::mk_prefixed_string(
2085 Self::p_expr(&c.str, env)?,
2088 IsExpression(c) => Ok(E_::mk_is(
2089 Self::p_expr(&c.left_operand, env)?,
2090 Self::p_hint(&c.right_operand, env)?,
2092 AsExpression(c) => Ok(E_::mk_as(
2093 Self::p_expr(&c.left_operand, env)?,
2094 Self::p_hint(&c.right_operand, env)?,
2097 NullableAsExpression(c) => Ok(E_::mk_as(
2098 Self::p_expr(&c.left_operand, env)?,
2099 Self::p_hint(&c.right_operand, env)?,
2102 AnonymousFunction(c) => {
2103 if env.parser_options.po_disable_static_closures
2104 && Self::token_kind(&c.static_keyword) == Some(TK::Static)
2106 Self::raise_parsing_error(
2109 &syntax_error::static_closures_are_disabled,
2112 let p_arg = |n: S<'a, T, V>, e: &mut Env<'a, TF>| match &n.children {
2113 Token(_) => mk_name_lid(n, e),
2114 _ => Self::missing_syntax("use variable", n, e),
2116 let (ctxs, unsafe_ctxs) = Self::p_contexts(&c.ctx_list, env)?;
2117 let p_use = |n: S<'a, T, V>, e: &mut Env<'a, TF>| match &n.children {
2118 AnonymousFunctionUseClause(c) => Self::could_map(p_arg, &c.variables, e),
2121 let suspension_kind = Self::mk_suspension_kind(&c.async_keyword);
2122 let (body, yield_) = {
2123 let mut env1 = Env::clone_and_unset_toplevel_if_toplevel(env);
2124 Self::mp_yielding(&Self::p_function_body, &c.body, env1.as_mut())?
2127 Self::extract_docblock(node, env).or_else(|| env.top_docblock().clone());
2128 let user_attributes = Self::p_user_attributes(&c.attribute_spec, env)?;
2129 let external = c.body.is_external();
2130 let params = Self::could_map(Self::p_fun_param, &c.parameters, env)?;
2131 let name_pos = Self::p_fun_pos(node, env);
2132 let fun = ast::Fun_ {
2133 span: Self::p_pos(node, env),
2135 mode: env.file_mode(),
2136 ret: ast::TypeHint((), Self::mp_optional(Self::p_hint, &c.type_, env)?),
2137 name: ast::Id(name_pos, String::from(";anonymous")),
2139 where_constraints: vec![],
2140 body: ast::FuncBody {
2144 fun_kind: Self::mk_fun_kind(suspension_kind, yield_),
2145 variadic: Self::determine_variadicity(¶ms),
2150 file_attributes: vec![],
2152 namespace: Self::mk_empty_ns_env(env),
2154 static_: !c.static_keyword.is_missing(),
2156 let uses = p_use(&c.use_, env).unwrap_or_else(|_| vec![]);
2157 Ok(E_::mk_efun(fun, uses))
2159 AwaitableCreationExpression(c) => {
2160 let suspension_kind = Self::mk_suspension_kind(&c.async_);
2162 Self::mp_yielding(&Self::p_function_body, &c.compound_statement, env)?;
2163 let user_attributes = Self::p_user_attributes(&c.attribute_spec, env)?;
2164 let external = c.compound_statement.is_external();
2165 let name_pos = Self::p_fun_pos(node, env);
2166 let body = ast::Fun_ {
2169 mode: env.file_mode(),
2170 ret: ast::TypeHint((), None),
2171 name: ast::Id(name_pos, String::from(";anonymous")),
2173 where_constraints: vec![],
2174 body: ast::FuncBody {
2175 ast: if blk.len() == 0 {
2176 let pos = Self::p_pos(&c.compound_statement, env);
2177 vec![ast::Stmt::noop(pos)]
2183 fun_kind: Self::mk_fun_kind(suspension_kind, yld),
2184 variadic: Self::determine_variadicity(&[]),
2186 ctxs: None, // TODO(T70095684)
2187 unsafe_ctxs: None, // TODO(T70095684)
2189 file_attributes: vec![],
2191 namespace: Self::mk_empty_ns_env(env),
2196 E::new(pos, E_::mk_lfun(body, vec![])),
2202 XHPExpression(c) if c.open.is_xhp_open() => {
2203 if let XHPOpen(c1) = &c.open.children {
2204 let name = Self::pos_name(&c1.name, env)?;
2205 let attrs = Self::could_map(Self::p_xhp_attr, &c1.attributes, env)?;
2206 let exprs = Self::aggregate_xhp_tokens(env, &c.body)?
2208 .map(|n| Self::p_xhp_embedded(Self::unesc_xhp, n, env))
2209 .collect::<std::result::Result<Vec<_>, _>>()?;
2211 let id = if env.empty_ns_env.disable_xhp_element_mangling {
2212 ast::Id(name.0, name.1)
2214 ast::Id(name.0, String::from(":") + &name.1)
2218 // TODO: update pos_name to support prefix
2222 Self::failwith("expect xhp open")
2225 EnumAtomExpression(c) => Ok(E_::EnumAtom(Self::pos_name(&c.expression, env)?.1)),
2226 _ => Self::missing_syntax_(Some(E_::Null), "expression", node, env),
2230 fn check_lvalue(ast::Expr(p, expr_): &ast::Expr, env: &mut Env<'a, TF>) {
2232 let mut raise = |s| Self::raise_parsing_error_pos(p, env, s);
2236 raise("Invalid lvalue")
2239 (_, ast::Expr(_, Id(_)), ast::OgNullFlavor::OGNullsafe, _) => {
2240 raise("?-> syntax is not supported for lvalues")
2242 (_, ast::Expr(_, Id(sid)), _, _) if sid.1.as_bytes()[0] == b':' => {
2243 raise("->: syntax is not supported for lvalues")
2250 if let ClassConst(_) = (ag.0).1 {
2251 raise("Array-like class consts are not valid lvalues");
2254 Call(c) => match &(c.0).1 {
2255 Id(sid) if sid.1 == "tuple" => {
2256 raise("Tuple cannot be used as an lvalue. Maybe you meant list?")
2258 _ => raise("Invalid lvalue"),
2262 Self::check_lvalue(i, env);
2265 Darray(_) | Varray(_) | Shape(_) | Collection(_) | Record(_) | Null | True | False
2266 | Id(_) | Clone(_) | ClassConst(_) | Int(_) | Float(_) | PrefixedString(_)
2267 | String(_) | String2(_) | Yield(_) | YieldBreak | Await(_) | Cast(_) | Unop(_)
2268 | Binop(_) | Eif(_) | New(_) | Efun(_) | Lfun(_) | Xml(_) | Import(_) | Pipe(_)
2269 | Callconv(_) | Is(_) | As(_) => raise("Invalid lvalue"),
2274 fn p_xhp_embedded<F>(escaper: F, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Expr>
2276 F: FnOnce(&[u8]) -> Vec<u8>,
2278 if let Some(kind) = Self::token_kind(node) {
2279 if env.codegen() && TK::XHPStringLiteral == kind {
2280 let p = Self::p_pos(node, env);
2281 /* for XHP string literals (attribute values) just extract
2282 value from quotes and decode HTML entities */
2283 let text = html_entities::decode(&Self::get_quoted_content(
2284 node.full_text(env.source_text()),
2286 Ok(ast::Expr::new(p, E_::make_string(text)))
2287 } else if env.codegen() && TK::XHPBody == kind {
2288 let p = Self::p_pos(node, env);
2289 /* for XHP body - only decode HTML entities */
2291 html_entities::decode(&Self::unesc_xhp(node.full_text(env.source_text())));
2292 Ok(ast::Expr::new(p, E_::make_string(text)))
2294 let p = Self::p_pos(node, env);
2295 let s = escaper(node.full_text(env.source_text()));
2296 Ok(ast::Expr::new(p, E_::make_string(s)))
2299 Self::p_expr(node, env)
2304 fn p_xhp_attr(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::XhpAttribute> {
2305 match &node.children {
2306 XHPSimpleAttribute(c) => {
2307 let attr_expr = &c.expression;
2308 let name = Self::p_pstring(&c.name, env)?;
2309 let expr = if attr_expr.is_braced_expression()
2310 && env.file_mode() == file_info::Mode::Mdecl
2313 ast::Expr::new(env.mk_none_pos(), E_::Null)
2315 Self::p_xhp_embedded(Self::unesc_xhp_attr, attr_expr, env)?
2317 Ok(ast::XhpAttribute::XhpSimple(name, expr))
2319 XHPSpreadAttribute(c) => {
2320 let expr = Self::p_xhp_embedded(Self::unesc_xhp, &c.expression, env)?;
2321 Ok(ast::XhpAttribute::XhpSpread(expr))
2323 _ => Self::missing_syntax("XHP attribute", node, env),
2327 fn aggregate_xhp_tokens(env: &mut Env<'a, TF>, nodes: S<'a, T, V>) -> Result<Vec<S<'a, T, V>>> {
2328 let nodes = nodes.syntax_node_to_list_skip_separator();
2329 let mut state = (None, None, vec![]); // (start, end, result)
2331 |state: &mut (Option<S<'a, T, V>>, Option<S<'a, T, V>>, Vec<S<'a, T, V>>)| {
2332 match (state.0, state.1) {
2333 (Some(s), None) => state.2.push(s),
2334 (Some(s), Some(e)) => {
2337 .concatenate(s.get_token().unwrap(), e.get_token().unwrap());
2338 let node = env.arena.alloc(<Syntax<T, V>>::make_token(token));
2349 Token(t) if t.kind() == TK::XHPComment => {
2350 if state.0.is_some() {
2351 combine(&mut state)?;
2355 if state.0.is_none() {
2362 combine(&mut state)?;
2367 combine(&mut state)?;
2376 env: &mut Env<'a, TF>,
2377 ) -> Result<ast::Expr_> {
2379 let mk = |op, l, r| Ok(E_::mk_binop(op, l, r));
2380 let mk_eq = |op, l, r| Ok(E_::mk_binop(Eq(Some(Box::new(op))), l, r));
2381 match Self::token_kind(node) {
2382 Some(TK::Equal) => mk(Eq(None), lhs, rhs),
2383 Some(TK::Bar) => mk(Bar, lhs, rhs),
2384 Some(TK::Ampersand) => mk(Amp, lhs, rhs),
2385 Some(TK::Plus) => mk(Plus, lhs, rhs),
2386 Some(TK::Minus) => mk(Minus, lhs, rhs),
2387 Some(TK::Star) => mk(Star, lhs, rhs),
2388 Some(TK::Carat) => mk(Xor, lhs, rhs),
2389 Some(TK::Slash) => mk(Slash, lhs, rhs),
2390 Some(TK::Dot) => mk(Dot, lhs, rhs),
2391 Some(TK::Percent) => mk(Percent, lhs, rhs),
2392 Some(TK::LessThan) => mk(Lt, lhs, rhs),
2393 Some(TK::GreaterThan) => mk(Gt, lhs, rhs),
2394 Some(TK::EqualEqual) => mk(Eqeq, lhs, rhs),
2395 Some(TK::LessThanEqual) => mk(Lte, lhs, rhs),
2396 Some(TK::GreaterThanEqual) => mk(Gte, lhs, rhs),
2397 Some(TK::StarStar) => mk(Starstar, lhs, rhs),
2398 Some(TK::ExclamationEqual) => mk(Diff, lhs, rhs),
2399 Some(TK::BarEqual) => mk_eq(Bar, lhs, rhs),
2400 Some(TK::PlusEqual) => mk_eq(Plus, lhs, rhs),
2401 Some(TK::MinusEqual) => mk_eq(Minus, lhs, rhs),
2402 Some(TK::StarEqual) => mk_eq(Star, lhs, rhs),
2403 Some(TK::StarStarEqual) => mk_eq(Starstar, lhs, rhs),
2404 Some(TK::SlashEqual) => mk_eq(Slash, lhs, rhs),
2405 Some(TK::DotEqual) => mk_eq(Dot, lhs, rhs),
2406 Some(TK::PercentEqual) => mk_eq(Percent, lhs, rhs),
2407 Some(TK::CaratEqual) => mk_eq(Xor, lhs, rhs),
2408 Some(TK::AmpersandEqual) => mk_eq(Amp, lhs, rhs),
2409 Some(TK::BarBar) => mk(Barbar, lhs, rhs),
2410 Some(TK::AmpersandAmpersand) => mk(Ampamp, lhs, rhs),
2411 Some(TK::LessThanLessThan) => mk(Ltlt, lhs, rhs),
2412 Some(TK::GreaterThanGreaterThan) => mk(Gtgt, lhs, rhs),
2413 Some(TK::EqualEqualEqual) => mk(Eqeqeq, lhs, rhs),
2414 Some(TK::LessThanLessThanEqual) => mk_eq(Ltlt, lhs, rhs),
2415 Some(TK::GreaterThanGreaterThanEqual) => mk_eq(Gtgt, lhs, rhs),
2416 Some(TK::ExclamationEqualEqual) => mk(Diff2, lhs, rhs),
2417 Some(TK::LessThanEqualGreaterThan) => mk(Cmp, lhs, rhs),
2418 Some(TK::QuestionQuestion) => mk(QuestionQuestion, lhs, rhs),
2419 Some(TK::QuestionQuestionEqual) => mk_eq(QuestionQuestion, lhs, rhs),
2420 /* The ugly duckling; In the FFP, `|>` is parsed as a
2421 * `BinaryOperator`, whereas the typed AST has separate constructors for
2422 * Pipe and Binop. This is why we don't just project onto a
2423 * `bop`, but a `expr -> expr -> expr_`.
2425 Some(TK::BarGreaterThan) => {
2427 ast::Lid::from_counter(pos, env.next_local_id(), special_idents::DOLLAR_DOLLAR);
2428 Ok(E_::mk_pipe(lid, lhs, rhs))
2430 Some(TK::QuestionColon) => Ok(E_::mk_eif(lhs, None, rhs)),
2431 _ => Self::missing_syntax("binary operator", node, env),
2435 fn p_exprs_with_loc(n: S<'a, T, V>, e: &mut Env<'a, TF>) -> Result<(Pos, Vec<ast::Expr>)> {
2436 let loc = Self::p_pos(&n, e);
2437 let p_expr = |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::Expr> {
2438 Self::p_expr_with_loc(ExprLocation::UsingStatement, n, e)
2440 Ok((loc, Self::could_map(p_expr, n, e)?))
2445 mut nodes: Iter<S<'a, T, V>>,
2446 env: &mut Env<'a, TF>,
2447 ) -> Result<Vec<ast::Stmt>> {
2450 match nodes.next() {
2451 Some(n) => match &n.children {
2452 UsingStatementFunctionScoped(c) => {
2453 let body = Self::p_stmt_list_(pos, nodes, env)?;
2454 let f = |e: &mut Env<'a, TF>| {
2457 ast::Stmt_::mk_using(ast::UsingStmt {
2458 is_block_scoped: false,
2459 has_await: !c.await_keyword.is_missing(),
2460 exprs: Self::p_exprs_with_loc(&c.expression, e)?,
2465 let using = Self::lift_awaits_in_statement_(f, Either::Right(pos), env)?;
2470 r.push(Self::p_stmt(n, env)?);
2478 fn handle_loop_body(pos: Pos, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Stmt> {
2479 let list: Vec<_> = node.syntax_node_to_list_skip_separator().collect();
2480 let blk: Vec<_> = Self::p_stmt_list_(&pos, list.iter(), env)?
2482 .filter(|stmt| !stmt.1.is_noop())
2484 let body = if blk.len() == 0 {
2485 vec![Self::mk_noop(env)]
2489 Ok(ast::Stmt::new(pos, ast::Stmt_::mk_block(body)))
2492 fn is_simple_assignment_await_expression(node: S<'a, T, V>) -> bool {
2493 match &node.children {
2494 BinaryExpression(c) => {
2495 Self::token_kind(&c.operator) == Some(TK::Equal)
2496 && Self::is_simple_await_expression(&c.right_operand)
2502 fn is_simple_await_expression(node: S<'a, T, V>) -> bool {
2503 match &node.children {
2504 PrefixUnaryExpression(c) => Self::token_kind(&c.operator) == Some(TK::Await),
2509 fn with_new_nonconcurrent_scope<F, R>(f: F, env: &mut Env<'a, TF>) -> R
2511 F: FnOnce(&mut Env<'a, TF>) -> R,
2513 let saved_lifted_awaits = env.lifted_awaits.take();
2514 let result = f(env);
2515 env.lifted_awaits = saved_lifted_awaits;
2519 fn with_new_concurrent_scope<F, R>(f: F, env: &mut Env<'a, TF>) -> Result<(LiftedAwaitExprs, R)>
2521 F: FnOnce(&mut Env<'a, TF>) -> Result<R>,
2523 let saved_lifted_awaits = env.lifted_awaits.replace(LiftedAwaits {
2525 lift_kind: LiftedAwaitKind::LiftedFromConcurrent,
2527 let result = f(env);
2528 let lifted_awaits = mem::replace(&mut env.lifted_awaits, saved_lifted_awaits);
2529 let result = result?;
2530 let awaits = match lifted_awaits {
2531 Some(la) => Self::process_lifted_awaits(la)?,
2532 None => Self::failwith("lifted awaits should not be None")?,
2534 Ok((awaits, result))
2537 fn process_lifted_awaits(mut awaits: LiftedAwaits) -> Result<LiftedAwaitExprs> {
2538 for await_ in awaits.awaits.iter() {
2539 if (await_.1).0.is_none() {
2540 return Self::failwith("none pos in lifted awaits");
2545 .sort_unstable_by(|a1, a2| Pos::cmp(&(a1.1).0, &(a2.1).0));
2549 fn clear_statement_scope<F, R>(f: F, env: &mut Env<'a, TF>) -> R
2551 F: FnOnce(&mut Env<'a, TF>) -> R,
2553 use LiftedAwaitKind::*;
2554 match &env.lifted_awaits {
2555 Some(LiftedAwaits { lift_kind, .. }) if *lift_kind == LiftedFromStatement => {
2556 let saved_lifted_awaits = env.lifted_awaits.take();
2557 let result = f(env);
2558 env.lifted_awaits = saved_lifted_awaits;
2565 fn lift_awaits_in_statement<F>(
2568 env: &mut Env<'a, TF>,
2569 ) -> Result<ast::Stmt>
2571 F: FnOnce(&mut Env<'a, TF>) -> Result<ast::Stmt>,
2573 Self::lift_awaits_in_statement_(f, Either::Left(node), env)
2576 fn lift_awaits_in_statement_<F>(
2578 pos: Either<S<'a, T, V>, &Pos>,
2579 env: &mut Env<'a, TF>,
2580 ) -> Result<ast::Stmt>
2582 F: FnOnce(&mut Env<'a, TF>) -> Result<ast::Stmt>,
2584 use LiftedAwaitKind::*;
2585 let (lifted_awaits, result) = match env.lifted_awaits {
2586 Some(LiftedAwaits { lift_kind, .. }) if lift_kind == LiftedFromConcurrent => {
2590 let saved = env.lifted_awaits.replace(LiftedAwaits {
2592 lift_kind: LiftedFromStatement,
2594 let result = f(env);
2595 let lifted_awaits = mem::replace(&mut env.lifted_awaits, saved);
2596 let result = result?;
2597 (lifted_awaits, result)
2600 if let Some(lifted_awaits) = lifted_awaits {
2601 if !lifted_awaits.awaits.is_empty() {
2602 let awaits = Self::process_lifted_awaits(lifted_awaits)?;
2603 let pos = match pos {
2604 Either::Left(n) => Self::p_pos(n, env),
2605 Either::Right(p) => p.clone(),
2607 return Ok(ast::Stmt::new(
2609 ast::Stmt_::mk_awaitall(awaits, vec![result]),
2619 env: &mut Env<'a, TF>,
2620 location: ExprLocation,
2621 ) -> Result<ast::Expr_> {
2622 use ExprLocation::*;
2623 match (&env.lifted_awaits, location) {
2624 (_, UsingStatement) | (_, RightOfAssignmentInUsingStatement) | (None, _) => {
2625 Ok(E_::mk_await(expr))
2628 if location != AsStatement {
2629 let name = env.make_tmp_var_name();
2630 let lid = ast::Lid::new(parent_pos, name.clone());
2631 let await_lid = ast::Lid::new(expr.0.clone(), name);
2632 let await_ = (Some(await_lid), expr);
2633 env.lifted_awaits.as_mut().map(|aw| aw.awaits.push(await_));
2634 Ok(E_::mk_lvar(lid))
2638 .map(|aw| aw.awaits.push((None, expr)));
2645 fn p_stmt(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Stmt> {
2646 Self::clear_statement_scope(
2647 |e: &mut Env<'a, TF>| {
2648 let docblock = Self::extract_docblock(node, e);
2649 e.push_docblock(docblock);
2650 let result = Self::p_stmt_(node, e);
2658 fn p_stmt_(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Stmt> {
2659 let pos = Self::p_pos(node, env);
2660 use ast::{Stmt, Stmt_ as S_};
2661 let new = Stmt::new;
2662 match &node.children {
2663 SwitchStatement(c) => {
2664 let p_label = |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::Case> {
2667 Ok(ast::Case::Case(Self::p_expr(&c.expression, e)?, vec![]))
2669 DefaultLabel(_) => Ok(ast::Case::Default(Self::p_pos(n, e), vec![])),
2670 _ => Self::missing_syntax("switch label", n, e),
2673 let p_section = |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<Vec<ast::Case>> {
2675 SwitchSection(c) => {
2676 let mut blk = Self::could_map(Self::p_stmt, &c.statements, e)?;
2677 if !c.fallthrough.is_missing() {
2678 blk.push(new(e.mk_none_pos(), S_::Fallthrough));
2680 let mut labels = Self::could_map(p_label, &c.labels, e)?;
2681 match labels.last_mut() {
2682 Some(ast::Case::Default(_, b)) => *b = blk,
2683 Some(ast::Case::Case(_, b)) => *b = blk,
2684 _ => Self::raise_parsing_error(n, e, "Malformed block result"),
2688 _ => Self::missing_syntax("switch section", n, e),
2691 let f = |env: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2695 Self::p_expr(&c.expression, env)?,
2696 itertools::concat(Self::could_map(p_section, &c.sections, env)?),
2700 Self::lift_awaits_in_statement(f, node, env)
2704 |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<(ast::Expr, ast::Block)> {
2706 ElseifClause(c) => Ok((
2707 Self::p_expr(&c.condition, e)?,
2708 Self::p_block(true, &c.statement, e)?,
2710 _ => Self::missing_syntax("elseif clause", n, e),
2713 let f = |env: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2714 let condition = Self::p_expr(&c.condition, env)?;
2715 let statement = Self::p_block(true /* remove noop */, &c.statement, env)?;
2716 let else_ = match &c.else_clause.children {
2717 ElseClause(c) => Self::p_block(true, &c.statement, env)?,
2718 Missing => vec![Self::mk_noop(env)],
2719 _ => Self::missing_syntax("else clause", &c.else_clause, env)?,
2721 let else_ifs = Self::could_map(p_else_if, &c.elseif_clauses, env)?;
2722 let else_if = else_ifs
2725 .fold(else_, |child, (cond, stmts)| {
2726 vec![new(pos.clone(), S_::mk_if(cond, stmts, child))]
2728 Ok(new(pos, S_::mk_if(condition, statement, else_if)))
2730 Self::lift_awaits_in_statement(f, node, env)
2732 ExpressionStatement(c) => {
2733 let expr = &c.expression;
2734 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2735 if expr.is_missing() {
2736 Ok(new(pos, S_::Noop))
2740 S_::mk_expr(Self::p_expr_with_loc(ExprLocation::AsStatement, expr, e)?),
2744 if Self::is_simple_assignment_await_expression(expr)
2745 || Self::is_simple_await_expression(expr)
2749 Self::lift_awaits_in_statement(f, node, env)
2752 CompoundStatement(c) => Self::handle_loop_body(pos, &c.statements, env),
2753 SyntaxList(_) => Self::handle_loop_body(pos, node, env),
2754 ThrowStatement(c) => Self::lift_awaits_in_statement(
2755 |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2756 Ok(new(pos, S_::mk_throw(Self::p_expr(&c.expression, e)?)))
2761 DoStatement(c) => Ok(new(
2764 Self::p_block(false /* remove noop */, &c.body, env)?,
2765 Self::p_expr(&c.condition, env)?,
2768 WhileStatement(c) => Ok(new(
2771 Self::p_expr(&c.condition, env)?,
2772 Self::p_block(true, &c.body, env)?,
2775 UsingStatementBlockScoped(c) => {
2776 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2779 S_::mk_using(ast::UsingStmt {
2780 is_block_scoped: true,
2781 has_await: !&c.await_keyword.is_missing(),
2782 exprs: Self::p_exprs_with_loc(&c.expressions, e)?,
2783 block: Self::p_block(false, &c.body, e)?,
2787 Self::lift_awaits_in_statement(f, node, env)
2789 UsingStatementFunctionScoped(c) => {
2790 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2793 S_::mk_using(ast::UsingStmt {
2794 is_block_scoped: false,
2795 has_await: !&c.await_keyword.is_missing(),
2796 exprs: Self::p_exprs_with_loc(&c.expression, e)?,
2797 block: vec![Self::mk_noop(e)],
2801 Self::lift_awaits_in_statement(f, node, env)
2803 ForStatement(c) => {
2804 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2805 let ini = Self::p_expr_l(&c.initializer, e)?;
2806 let ctr = Self::mp_optional(Self::p_expr, &c.control, e)?;
2807 let eol = Self::p_expr_l(&c.end_of_loop, e)?;
2808 let blk = Self::p_block(true, &c.body, e)?;
2809 Ok(Stmt::new(pos, S_::mk_for(ini, ctr, eol, blk)))
2811 Self::lift_awaits_in_statement(f, node, env)
2813 ForeachStatement(c) => {
2814 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2815 let col = Self::p_expr(&c.collection, e)?;
2816 let akw = match Self::token_kind(&c.await_keyword) {
2817 Some(TK::Await) => Some(Self::p_pos(&c.await_keyword, e)),
2820 let value = Self::p_expr(&c.value, e)?;
2821 let akv = match (akw, &c.key.children) {
2822 (Some(p), Missing) => ast::AsExpr::AwaitAsV(p, value),
2823 (None, Missing) => ast::AsExpr::AsV(value),
2824 (Some(p), _) => ast::AsExpr::AwaitAsKv(p, Self::p_expr(&c.key, e)?, value),
2825 (None, _) => ast::AsExpr::AsKv(Self::p_expr(&c.key, e)?, value),
2827 let blk = Self::p_block(true, &c.body, e)?;
2828 Ok(new(pos, S_::mk_foreach(col, akv, blk)))
2830 Self::lift_awaits_in_statement(f, node, env)
2832 TryStatement(c) => Ok(new(
2835 Self::p_block(false, &c.compound_statement, env)?,
2837 |n: S<'a, T, V>, e| match &n.children {
2838 CatchClause(c) => Ok(ast::Catch(
2839 Self::pos_name(&c.type_, e)?,
2840 Self::lid_from_name(&c.variable, e)?,
2841 Self::p_block(true, &c.body, e)?,
2843 _ => Self::missing_syntax("catch clause", n, e),
2848 match &c.finally_clause.children {
2849 FinallyClause(c) => Self::p_block(false, &c.body, env)?,
2854 ReturnStatement(c) => {
2855 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2856 let expr = match &c.expression.children {
2858 _ => Some(Self::p_expr_with_loc(
2859 ExprLocation::RightOfReturn,
2864 Ok(ast::Stmt::new(pos, ast::Stmt_::mk_return(expr)))
2866 if Self::is_simple_await_expression(&c.expression) {
2869 Self::lift_awaits_in_statement(f, node, env)
2872 EchoStatement(c) => {
2873 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2874 let echo = match &c.keyword.children {
2875 QualifiedName(_) | SimpleTypeSpecifier(_) | Token(_) => {
2876 let name = Self::pos_name(&c.keyword, e)?;
2877 ast::Expr::new(name.0.clone(), E_::mk_id(name))
2879 _ => Self::missing_syntax("id", &c.keyword, e)?,
2881 let args = Self::could_map(Self::p_expr, &c.expressions, e)?;
2884 S_::mk_expr(ast::Expr::new(pos, E_::mk_call(echo, vec![], args, None))),
2887 Self::lift_awaits_in_statement(f, node, env)
2889 UnsetStatement(c) => {
2890 let f = |e: &mut Env<'a, TF>| -> Result<ast::Stmt> {
2891 let args = Self::could_map(Self::p_expr, &c.variables, e)?;
2892 if e.parser_options.po_disable_unset_class_const {
2894 .for_each(|arg| Self::check_mutate_class_const(arg, node, e))
2896 let unset = match &c.keyword.children {
2897 QualifiedName(_) | SimpleTypeSpecifier(_) | Token(_) => {
2898 let name = Self::pos_name(&c.keyword, e)?;
2899 ast::Expr::new(name.0.clone(), E_::mk_id(name))
2901 _ => Self::missing_syntax("id", &c.keyword, e)?,
2905 S_::mk_expr(ast::Expr::new(pos, E_::mk_call(unset, vec![], args, None))),
2908 Self::lift_awaits_in_statement(f, node, env)
2910 BreakStatement(_) => Ok(new(pos, S_::Break)),
2911 ContinueStatement(_) => Ok(new(pos, S_::Continue)),
2912 ConcurrentStatement(c) => {
2913 let keyword_pos = Self::p_pos(&c.keyword, env);
2914 let (lifted_awaits, Stmt(stmt_pos, stmt)) = Self::with_new_concurrent_scope(
2915 |e: &mut Env<'a, TF>| Self::p_stmt(&c.statement, e),
2918 let stmt = match stmt {
2919 S_::Block(stmts) => {
2922 /* Reuse tmp vars from lifted_awaits, this is safe because there will
2923 * always be more awaits with tmp vars than statements with assignments */
2924 let mut tmp_vars = lifted_awaits
2926 .filter_map(|lifted_await| lifted_await.0.as_ref().map(|x| &x.1));
2927 let mut body_stmts = vec![];
2928 let mut assign_stmts = vec![];
2929 for n in stmts.into_iter() {
2930 if !n.is_assign_expr() {
2935 if let Some(tv) = tmp_vars.next() {
2936 if let Stmt(p1, S_::Expr(expr)) = n {
2937 if let E(p2, E_::Binop(bop)) = *expr {
2938 if let (Eq(op), e1, e2) = *bop {
2939 let tmp_n = E::mk_lvar(&e2.0, &(tv.1));
2940 if tmp_n.lvar_name() != e2.lvar_name() {
2952 body_stmts.push(new_n);
2954 let assign_stmt = new(
2958 E_::mk_binop(Eq(op), e1, tmp_n),
2961 assign_stmts.push(assign_stmt);
2967 Self::failwith("Expect assignment stmt")?;
2969 Self::raise_parsing_error_pos(
2972 &syntax_error::statement_without_await_in_concurrent_block,
2977 body_stmts.append(&mut assign_stmts);
2978 new(stmt_pos, S_::mk_block(body_stmts))
2980 _ => Self::failwith("Unexpected concurrent stmt structure")?,
2982 Ok(new(keyword_pos, S_::mk_awaitall(lifted_awaits, vec![stmt])))
2984 MarkupSection(_) => Self::p_markup(node, env),
2985 _ => Self::missing_syntax_(
2986 Some(new(env.mk_none_pos(), S_::Noop)),
2993 fn check_mutate_class_const(e: &ast::Expr, node: S<'a, T, V>, env: &mut Env<'a, TF>) {
2995 E_::ArrayGet(c) if c.1.is_some() => Self::check_mutate_class_const(&c.0, node, env),
2996 E_::ClassConst(_) => {
2997 Self::raise_parsing_error(node, env, &syntax_error::const_mutation)
3003 fn is_hashbang(node: S<'a, T, V>, env: &Env<TF>) -> bool {
3004 let text = Self::text_str(node, env);
3006 static ref RE: regex::Regex = regex::Regex::new("^#!.*\n").unwrap();
3008 text.lines().nth(1).is_none() && // only one line
3012 fn p_markup(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Stmt> {
3013 match &node.children {
3014 MarkupSection(c) => {
3015 let markup_hashbang = &c.hashbang;
3016 let pos = Self::p_pos(node, env);
3017 let f = pos.filename();
3018 if (f.has_extension("hack") || f.has_extension("hackpartial"))
3019 && !(&c.suffix.is_missing())
3021 let ext = f.path().extension().unwrap(); // has_extension ensures this is a Some
3022 Self::raise_parsing_error(
3025 &syntax_error::error1060(&ext.to_str().unwrap()),
3027 } else if markup_hashbang.width() > 0 && !Self::is_hashbang(markup_hashbang, env) {
3028 Self::raise_parsing_error(node, env, &syntax_error::error1001);
3030 let stmt_ = ast::Stmt_::mk_markup((pos.clone(), Self::text(markup_hashbang, env)));
3031 Ok(ast::Stmt::new(pos, stmt_))
3033 _ => Self::failwith("invalid node"),
3037 fn p_modifiers<F: Fn(R, modifier::Kind) -> R, R>(
3041 env: &mut Env<'a, TF>,
3042 ) -> Result<(modifier::KindSet, R)> {
3043 let mut kind_set = modifier::KindSet::new();
3044 for n in node.syntax_node_to_list_skip_separator() {
3045 let token_kind = Self::token_kind(n).map_or(None, modifier::from_token_kind);
3049 init = on_kind(init, kind);
3051 _ => Self::missing_syntax("kind", n, env)?,
3054 Ok((kind_set, init))
3057 fn p_kinds(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<modifier::KindSet> {
3058 Self::p_modifiers(|_, _| {}, (), node, env).map(|r| r.0)
3061 fn could_map<R, F>(f: F, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<Vec<R>>
3063 F: Fn(S<'a, T, V>, &mut Env<'a, TF>) -> Result<R>,
3065 Self::map_flatten_(f, node, env, vec![])
3068 fn map_flatten_<R, F>(
3071 env: &mut Env<'a, TF>,
3075 F: Fn(S<'a, T, V>, &mut Env<'a, TF>) -> Result<R>,
3077 let op = |mut v: Vec<R>, a| {
3081 Self::map_fold(&f, &op, node, env, acc)
3084 fn could_map_filter<R, F>(
3087 env: &mut Env<'a, TF>,
3088 ) -> Result<(Vec<R>, bool)>
3090 F: Fn(S<'a, T, V>, &mut Env<'a, TF>) -> Result<Option<R>>,
3092 Self::map_flatten_filter_(f, node, env, (vec![], false))
3096 fn map_flatten_filter_<R, F>(
3099 env: &mut Env<'a, TF>,
3100 acc: (Vec<R>, bool),
3101 ) -> Result<(Vec<R>, bool)>
3103 F: Fn(S<'a, T, V>, &mut Env<'a, TF>) -> Result<Option<R>>,
3105 let op = |mut v: (Vec<R>, bool), a| -> (Vec<R>, bool) {
3107 Option::None => (v.0, true),
3108 Option::Some(a) => {
3114 Self::map_fold(&f, &op, node, env, acc)
3117 fn map_fold<A, R, F, O>(
3121 env: &mut Env<'a, TF>,
3125 F: Fn(S<'a, T, V>, &mut Env<'a, TF>) -> Result<R>,
3128 for n in node.syntax_node_to_list_skip_separator() {
3129 acc = op(acc, f(n, env)?);
3134 fn p_visibility(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<Option<ast::Visibility>> {
3136 |r: Option<ast::Visibility>, kind| r.or_else(|| modifier::to_visibility(kind));
3137 Self::p_modifiers(first_vis, None, node, env).map(|r| r.1)
3142 env: &mut Env<'a, TF>,
3143 default: ast::Visibility,
3144 ) -> Result<ast::Visibility> {
3145 Self::p_visibility(node, env).map(|v| v.unwrap_or(default))
3148 fn p_visibility_last_win(
3150 env: &mut Env<'a, TF>,
3151 ) -> Result<Option<ast::Visibility>> {
3152 let last_vis = |r, kind| modifier::to_visibility(kind).or(r);
3153 Self::p_modifiers(last_vis, None, node, env).map(|r| r.1)
3156 fn p_visibility_last_win_or(
3158 env: &mut Env<'a, TF>,
3159 default: ast::Visibility,
3160 ) -> Result<ast::Visibility> {
3161 Self::p_visibility_last_win(node, env).map(|v| v.unwrap_or(default))
3164 fn has_soft(attrs: &[ast::UserAttribute]) -> bool {
3165 attrs.iter().any(|attr| attr.name.1 == special_attrs::SOFT)
3168 fn soften_hint(attrs: &[ast::UserAttribute], hint: ast::Hint) -> ast::Hint {
3169 if Self::has_soft(attrs) {
3170 ast::Hint::new(hint.0.clone(), ast::Hint_::Hsoft(hint))
3176 fn has_polymorphic_context(contexts: &Option<ast::Contexts>) -> bool {
3177 use ast::Hint_::{Haccess, HfunContext, Hvar};
3178 if let Some(ast::Contexts(_, ref context_hints)) = contexts {
3179 return context_hints.iter().any(|c| match *c.1 {
3180 HfunContext(_) => true,
3181 Haccess(ref root, _) => {
3182 if let Hvar(_) = *root.1 {
3195 fn rewrite_effect_polymorphism(
3196 env: &mut Env<'a, TF>,
3197 params: &mut Vec<ast::FunParam>,
3198 tparams: &mut Vec<ast::Tparam>,
3199 contexts: &Option<ast::Contexts>,
3200 where_constraints: &mut Vec<ast::WhereConstraintHint>,
3202 use ast::{Hint, Hint_, ReifyKind, Variance};
3203 use Hint_::{Haccess, Happly, HfunContext, Hvar};
3205 if !Self::has_polymorphic_context(contexts) {
3208 let ast::Contexts(ref _p, ref context_hints) = contexts.as_ref().unwrap();
3209 let tp = |name, v| ast::Tparam {
3210 variance: Variance::Invariant,
3214 reified: ReifyKind::Erased,
3215 user_attributes: vec![],
3218 // For polymorphic context with form `ctx $f`
3219 // require that `(function (ts)[_]: t) $f` exists
3220 // rewrite as `(function (ts)[ctx $f]: t) $f`
3221 // add a type parameter named "Tctx$f"
3222 let rewrite_fun_ctx =
3223 |env: &mut Env<'a, TF>, tparams: &mut Vec<ast::Tparam>, hint: &mut Hint, name: &str| {
3224 let mut invalid = |p| {
3225 Self::raise_parsing_error_pos(
3228 &syntax_error::ctx_fun_invalid_type_hint(name),
3232 Hint_::Hfun(ref mut hf) => {
3233 if let Some(ast::Contexts(ref p, ref mut hl)) = &mut hf.ctxs {
3234 if let [ref mut h] = *hl.as_mut_slice() {
3235 if let Hint_::Happly(ast::Id(ref pos, s), _) = &*h.1 {
3237 *h.1 = Hint_::HfunContext(name.to_string());
3239 ast::Id(h.0.clone(), "Tctx".to_string() + name),
3255 _ => invalid(&hint.0),
3259 // For polymorphic context with form `$g::C`
3260 // if $g's type is not a type parameter
3261 // add one named "T$g" constrained by $g's type
3262 // replace $g's type hint
3263 // let Tg denote $g's final type (must be a type parameter).
3264 // add a type parameter "T$g@C"
3265 // add a where constraint T$g@C = Tg :: C
3266 let rewrite_arg_ctx = |
3267 env: &mut Env<'a, TF>,
3268 tparams: &mut Vec<ast::Tparam>,
3269 where_constraints: &mut Vec<ast::WhereConstraintHint>,
3276 Happly(ast::Id(_, ref type_name), _) => {
3277 if !tparams.iter().any(|h| h.name.1 == *type_name) {
3278 // If the parameter is X $g, create tparam `T$g as X` and replace $g's type hint
3279 let id = ast::Id(param_pos.clone(), "T".to_string() + name);
3282 vec![(ast::ConstraintKind::ConstraintAs, hint.clone())],
3284 *hint = ast::Hint::new(param_pos.clone(), Happly(id, vec![]));
3286 let right = ast::Hint::new(
3287 context_pos.clone(),
3288 Haccess(hint.clone(), vec![cst.clone()]),
3290 let left_id = ast::Id(
3291 context_pos.clone(),
3292 // IMPORTANT: using `::` here will not work, because
3293 // Typing_taccess constructs its own fake type parameter
3294 // for the Taccess with `::`. So, if the two type parameters
3295 // are named `Tprefix` and `Tprefix::Const`, the latter
3296 // will collide with the one generated for `Tprefix`::Const
3297 "T".to_string() + name + "@" + &cst.1,
3299 tparams.push(tp(left_id.clone(), vec![]));
3300 let left = ast::Hint::new(context_pos.clone(), Happly(left_id, vec![]));
3301 where_constraints.push(ast::WhereConstraintHint(
3303 ast::ConstraintKind::ConstraintEq,
3307 _ => Self::raise_parsing_error_pos(
3310 &syntax_error::ctx_var_invalid_type_hint(name),
3314 let mut hint_by_param: std::collections::HashMap<
3316 (&mut Option<ast::Hint>, &Pos, aast::IsVariadic),
3317 std::hash::BuildHasherDefault<fnv::FnvHasher>,
3318 > = fnv::FnvHashMap::default();
3319 for param in params.iter_mut() {
3320 hint_by_param.insert(
3321 param.name.as_ref(),
3322 (&mut param.type_hint.1, ¶m.pos, param.is_variadic),
3327 for context_hint in context_hints {
3328 match *context_hint.1 {
3329 HfunContext(ref name) => match hint_by_param.get_mut::<str>(name) {
3330 Some((hint_opt, param_pos, _is_variadic)) => match hint_opt {
3331 Some(ref mut param_hint) => match *param_hint.1 {
3332 Hint_::Hoption(ref mut h) => rewrite_fun_ctx(env, tparams, h, name),
3333 _ => rewrite_fun_ctx(env, tparams, param_hint, name),
3335 None => Self::raise_parsing_error_pos(
3338 &syntax_error::ctx_var_missing_type_hint(name),
3342 None => Self::raise_parsing_error_pos(
3345 &syntax_error::ctx_var_invalid_parameter(name),
3348 Haccess(ref root, ref csts) => {
3349 if let Hvar(ref name) = *root.1 {
3350 match hint_by_param.get_mut::<str>(name) {
3351 Some((hint_opt, param_pos, is_variadic)) => {
3353 Self::raise_parsing_error_pos(
3356 &syntax_error::ctx_var_variadic(name),
3360 Some(ref mut param_hint) => match *param_hint.1 {
3361 Hint_::Hoption(ref mut h) => rewrite_arg_ctx(
3371 _ => rewrite_arg_ctx(
3382 None => Self::raise_parsing_error_pos(
3385 &syntax_error::ctx_var_missing_type_hint(name),
3390 None => Self::raise_parsing_error_pos(
3393 &syntax_error::ctx_var_invalid_parameter(name),
3403 fn p_fun_param_default_value(
3405 env: &mut Env<'a, TF>,
3406 ) -> Result<Option<ast::Expr>> {
3407 match &node.children {
3408 SimpleInitializer(c) => Self::mp_optional(Self::p_expr, &c.value, env),
3413 fn p_param_kind(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::ParamKind> {
3414 match Self::token_kind(node) {
3415 Some(TK::Inout) => Ok(ast::ParamKind::Pinout),
3416 _ => Self::missing_syntax("param kind", node, env),
3420 fn param_template(node: S<'a, T, V>, env: &Env<TF>) -> ast::FunParam {
3421 let pos = Self::p_pos(node, env);
3423 annotation: pos.clone(),
3424 type_hint: ast::TypeHint((), None),
3427 name: Self::text(node, env),
3430 user_attributes: vec![],
3435 fn p_fun_param(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::FunParam> {
3436 match &node.children {
3437 ParameterDeclaration(ParameterDeclarationChildren {
3445 let (is_variadic, name) = match &name.children {
3446 DecoratedExpression(DecoratedExpressionChildren {
3450 let decorator = Self::text_str(decorator, env);
3451 match &expression.children {
3452 DecoratedExpression(c) => {
3453 let nested_expression = &c.expression;
3454 let nested_decorator = Self::text_str(&c.decorator, env);
3456 decorator == "..." || nested_decorator == "...",
3460 _ => (decorator == "...", expression),
3465 let user_attributes = Self::p_user_attributes(attribute, env)?;
3466 let pos = Self::p_pos(name, env);
3467 let name = Self::text(name, env);
3468 let hint = Self::mp_optional(Self::p_hint, type_, env)?;
3469 let hint = hint.map(|h| Self::soften_hint(&user_attributes, h));
3471 if is_variadic && !user_attributes.is_empty() {
3472 Self::raise_parsing_error(
3475 &syntax_error::no_attributes_on_variadic_parameter,
3479 annotation: pos.clone(),
3480 type_hint: ast::TypeHint((), hint),
3485 expr: Self::p_fun_param_default_value(default_value, env)?,
3486 callconv: Self::mp_optional(Self::p_param_kind, call_convention, env)?,
3487 /* implicit field via constructor parameter.
3488 * This is always None except for constructors and the modifier
3489 * can be only Public or Protected or Private.
3491 visibility: Self::p_visibility(visibility, env)?,
3494 VariadicParameter(_) => {
3495 let mut param = Self::param_template(node, env);
3496 param.is_variadic = true;
3499 Token(_) if Self::text_str(node, env) == "..." => {
3500 let mut param = Self::param_template(node, env);
3501 param.is_variadic = true;
3504 _ => Self::missing_syntax("function parameter", node, env),
3508 fn p_tconstraint_ty(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Hint> {
3509 match &node.children {
3510 TypeConstraint(c) => Self::p_hint(&c.type_, env),
3511 _ => Self::missing_syntax("type constraint", node, env),
3517 env: &mut Env<'a, TF>,
3518 ) -> Result<(ast::ConstraintKind, ast::Hint)> {
3519 match &node.children {
3520 TypeConstraint(c) => Ok((
3521 match Self::token_kind(&c.keyword) {
3522 Some(TK::As) => ast::ConstraintKind::ConstraintAs,
3523 Some(TK::Super) => ast::ConstraintKind::ConstraintSuper,
3524 Some(TK::Equal) => ast::ConstraintKind::ConstraintEq,
3525 _ => Self::missing_syntax("constraint operator", &c.keyword, env)?,
3527 Self::p_hint(&c.type_, env)?,
3529 _ => Self::missing_syntax("type constraint", node, env),
3533 fn p_tparam(is_class: bool, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Tparam> {
3534 match &node.children {
3535 TypeParameter(TypeParameterChildren {
3543 let user_attributes = Self::p_user_attributes(attribute_spec, env)?;
3544 let is_reified = !reified.is_missing();
3545 if is_class && is_reified {
3546 let type_name = Self::text(name, env);
3547 env.cls_reified_generics().insert(type_name);
3549 let variance = match Self::token_kind(variance) {
3550 Some(TK::Plus) => ast::Variance::Covariant,
3551 Some(TK::Minus) => ast::Variance::Contravariant,
3552 _ => ast::Variance::Invariant,
3554 if is_reified && variance != ast::Variance::Invariant {
3555 Self::raise_parsing_error(
3558 &syntax_error::non_invariant_reified_generic,
3561 let reified = match (is_reified, Self::has_soft(&user_attributes)) {
3562 (true, true) => ast::ReifyKind::SoftReified,
3563 (true, false) => ast::ReifyKind::Reified,
3564 _ => ast::ReifyKind::Erased,
3566 let parameters = Self::p_tparam_l(is_class, param_params, env)?;
3569 name: Self::pos_name(name, env)?,
3571 constraints: Self::could_map(Self::p_tconstraint, constraints, env)?,
3576 _ => Self::missing_syntax("type parameter", node, env),
3583 env: &mut Env<'a, TF>,
3584 ) -> Result<Vec<ast::Tparam>> {
3585 match &node.children {
3586 Missing => Ok(vec![]),
3587 TypeParameters(c) => {
3588 Self::could_map(|n, e| Self::p_tparam(is_class, n, e), &c.parameters, env)
3590 _ => Self::missing_syntax("type parameter", node, env),
3596 env: &mut Env<'a, TF>,
3597 ) -> Result<(Option<ast::Contexts>, Option<ast::Contexts>)> {
3598 match &node.children {
3599 Missing => Ok((None, None)),
3601 let hints = Self::could_map(&Self::p_hint, &c.types, env)?;
3602 let pos = Self::p_pos(node, env);
3603 let ctxs = ast::Contexts(pos, hints);
3604 let unsafe_ctxs = ctxs.clone();
3605 Ok((Some(ctxs), Some(unsafe_ctxs)))
3607 _ => Self::missing_syntax("contexts", node, env),
3611 fn p_context_list_to_intersection(
3612 ctx_list: S<'a, T, V>,
3613 env: &mut Env<'a, TF>,
3614 ) -> Result<Option<ast::Hint>> {
3615 Ok(Self::mp_optional(Self::p_contexts, &ctx_list, env)?
3617 .map(|t| ast::Hint::new(t.0, ast::Hint_::Hintersection(t.1))))
3620 fn p_fun_hdr(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<FunHdr> {
3621 match &node.children {
3622 FunctionDeclarationHeader(FunctionDeclarationHeaderChildren {
3626 type_parameter_list,
3632 if name.value.is_missing() {
3633 Self::raise_parsing_error(name, env, &syntax_error::empty_method_name);
3635 let kinds = Self::p_kinds(modifiers, env)?;
3636 let has_async = kinds.has(modifier::ASYNC);
3637 let mut type_parameters = Self::p_tparam_l(false, type_parameter_list, env)?;
3638 let mut parameters = Self::could_map(Self::p_fun_param, parameter_list, env)?;
3639 let (contexts, unsafe_contexts) = Self::p_contexts(contexts, env)?;
3640 let mut constrs = Self::p_where_constraint(false, node, where_clause, env)?;
3641 Self::rewrite_effect_polymorphism(
3644 &mut type_parameters,
3648 let return_type = Self::mp_optional(Self::p_hint, type_, env)?;
3649 let suspension_kind = Self::mk_suspension_kind_(has_async);
3650 let name = Self::pos_name(name, env)?;
3662 LambdaSignature(LambdaSignatureChildren {
3668 let mut header = FunHdr::make_empty(env);
3669 header.parameters = Self::could_map(Self::p_fun_param, parameters, env)?;
3670 let (contexts, unsafe_contexts) = Self::p_contexts(contexts, env)?;
3671 header.contexts = contexts;
3672 header.unsafe_contexts = unsafe_contexts;
3673 header.return_type = Self::mp_optional(Self::p_hint, type_, env)?;
3676 Token(_) => Ok(FunHdr::make_empty(env)),
3677 _ => Self::missing_syntax("function header", node, env),
3681 fn determine_variadicity(params: &[ast::FunParam]) -> ast::FunVariadicity {
3682 use aast::FunVariadicity::*;
3683 if let Some(x) = params.last() {
3684 match (x.is_variadic, &x.name) {
3685 (false, _) => FVnonVariadic,
3686 (true, name) if name == "..." => FVellipsis(x.pos.clone()),
3687 (true, _) => FVvariadicArg(x.clone()),
3694 fn p_fun_pos(node: S<'a, T, V>, env: &Env<TF>) -> Pos {
3695 let get_pos = |n: S<'a, T, V>, p: Pos| -> Pos {
3696 if let FunctionDeclarationHeader(c1) = &n.children {
3697 if !c1.keyword.is_missing() {
3698 return Pos::btw_nocheck(Self::p_pos(&c1.keyword, env), p);
3703 let p = Self::p_pos(node, env);
3704 match &node.children {
3705 FunctionDeclaration(c) if env.codegen() => get_pos(&c.declaration_header, p),
3706 MethodishDeclaration(c) if env.codegen() => get_pos(&c.function_decl_header, p),
3711 fn p_block(remove_noop: bool, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Block> {
3712 let ast::Stmt(p, stmt_) = Self::p_stmt(node, env)?;
3713 if let ast::Stmt_::Block(blk) = stmt_ {
3714 if remove_noop && blk.len() == 1 && blk[0].1.is_noop() {
3719 Ok(vec![ast::Stmt(p, stmt_)])
3723 fn mk_noop(env: &Env<TF>) -> ast::Stmt {
3724 ast::Stmt::noop(env.mk_none_pos())
3727 fn p_function_body(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Block> {
3728 let mk_noop_result = |e: &Env<TF>| Ok(vec![Self::mk_noop(e)]);
3729 let f = |e: &mut Env<'a, TF>| -> Result<ast::Block> {
3730 match &node.children {
3731 Missing => Ok(vec![]),
3732 CompoundStatement(c) => {
3733 let compound_statements = &c.statements.children;
3734 let compound_right_brace = &c.right_brace.children;
3735 match (compound_statements, compound_right_brace) {
3736 (Missing, Token(_)) => mk_noop_result(e),
3737 (SyntaxList(t), _) if t.len() == 1 && t[0].is_yield() => {
3742 if !e.top_level_statements
3743 && ((e.file_mode() == file_info::Mode::Mdecl && !e.codegen())
3748 Self::p_block(false /*remove noop*/, node, e)
3754 let f = |e: &mut Env<'a, TF>| {
3755 let expr = Self::p_expr(node, e)?;
3758 ast::Stmt_::mk_return(Some(expr)),
3761 if Self::is_simple_await_expression(node) {
3764 Ok(vec![Self::lift_awaits_in_statement(f, node, e)?])
3769 Self::with_new_nonconcurrent_scope(f, env)
3772 fn mk_suspension_kind(async_keyword: S<'a, T, V>) -> SuspensionKind {
3773 Self::mk_suspension_kind_(!async_keyword.is_missing())
3776 fn mk_suspension_kind_(has_async: bool) -> SuspensionKind {
3778 SuspensionKind::SKAsync
3780 SuspensionKind::SKSync
3784 fn mk_fun_kind(suspension_kind: SuspensionKind, yield_: bool) -> ast::FunKind {
3785 use ast::FunKind::*;
3786 use SuspensionKind::*;
3787 match (suspension_kind, yield_) {
3788 (SKSync, true) => FGenerator,
3789 (SKAsync, true) => FAsyncGenerator,
3790 (SKSync, false) => FSync,
3791 (SKAsync, false) => FAsync,
3795 fn process_attribute_constructor_call(
3797 constructor_call_argument_list: S<'a, T, V>,
3798 constructor_call_type: S<'a, T, V>,
3799 env: &mut Env<'a, TF>,
3800 ) -> Result<ast::UserAttribute> {
3801 let name = Self::pos_name(constructor_call_type, env)?;
3802 if name.1.eq_ignore_ascii_case("__reified")
3803 || name.1.eq_ignore_ascii_case("__hasreifiedparent")
3805 Self::raise_parsing_error(node, env, &syntax_error::reified_attribute);
3806 } else if name.1.eq_ignore_ascii_case(special_attrs::SOFT)
3807 && constructor_call_argument_list
3808 .syntax_node_to_list_skip_separator()
3812 Self::raise_parsing_error(node, env, &syntax_error::soft_no_arguments);
3814 let params = Self::could_map(
3815 |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::Expr> {
3816 Self::is_valid_attribute_arg(n, e);
3819 constructor_call_argument_list,
3822 Ok(ast::UserAttribute { name, params })
3825 // Arguments to attributes must be literals (int, string, etc), collections
3826 // (eg vec, dict, keyset, etc), Foo::class strings, shapes, string
3827 // concatenations, or tuples.
3828 fn is_valid_attribute_arg(node: S<'a, T, V>, env: &mut Env<'a, TF>) {
3829 let mut is_valid_list = |nodes: S<'a, T, V>| {
3830 let _ = Self::could_map(
3832 Self::is_valid_attribute_arg(n, e);
3840 match &node.children {
3841 ParenthesizedExpression(c) => Self::is_valid_attribute_arg(&c.expression, env),
3842 // Normal literals (string, int, etc)
3843 LiteralExpression(_) => {}
3845 ScopeResolutionExpression(c) => {
3846 if let Some(TK::Class) = Self::token_kind(&c.name) {
3848 Self::raise_parsing_error(
3851 &syntax_error::expression_as_attribute_arguments,
3856 PrefixUnaryExpression(c) => {
3857 Self::is_valid_attribute_arg(&c.operand, env);
3858 match Self::token_kind(&c.operator) {
3859 Some(TK::Minus) => {}
3860 Some(TK::Plus) => {}
3861 _ => Self::raise_parsing_error(
3864 &syntax_error::expression_as_attribute_arguments,
3868 // String concatenation
3869 BinaryExpression(c) => {
3870 if let Some(TK::Dot) = Self::token_kind(&c.operator) {
3871 Self::is_valid_attribute_arg(&c.left_operand, env);
3872 Self::is_valid_attribute_arg(&c.right_operand, env);
3874 Self::raise_parsing_error(
3877 &syntax_error::expression_as_attribute_arguments,
3881 // Top-level Collections
3882 DarrayIntrinsicExpression(c) => is_valid_list(&c.members),
3883 DictionaryIntrinsicExpression(c) => is_valid_list(&c.members),
3884 KeysetIntrinsicExpression(c) => is_valid_list(&c.members),
3885 VarrayIntrinsicExpression(c) => is_valid_list(&c.members),
3886 VectorIntrinsicExpression(c) => is_valid_list(&c.members),
3887 ShapeExpression(c) => is_valid_list(&c.fields),
3888 TupleExpression(c) => is_valid_list(&c.items),
3889 // Collection Internals
3890 FieldInitializer(c) => {
3891 Self::is_valid_attribute_arg(&c.name, env);
3892 Self::is_valid_attribute_arg(&c.value, env);
3894 ElementInitializer(c) => {
3895 Self::is_valid_attribute_arg(&c.key, env);
3896 Self::is_valid_attribute_arg(&c.value, env);
3898 // Everything else is not allowed
3899 _ => Self::raise_parsing_error(
3902 &syntax_error::expression_as_attribute_arguments,
3907 fn p_user_attribute(
3909 env: &mut Env<'a, TF>,
3910 ) -> Result<Vec<ast::UserAttribute>> {
3911 let p_attr = |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::UserAttribute> {
3913 ConstructorCall(c) => {
3914 Self::process_attribute_constructor_call(node, &c.argument_list, &c.type_, e)
3916 _ => Self::missing_syntax("attribute", node, e),
3919 match &node.children {
3920 FileAttributeSpecification(c) => Self::could_map(p_attr, &c.attributes, env),
3921 OldAttributeSpecification(c) => Self::could_map(p_attr, &c.attributes, env),
3922 AttributeSpecification(c) => Self::could_map(
3923 |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::UserAttribute> {
3925 Attribute(c) => p_attr(&c.attribute_name, e),
3926 _ => Self::missing_syntax("attribute", node, e),
3932 _ => Self::missing_syntax("attribute specification", node, env),
3936 fn p_user_attributes(
3938 env: &mut Env<'a, TF>,
3939 ) -> Result<Vec<ast::UserAttribute>> {
3941 &Self::p_user_attribute,
3942 &|mut acc: Vec<ast::UserAttribute>, mut x: Vec<ast::UserAttribute>| {
3952 fn mp_yielding<F, R>(p: F, node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<(R, bool)>
3954 F: FnOnce(S<'a, T, V>, &mut Env<'a, TF>) -> Result<R>,
3956 let outer_saw_yield = env.saw_yield;
3957 env.saw_yield = false;
3958 let r = p(node, env);
3959 let saw_yield = env.saw_yield;
3960 env.saw_yield = outer_saw_yield;
3964 fn mk_empty_ns_env(env: &Env<TF>) -> RcOc<NamespaceEnv> {
3965 RcOc::clone(&env.empty_ns_env)
3968 fn extract_docblock(node: S<'a, T, V>, env: &Env<TF>) -> Option<DocComment> {
3969 #[derive(Copy, Clone, Eq, PartialEq)]
3982 // `parse` mixes loop and recursion to use less stack space.
3988 ) -> Option<(usize, usize, String)> {
3989 let is_whitespace = |c| c == ' ' || c == '\t' || c == '\n' || c == '\r';
3990 let mut s = (start, state, idx);
3991 let chars = str.as_bytes();
3993 if s.2 == str.len() {
3998 match (s.1, chars[s.2] as char) {
3999 (LineCmt, '\n') => s = (next, Free, next),
4000 (EndEmbedded, '/') => s = (next, Free, next),
4002 let r = parse(str, next, Free, next);
4004 d @ Some(_) => break d,
4005 None => break Some((s.0, s.2 + 1, String::from(&str[s.0..s.2 + 1]))),
4008 /* PHP has line comments delimited by a # */
4009 (Free, '#') => s = (next, LineCmt, next),
4010 /* All other comment delimiters start with a / */
4011 (Free, '/') => s = (s.2, SawSlash, next),
4012 /* After a / in trivia, we must see either another / or a * */
4013 (SawSlash, '/') => s = (next, LineCmt, next),
4014 (SawSlash, '*') => s = (s.0, MaybeDoc, next),
4015 (MaybeDoc, '*') => s = (s.0, MaybeDoc2, next),
4016 (MaybeDoc, _) => s = (s.0, EmbeddedCmt, next),
4017 (MaybeDoc2, '/') => s = (next, Free, next),
4018 /* Doc comments have a space after the second star */
4019 (MaybeDoc2, c) if is_whitespace(c) => s = (s.0, DocComment, s.2),
4020 (MaybeDoc2, _) => s = (s.0, EmbeddedCmt, next),
4021 (DocComment, '*') => s = (s.0, EndDoc, next),
4022 (DocComment, _) => s = (s.0, DocComment, next),
4023 (EndDoc, _) => s = (s.0, DocComment, next),
4024 /* A * without a / does not end an embedded comment */
4025 (EmbeddedCmt, '*') => s = (s.0, EndEmbedded, next),
4026 (EndEmbedded, '*') => s = (s.0, EndEmbedded, next),
4027 (EndEmbedded, _) => s = (s.0, EmbeddedCmt, next),
4028 /* Whitespace skips everywhere else */
4029 (_, c) if is_whitespace(c) => s = (s.0, s.1, next),
4030 /* When scanning comments, anything else is accepted */
4031 (LineCmt, _) => s = (s.0, s.1, next),
4032 (EmbeddedCmt, _) => s = (s.0, s.1, next),
4037 let str = node.leading_text(env.indexed_source_text.source_text());
4038 parse(str, 0, Free, 0).map(|(start, end, txt)| {
4039 let anchor = node.leading_start_offset();
4041 .indexed_source_text
4042 .relative_pos(anchor + start, anchor + end);
4043 let ps = (pos, txt);
4044 oxidized::doc_comment::DocComment::new(ps)
4048 fn p_xhp_child(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::XhpChild> {
4049 use ast::XhpChild::*;
4050 use ast::XhpChildOp::*;
4051 match &node.children {
4052 Token(_) => Self::pos_name(node, env).map(ChildName),
4053 PostfixUnaryExpression(c) => {
4054 let operand = Self::p_xhp_child(&c.operand, env)?;
4055 let operator = match Self::token_kind(&c.operator) {
4056 Some(TK::Question) => ChildQuestion,
4057 Some(TK::Plus) => ChildPlus,
4058 Some(TK::Star) => ChildStar,
4059 _ => Self::missing_syntax("xhp children operator", node, env)?,
4061 Ok(ChildUnary(Box::new(operand), operator))
4063 BinaryExpression(c) => {
4064 let left = Self::p_xhp_child(&c.left_operand, env)?;
4065 let right = Self::p_xhp_child(&c.right_operand, env)?;
4066 Ok(ChildBinary(Box::new(left), Box::new(right)))
4068 XHPChildrenParenthesizedList(c) => {
4069 let children: std::result::Result<Vec<_>, _> = c
4071 .syntax_node_to_list_skip_separator()
4072 .map(|c| Self::p_xhp_child(c, env))
4074 Ok(ChildList(children?))
4076 _ => Self::missing_syntax("xhp children", node, env),
4081 class: &mut ast::Class_,
4083 env: &mut Env<'a, TF>,
4085 use ast::Visibility;
4086 let doc_comment_opt = Self::extract_docblock(node, env);
4087 let has_fun_header = |m: &MethodishDeclarationChildren<T, V>| {
4089 m.function_decl_header.children,
4090 FunctionDeclarationHeader(_)
4093 let p_method_vis = |
4096 env: &mut Env<'a, TF>,
4097 | -> Result<Visibility> {
4098 match Self::p_visibility_last_win(node, env)? {
4100 Self::raise_hh_error(env, Naming::method_needs_visibility(name_pos.clone()));
4101 Ok(Visibility::Public)
4106 match &node.children {
4107 ConstDeclaration(c) => {
4108 // TODO: make wrap `type_` `doc_comment` by `Rc` in ClassConst to avoid clone
4109 let type_ = Self::mp_optional(Self::p_hint, &c.type_specifier, env)?;
4110 // using map_fold can save one Vec allocation, but ocaml's behavior is that
4111 // if anything throw, it will discard all lowered elements. So adding to class
4112 // must be at the last.
4113 let mut class_consts = Self::could_map(
4114 |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::ClassConst> {
4116 ConstantDeclarator(c) => {
4117 let id = Self::pos_name(&c.name, e)?;
4118 let expr = if n.is_abstract() {
4122 Self::p_simple_initializer,
4127 Ok(ast::ClassConst {
4128 type_: type_.clone(),
4131 doc_comment: doc_comment_opt.clone(),
4134 _ => Self::missing_syntax("constant declarator", n, e),
4140 Ok(class.consts.append(&mut class_consts))
4142 TypeConstDeclaration(c) => {
4143 if !c.type_parameters.is_missing() {
4144 Self::raise_parsing_error(node, env, &syntax_error::tparams_in_tconst);
4146 let user_attributes = Self::p_user_attributes(&c.attribute_spec, env)?;
4147 let type__ = Self::mp_optional(Self::p_hint, &c.type_specifier, env)?
4148 .map(|hint| Self::soften_hint(&user_attributes, hint));
4149 let kinds = Self::p_kinds(&c.modifiers, env)?;
4150 let name = Self::pos_name(&c.name, env)?;
4152 Self::mp_optional(Self::p_tconstraint_ty, &c.type_constraint, env)?;
4153 let span = Self::p_pos(node, env);
4154 let has_abstract = kinds.has(modifier::ABSTRACT);
4155 let (type_, abstract_kind) = match (has_abstract, &constraint, &type__) {
4156 (false, _, None) => {
4157 Self::raise_hh_error(
4159 NastCheck::not_abstract_without_typeconst(name.0.clone()),
4161 (constraint.clone(), ast::TypeconstAbstractKind::TCConcrete)
4163 (false, None, Some(_)) => (type__, ast::TypeconstAbstractKind::TCConcrete),
4164 (false, Some(_), Some(_)) => {
4165 (type__, ast::TypeconstAbstractKind::TCPartiallyAbstract)
4167 (true, _, None) => (
4169 ast::TypeconstAbstractKind::TCAbstract(type__),
4171 (true, _, Some(_)) => (None, ast::TypeconstAbstractKind::TCAbstract(type__)),
4173 Ok(class.typeconsts.push(ast::ClassTypeconst {
4174 abstract_: abstract_kind,
4180 doc_comment: doc_comment_opt,
4184 ContextConstDeclaration(c) => {
4185 if !c.type_parameters.is_missing() {
4186 Self::raise_parsing_error(node, env, &syntax_error::tparams_in_tconst);
4188 let name = Self::pos_name(&c.name, env)?;
4189 let context = Self::p_context_list_to_intersection(&c.ctx_list, env)?;
4190 let constraints = Self::could_map(
4191 |cstrnt, e: &mut Env<'a, TF>| match &cstrnt.children {
4192 Missing => Ok(None),
4193 TypeConstraint(c) => Self::p_context_list_to_intersection(&c.type_, e),
4194 _ => Self::missing_syntax("const ctx constraint", node, e),
4201 // so it turns out that type constant and context constant
4202 // constraints super don't jive.
4203 // 1. type constants allow only 1 constraint
4204 // 2. type constants don't record the *type* of the constraint
4205 // these both need to be fixed or we need to lower to something
4206 // else in the meantime, just grab the first one if it exists
4207 let constraint = constraints.into_iter().next().flatten();
4209 let span = Self::p_pos(node, env);
4210 let kinds = Self::p_kinds(&c.modifiers, env)?;
4211 let has_abstract = kinds.has(modifier::ABSTRACT);
4212 let (context, abstract_kind) = match (has_abstract, &constraint, &context) {
4213 (false, _, None) => {
4214 Self::raise_hh_error(
4216 NastCheck::not_abstract_without_typeconst(name.0.clone()),
4218 (constraint.clone(), ast::TypeconstAbstractKind::TCConcrete)
4220 (false, None, Some(_)) => (context, ast::TypeconstAbstractKind::TCConcrete),
4221 (false, Some(_), Some(_)) => {
4222 let errstr = "No partially abstract context constants";
4223 Self::raise_parsing_error(node, env, errstr);
4224 return Err(Error::Failwith(errstr.to_string()));
4226 (true, _, None) => (
4228 ast::TypeconstAbstractKind::TCAbstract(context),
4230 (true, _, Some(_)) => (None, ast::TypeconstAbstractKind::TCAbstract(context)),
4232 Ok(class.typeconsts.push(ast::ClassTypeconst {
4233 abstract_: abstract_kind,
4237 user_attributes: vec![],
4239 doc_comment: doc_comment_opt,
4243 PropertyDeclaration(c) => {
4244 let user_attributes = Self::p_user_attributes(&c.attribute_spec, env)?;
4245 let type_ = Self::mp_optional(Self::p_hint, &c.type_, env)?
4246 .map(|t| Self::soften_hint(&user_attributes, t));
4247 let kinds = Self::p_kinds(&c.modifiers, env)?;
4248 let vis = Self::p_visibility_last_win_or(&c.modifiers, env, Visibility::Public)?;
4249 let doc_comment = if env.quick_mode {
4254 let name_exprs = Self::could_map(
4255 |n, e| -> Result<(Pos, ast::Sid, Option<ast::Expr>)> {
4257 PropertyDeclarator(c) => {
4258 let name = Self::pos_name_(&c.name, e, Some('$'))?;
4259 let pos = Self::p_pos(n, e);
4260 let expr = Self::mp_optional(
4261 Self::p_simple_initializer,
4265 Ok((pos, name, expr))
4267 _ => Self::missing_syntax("property declarator", n, e),
4275 for name_expr in name_exprs.into_iter() {
4276 class.vars.push(ast::ClassVar {
4277 final_: kinds.has(modifier::FINAL),
4279 abstract_: kinds.has(modifier::ABSTRACT),
4281 type_: ast::TypeHint((), type_.clone()),
4284 user_attributes: user_attributes.clone(),
4285 doc_comment: if i == 0 { doc_comment.clone() } else { None },
4286 is_promoted_variadic: false,
4287 is_static: kinds.has(modifier::STATIC),
4294 MethodishDeclaration(c) if has_fun_header(c) => {
4295 let classvar_init = |param: &ast::FunParam| -> (ast::Stmt, ast::ClassVar) {
4296 let cvname = Self::drop_prefix(¶m.name, '$');
4298 let span = match ¶m.expr {
4299 Some(ast::Expr(pos_end, _)) => {
4300 Pos::btw(p, pos_end).unwrap_or_else(|_| p.clone())
4304 let e = |expr_: ast::Expr_| -> ast::Expr { ast::Expr::new(p.clone(), expr_) };
4305 let lid = |s: &str| -> ast::Lid { ast::Lid(p.clone(), (0, s.to_string())) };
4309 ast::Stmt_::mk_expr(e(E_::mk_binop(
4312 e(E_::mk_lvar(lid(special_idents::THIS))),
4313 e(E_::mk_id(ast::Id(p.clone(), cvname.to_string()))),
4314 ast::OgNullFlavor::OGNullthrows,
4317 e(E_::mk_lvar(lid(¶m.name))),
4324 visibility: param.visibility.unwrap(),
4325 type_: param.type_hint.clone(),
4326 id: ast::Id(p.clone(), cvname.to_string()),
4328 user_attributes: param.user_attributes.clone(),
4330 is_promoted_variadic: param.is_variadic,
4336 let header = &c.function_decl_header;
4337 let h = match &header.children {
4338 FunctionDeclarationHeader(h) => h,
4341 let hdr = Self::p_fun_hdr(header, env)?;
4342 let (mut member_init, mut member_def): (Vec<ast::Stmt>, Vec<ast::ClassVar>) = hdr
4345 .filter_map(|p| p.visibility.map(|_| classvar_init(p)))
4348 let kinds = Self::p_kinds(&h.modifiers, env)?;
4349 let visibility = p_method_vis(&h.modifiers, &hdr.name.0, env)?;
4350 let is_static = kinds.has(modifier::STATIC);
4351 *env.in_static_method() = is_static;
4352 let (mut body, body_has_yield) =
4353 Self::mp_yielding(Self::p_function_body, &c.function_body, env)?;
4355 member_init.reverse();
4357 member_init.append(&mut body);
4358 let body = member_init;
4359 *env.in_static_method() = false;
4360 let is_abstract = kinds.has(modifier::ABSTRACT);
4361 let is_external = !is_abstract && c.function_body.is_external();
4362 let user_attributes = Self::p_user_attributes(&c.attribute, env)?;
4363 Self::check_effect_polymorphic_memoized(
4369 let method = ast::Method_ {
4370 span: Self::p_fun_pos(node, env),
4372 final_: kinds.has(modifier::FINAL),
4373 abstract_: is_abstract,
4377 tparams: hdr.type_parameters,
4378 where_constraints: hdr.constrs,
4379 variadic: Self::determine_variadicity(&hdr.parameters),
4380 params: hdr.parameters,
4382 unsafe_ctxs: hdr.unsafe_contexts,
4383 body: ast::FuncBody {
4387 fun_kind: Self::mk_fun_kind(hdr.suspension_kind, body_has_yield),
4389 ret: ast::TypeHint((), hdr.return_type),
4390 external: is_external,
4391 doc_comment: doc_comment_opt,
4393 class.vars.append(&mut member_def);
4394 Ok(class.methods.push(method))
4396 TraitUseConflictResolution(c) => {
4397 type Ret = Result<Either<ast::InsteadofAlias, ast::UseAsAlias>>;
4398 let p_item = |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Ret {
4400 TraitUsePrecedenceItem(c) => {
4401 let (qualifier, name) = match &c.name.children {
4402 ScopeResolutionExpression(c) => (
4403 Self::pos_name(&c.qualifier, e)?,
4404 Self::p_pstring(&c.name, e)?,
4406 _ => Self::missing_syntax("trait use precedence item", n, e)?,
4409 Self::could_map(Self::pos_name, &c.removed_names, e)?;
4410 Self::raise_hh_error(e, Naming::unsupported_instead_of(name.0.clone()));
4411 Ok(Either::Left(ast::InsteadofAlias(
4417 TraitUseAliasItem(c) => {
4418 let (qualifier, name) = match &c.aliasing_name.children {
4419 ScopeResolutionExpression(c) => (
4420 Some(Self::pos_name(&c.qualifier, e)?),
4421 Self::p_pstring(&c.name, e)?,
4423 _ => (None, Self::p_pstring(&c.aliasing_name, e)?),
4425 let (kinds, mut vis_raw) = Self::p_modifiers(
4426 |mut acc, kind| -> Vec<ast::UseAsVisibility> {
4427 if let Some(v) = modifier::to_use_as_visibility(kind) {
4436 let vis = if kinds.is_empty() || kinds.has_any(modifier::VISIBILITIES) {
4439 let mut v = vec![ast::UseAsVisibility::UseAsPublic];
4440 v.append(&mut vis_raw);
4443 let aliased_name = if !c.aliased_name.is_missing() {
4444 Some(Self::pos_name(&c.aliased_name, e)?)
4448 Self::raise_hh_error(
4450 Naming::unsupported_trait_use_as(name.0.clone()),
4452 Ok(Either::Right(ast::UseAsAlias(
4459 _ => Self::missing_syntax("trait use conflict resolution item", n, e),
4462 let mut uses = Self::could_map(Self::p_hint, &c.names, env)?;
4463 let elts = Self::could_map(p_item, &c.clauses, env)?;
4464 class.uses.append(&mut uses);
4465 for elt in elts.into_iter() {
4467 Either::Left(l) => class.insteadof_alias.push(l),
4468 Either::Right(r) => class.use_as_alias.push(r),
4474 let mut uses = Self::could_map(Self::p_hint, &c.names, env)?;
4475 Ok(class.uses.append(&mut uses))
4477 RequireClause(c) => {
4478 let hint = Self::p_hint(&c.name, env)?;
4479 let is_extends = match Self::token_kind(&c.kind) {
4480 Some(TK::Implements) => false,
4481 Some(TK::Extends) => true,
4482 _ => Self::missing_syntax("trait require kind", &c.kind, env)?,
4484 Ok(class.reqs.push((hint, is_extends)))
4486 XHPClassAttributeDeclaration(c) => {
4487 type Ret = Result<Either<ast::XhpAttr, ast::Hint>>;
4488 let p_attr = |node: S<'a, T, V>, env: &mut Env<'a, TF>| -> Ret {
4489 let mk_attr_use = |n: S<'a, T, V>, env: &mut Env<'a, TF>| {
4490 Ok(Either::Right(ast::Hint(
4491 Self::p_pos(n, env),
4492 Box::new(ast::Hint_::Happly(Self::pos_name(n, env)?, vec![])),
4495 match &node.children {
4496 XHPClassAttribute(c) => {
4497 let ast::Id(p, name) = Self::pos_name(&c.name, env)?;
4498 if let TypeConstant(_) = &c.type_.children {
4499 if env.is_typechecker() {
4500 Self::raise_parsing_error(
4503 &syntax_error::xhp_class_attribute_type_constant,
4507 let req = match &c.required.children {
4508 XHPRequired(_) => Some(ast::XhpAttrTag::Required),
4509 XHPLateinit(_) => Some(ast::XhpAttrTag::LateInit),
4512 let pos = if c.initializer.is_missing() {
4515 Pos::btw(&p, &Self::p_pos(&c.initializer, env))
4516 .map_err(Error::Failwith)?
4518 let (hint, enum_) = match &c.type_.children {
4519 XHPEnumType(c1) => {
4520 let p = Self::p_pos(&c.type_, env);
4521 let vals = Self::could_map(Self::p_expr, &c1.values, env)?;
4522 (None, Some((p, vals)))
4524 _ => (Some(Self::p_hint(&c.type_, env)?), None),
4527 Self::mp_optional(Self::p_simple_initializer, &c.initializer, env)?;
4528 let xhp_attr = ast::XhpAttr(
4529 ast::TypeHint((), hint.clone()),
4532 xhp_attr: Some(ast::XhpAttrInfo { xai_tag: req }),
4534 visibility: ast::Visibility::Public,
4535 type_: ast::TypeHint((), hint),
4536 id: ast::Id(p, String::from(":") + &name),
4538 user_attributes: vec![],
4540 is_promoted_variadic: false,
4547 Ok(Either::Left(xhp_attr))
4549 XHPSimpleClassAttribute(c) => mk_attr_use(&c.type_, env),
4550 Token(_) => mk_attr_use(node, env),
4551 _ => Self::missing_syntax("XHP attribute", node, env),
4554 let attrs = Self::could_map(p_attr, &c.attributes, env)?;
4555 for attr in attrs.into_iter() {
4557 Either::Left(attr) => class.xhp_attrs.push(attr),
4558 Either::Right(xhp_attr_use) => class.xhp_attr_uses.push(xhp_attr_use),
4563 XHPChildrenDeclaration(c) => {
4564 let p = Self::p_pos(node, env);
4567 .push((p, Self::p_xhp_child(&c.expression, env)?)))
4569 XHPCategoryDeclaration(c) => {
4570 let p = Self::p_pos(node, env);
4572 Self::could_map(|n, e| Self::p_pstring_(n, e, Some('%')), &c.categories, env)?;
4573 if let Some((_, cs)) = &class.xhp_category {
4574 if let Some(category) = cs.first() {
4575 Self::raise_hh_error(
4577 NastCheck::multiple_xhp_category(category.0.clone()),
4581 Ok(class.xhp_category = Some((p, categories)))
4583 _ => Self::missing_syntax("class element", node, env),
4588 class: &mut ast::Class_,
4590 env: &mut Env<'a, TF>,
4592 let r = Self::p_class_elt_(class, node, env);
4594 // match ocaml behavior, don't throw if missing syntax when fail_open is true
4595 Err(Error::MissingSyntax { .. }) if env.fail_open() => Ok(()),
4600 fn contains_class_body(c: &ClassishDeclarationChildren<T, V>) -> bool {
4601 matches!(&c.body.children, ClassishBody(_))
4604 fn p_where_constraint(
4606 parent: S<'a, T, V>,
4608 env: &mut Env<'a, TF>,
4609 ) -> Result<Vec<ast::WhereConstraintHint>> {
4610 match &node.children {
4611 Missing => Ok(vec![]),
4613 let f = |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::WhereConstraintHint> {
4615 WhereConstraint(c) => {
4616 use ast::ConstraintKind::*;
4617 let l = Self::p_hint(&c.left_type, e)?;
4618 let o = &c.operator;
4619 let o = match Self::token_kind(o) {
4620 Some(TK::Equal) => ConstraintEq,
4621 Some(TK::As) => ConstraintAs,
4622 Some(TK::Super) => ConstraintSuper,
4623 _ => Self::missing_syntax("constraint operator", o, e)?,
4625 Ok(ast::WhereConstraintHint(
4628 Self::p_hint(&c.right_type, e)?,
4631 _ => Self::missing_syntax("where constraint", n, e),
4635 .syntax_node_to_list_skip_separator()
4641 Self::missing_syntax("classish declaration constraints", parent, env)
4643 Self::missing_syntax("function header constraints", parent, env)
4649 fn p_namespace_use_kind(kind: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::NsKind> {
4651 match &kind.children {
4652 Missing => Ok(NSClassAndNamespace),
4653 _ => match Self::token_kind(kind) {
4654 Some(TK::Namespace) => Ok(NSNamespace),
4655 Some(TK::Type) => Ok(NSClass),
4656 Some(TK::Function) => Ok(NSFun),
4657 Some(TK::Const) => Ok(NSConst),
4658 _ => Self::missing_syntax("namespace use kind", kind, env),
4663 fn p_namespace_use_clause(
4664 prefix: Option<S<'a, T, V>>,
4665 kind: Result<ast::NsKind>,
4667 env: &mut Env<'a, TF>,
4668 ) -> Result<(ast::NsKind, ast::Sid, ast::Sid)> {
4670 static ref NAMESPACE_USE: regex::Regex = regex::Regex::new("[^\\\\]*$").unwrap();
4673 match &node.children {
4674 NamespaceUseClause(NamespaceUseClauseChildren {
4680 let ast::Id(p, n) = match (prefix, Self::pos_name(name, env)?) {
4682 (Some(prefix), ast::Id(p, n)) => {
4683 ast::Id(p, Self::pos_name(prefix, env)?.1 + &n)
4686 let alias = if alias.is_missing() {
4687 let x = NAMESPACE_USE.find(&n).unwrap().as_str();
4688 ast::Id(p.clone(), x.to_string())
4690 Self::pos_name(alias, env)?
4692 let kind = if clause_kind.is_missing() {
4695 Self::p_namespace_use_kind(clause_kind, env)
4701 if n.len() > 0 && n.chars().nth(0) == Some('\\') {
4704 String::from("\\") + &n
4710 _ => Self::missing_syntax("namespace use clause", node, env),
4714 fn check_effect_polymorphic_memoized(
4715 contexts: &Option<ast::Contexts>,
4716 user_attributes: &[aast::UserAttribute<Pos, (), (), ()>],
4718 env: &mut Env<'a, TF>,
4720 if Self::has_polymorphic_context(contexts) {
4721 if let Some(u) = user_attributes
4723 .find(|u| u.name.1 == naming_special_names_rust::user_attributes::MEMOIZE)
4725 Self::raise_parsing_error_pos(
4728 &syntax_error::effect_polymorphic_memoized(kind),
4734 fn p_def(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<Vec<ast::Def>> {
4735 let doc_comment_opt = Self::extract_docblock(node, env);
4736 match &node.children {
4737 FunctionDeclaration(FunctionDeclarationChildren {
4742 let mut env = Env::clone_and_unset_toplevel_if_toplevel(env);
4743 let env = env.as_mut();
4744 let hdr = Self::p_fun_hdr(declaration_header, env)?;
4745 let is_external = body.is_external();
4746 let (block, yield_) = if is_external {
4749 Self::mp_yielding(&Self::p_function_body, body, env)?
4751 let user_attributes = Self::p_user_attributes(attribute_spec, env)?;
4752 Self::check_effect_polymorphic_memoized(
4758 let variadic = Self::determine_variadicity(&hdr.parameters);
4759 let ret = ast::TypeHint((), hdr.return_type);
4760 Ok(vec![ast::Def::mk_fun(ast::Fun_ {
4761 span: Self::p_fun_pos(node, env),
4763 mode: env.file_mode(),
4766 tparams: hdr.type_parameters,
4767 where_constraints: hdr.constrs,
4768 params: hdr.parameters,
4770 unsafe_ctxs: hdr.unsafe_contexts,
4771 body: ast::FuncBody {
4775 fun_kind: Self::mk_fun_kind(hdr.suspension_kind, yield_),
4778 file_attributes: vec![],
4779 external: is_external,
4780 namespace: Self::mk_empty_ns_env(env),
4781 doc_comment: doc_comment_opt,
4785 ClassishDeclaration(c) if Self::contains_class_body(c) => {
4786 let mut env = Env::clone_and_unset_toplevel_if_toplevel(env);
4787 let env = env.as_mut();
4788 let mode = env.file_mode();
4789 let user_attributes = Self::p_user_attributes(&c.attribute, env)?;
4790 let kinds = Self::p_kinds(&c.modifiers, env)?;
4791 let final_ = kinds.has(modifier::FINAL);
4792 let is_xhp = matches!(
4793 Self::token_kind(&c.name),
4794 Some(TK::XHPElementName) | Some(TK::XHPClassName)
4796 let has_xhp_keyword = matches!(Self::token_kind(&c.xhp), Some(TK::XHP));
4797 let name = Self::pos_name(&c.name, env)?;
4798 *env.cls_reified_generics() = HashSet::new();
4799 let tparams = Self::p_tparam_l(true, &c.type_parameters, env)?;
4800 let class_kind = match Self::token_kind(&c.keyword) {
4801 Some(TK::Class) if kinds.has(modifier::ABSTRACT) => ast::ClassKind::Cabstract,
4802 Some(TK::Class) => ast::ClassKind::Cnormal,
4803 Some(TK::Interface) => ast::ClassKind::Cinterface,
4804 Some(TK::Trait) => ast::ClassKind::Ctrait,
4805 Some(TK::Enum) => ast::ClassKind::Cenum,
4806 _ => Self::missing_syntax("class kind", &c.keyword, env)?,
4808 let filter_dynamic =
4809 |node: S<'a, T, V>, env: &mut Env<'a, TF>| -> Result<Option<ast::Hint>> {
4810 match Self::p_hint(node, env) {
4812 Ok(h) => match &*h.1 {
4813 oxidized::aast_defs::Hint_::Happly(oxidized::ast::Id(_, id), _) => {
4814 if id == "dynamic" {
4824 let (extends, extends_dynamic) = match class_kind {
4825 ast::ClassKind::Cinterface if env.parser_options.tco_enable_sound_dynamic => {
4826 Self::could_map_filter(filter_dynamic, &c.extends_list, env)?
4828 _ => (Self::could_map(Self::p_hint, &c.extends_list, env)?, false),
4830 *env.parent_maybe_reified() = match extends.first().map(|h| h.1.as_ref()) {
4831 Some(ast::Hint_::Happly(_, hl)) => !hl.is_empty(),
4834 let (implements, implements_dynamic) =
4835 if env.parser_options.tco_enable_sound_dynamic {
4836 Self::could_map_filter(filter_dynamic, &c.implements_list, env)?
4839 Self::could_map(Self::p_hint, &c.implements_list, env)?,
4844 let where_constraints = Self::p_where_constraint(true, node, &c.where_clause, env)?;
4845 let namespace = Self::mk_empty_ns_env(env);
4846 let span = Self::p_pos(node, env);
4847 let mut class_ = ast::Class_ {
4859 use_as_alias: vec![],
4860 insteadof_alias: vec![],
4861 xhp_attr_uses: vec![],
4865 implements_dynamic: implements_dynamic || extends_dynamic,
4871 // TODO: what is this attbiute? check ast_to_aast
4873 xhp_children: vec![],
4877 file_attributes: vec![],
4879 doc_comment: doc_comment_opt,
4882 match &c.body.children {
4883 ClassishBody(c1) => {
4884 for elt in c1.elements.syntax_node_to_list_skip_separator() {
4885 Self::p_class_elt(&mut class_, elt, env)?;
4888 _ => Self::missing_syntax("classish body", &c.body, env)?,
4890 Ok(vec![ast::Def::mk_class(class_)])
4892 ConstDeclaration(c) => {
4893 let ty = &c.type_specifier;
4894 let decls = c.declarators.syntax_node_to_list_skip_separator();
4895 let mut defs = vec![];
4897 let def = match &decl.children {
4898 ConstantDeclarator(c) => {
4900 let init = &c.initializer;
4901 let gconst = ast::Gconst {
4903 mode: env.file_mode(),
4904 name: Self::pos_name(name, env)?,
4905 type_: Self::mp_optional(Self::p_hint, ty, env)?,
4906 value: Self::p_simple_initializer(init, env)?,
4907 namespace: Self::mk_empty_ns_env(env),
4908 span: Self::p_pos(node, env),
4911 ast::Def::mk_constant(gconst)
4913 _ => Self::missing_syntax("constant declaration", decl, env)?,
4919 AliasDeclaration(c) => {
4920 let tparams = Self::p_tparam_l(false, &c.generic_parameter, env)?;
4921 for tparam in tparams.iter() {
4922 if tparam.reified != ast::ReifyKind::Erased {
4923 Self::raise_parsing_error(node, env, &syntax_error::invalid_reified)
4926 Ok(vec![ast::Def::mk_typedef(ast::Typedef {
4928 name: Self::pos_name(&c.name, env)?,
4930 constraint: Self::mp_optional(Self::p_tconstraint, &c.constraint, env)?
4932 user_attributes: itertools::concat(
4934 .syntax_node_to_list_skip_separator()
4935 .map(|attr| Self::p_user_attribute(attr, env))
4936 .collect::<std::result::Result<Vec<Vec<_>>, _>>()?,
4938 namespace: Self::mk_empty_ns_env(env),
4939 mode: env.file_mode(),
4940 vis: match Self::token_kind(&c.keyword) {
4941 Some(TK::Type) => ast::TypedefVisibility::Transparent,
4942 Some(TK::Newtype) => ast::TypedefVisibility::Opaque,
4943 _ => Self::missing_syntax("kind", &c.keyword, env)?,
4945 kind: Self::p_hint(&c.type_, env)?,
4946 span: Self::p_pos(node, env),
4950 EnumDeclaration(c) => {
4952 |n: S<'a, T, V>, e: &mut Env<'a, TF>| -> Result<ast::ClassConst> {
4954 Enumerator(c) => Ok(ast::ClassConst {
4956 id: Self::pos_name(&c.name, e)?,
4957 expr: Some(Self::p_expr(&c.value, e)?),
4960 _ => Self::missing_syntax("enumerator", n, e),
4963 Ok(vec![ast::Def::mk_class(ast::Class_ {
4965 mode: env.file_mode(),
4966 user_attributes: Self::p_user_attributes(&c.attribute_spec, env)?,
4967 file_attributes: vec![],
4969 kind: ast::ClassKind::Cenum,
4971 has_xhp_keyword: false,
4972 name: Self::pos_name(&c.name, env)?,
4976 implements_dynamic: false,
4977 where_constraints: vec![],
4978 consts: Self::could_map(p_enumerator, &c.enumerators, env)?,
4979 namespace: Self::mk_empty_ns_env(env),
4980 span: Self::p_pos(node, env),
4981 enum_: Some(ast::Enum_ {
4982 base: Self::p_hint(&c.base, env)?,
4983 constraint: Self::mp_optional(Self::p_tconstraint_ty, &c.type_, env)?,
4984 includes: Self::could_map(Self::p_hint, &c.includes_list, env)?,
4988 doc_comment: doc_comment_opt,
4990 use_as_alias: vec![],
4991 insteadof_alias: vec![],
4992 xhp_attr_uses: vec![],
4999 xhp_children: vec![],
5004 EnumClassDeclaration(c) => {
5005 let name = Self::pos_name(&c.name, env)?;
5006 // Adding __EnumClass
5007 let mut user_attributes = Self::p_user_attributes(&c.attribute_spec, env)?;
5008 let enum_class_attribute = ast::UserAttribute {
5009 name: ast::Id(name.0.clone(), special_attrs::ENUM_CLASS.to_string()),
5012 user_attributes.push(enum_class_attribute);
5013 // During lowering we store the base type as is. It will be updated during
5015 let base_type = Self::p_hint(&c.base, env)?;
5017 let name_s = name.1.clone(); // TODO: can I avoid this clone ?
5019 // Helper to build X -> HH\MemberOf<enum_name, X>
5020 let build_elt = |p: Pos, ty: ast::Hint| -> ast::Hint {
5021 let enum_name = ast::Id(p.clone(), name_s.clone());
5022 let enum_class = ast::Hint_::mk_happly(enum_name, vec![]);
5023 let enum_class = ast::Hint::new(p.clone(), enum_class);
5024 let elt_id = ast::Id(p.clone(), special_classes::MEMBER_OF.to_string());
5025 let full_type = ast::Hint_::mk_happly(elt_id, vec![enum_class, ty]);
5026 ast::Hint::new(p, full_type)
5029 let extends = Self::could_map(Self::p_hint, &c.extends_list, env)?;
5031 let mut enum_class = ast::Class_ {
5033 mode: env.file_mode(),
5035 file_attributes: vec![],
5036 final_: false, // TODO(T77095784): support final EDTs
5037 kind: ast::ClassKind::Cenum,
5039 has_xhp_keyword: false,
5042 extends: extends.clone(),
5044 implements_dynamic: false,
5045 where_constraints: vec![],
5047 namespace: Self::mk_empty_ns_env(env),
5048 span: Self::p_pos(node, env),
5049 enum_: Some(ast::Enum_ {
5055 doc_comment: doc_comment_opt,
5057 use_as_alias: vec![],
5058 insteadof_alias: vec![],
5059 xhp_attr_uses: vec![],
5066 xhp_children: vec![],
5071 for n in c.elements.syntax_node_to_list_skip_separator() {
5073 // TODO(T77095784): check pos and span usage
5074 EnumClassEnumerator(c) => {
5076 // - type name = expression;
5078 // - const MemberOf<enum_name, type> name = expression
5079 let name = Self::pos_name(&c.name, env)?;
5081 let elt_type = Self::p_hint(&c.type_, env)?;
5082 let full_type = build_elt(pos.clone(), elt_type);
5083 let initial_value = Self::p_expr(&c.initial_value, env)?;
5084 let class_const = ast::ClassConst {
5085 type_: Some(full_type),
5087 expr: Some(initial_value),
5090 enum_class.consts.push(class_const)
5093 let pos = Self::p_pos(n, env);
5094 Self::raise_parsing_error_pos(
5097 &syntax_error::invalid_enum_class_enumerator,
5102 Ok(vec![ast::Def::mk_class(enum_class)])
5104 RecordDeclaration(c) => {
5105 let p_field = |n: S<'a, T, V>, e: &mut Env<'a, TF>| match &n.children {
5106 RecordField(c) => Ok((
5107 Self::pos_name(&c.name, e)?,
5108 Self::p_hint(&c.type_, e)?,
5109 Self::mp_optional(Self::p_simple_initializer, &c.init, e)?,
5111 _ => Self::missing_syntax("record_field", n, e),
5113 Ok(vec![ast::Def::mk_record_def(ast::RecordDef {
5115 name: Self::pos_name(&c.name, env)?,
5116 extends: Self::could_map(Self::p_hint, &c.extends_opt, env)?
5119 abstract_: Self::token_kind(&c.modifier) == Some(TK::Abstract),
5120 user_attributes: Self::p_user_attributes(&c.attribute_spec, env)?,
5121 fields: Self::could_map(p_field, &c.fields, env)?,
5122 namespace: Self::mk_empty_ns_env(env),
5123 span: Self::p_pos(node, env),
5124 doc_comment: doc_comment_opt,
5128 InclusionDirective(c) if env.file_mode() != file_info::Mode::Mdecl || env.codegen() => {
5129 let expr = Self::p_expr(&c.expression, env)?;
5130 Ok(vec![ast::Def::mk_stmt(ast::Stmt::new(
5131 Self::p_pos(node, env),
5132 ast::Stmt_::mk_expr(expr),
5135 NamespaceDeclaration(c) => {
5136 let name = if let NamespaceDeclarationHeader(h) = &c.header.children {
5139 return Self::missing_syntax("namespace_declaration_header", node, env);
5141 let defs = match &c.body.children {
5142 NamespaceBody(c) => {
5143 let mut env1 = Env::clone_and_unset_toplevel_if_toplevel(env);
5144 let env1 = env1.as_mut();
5147 .syntax_node_to_list_skip_separator()
5148 .map(|n| Self::p_def(n, env1))
5149 .collect::<std::result::Result<Vec<Vec<_>>, _>>()?,
5154 Ok(vec![ast::Def::mk_namespace(
5155 Self::pos_name(name, env)?,
5159 NamespaceGroupUseDeclaration(c) => {
5160 let uses: std::result::Result<Vec<_>, _> = c
5162 .syntax_node_to_list_skip_separator()
5164 Self::p_namespace_use_clause(
5166 Self::p_namespace_use_kind(&c.kind, env),
5172 Ok(vec![ast::Def::mk_namespace_use(uses?)])
5174 NamespaceUseDeclaration(c) => {
5175 let uses: std::result::Result<Vec<_>, _> = c
5177 .syntax_node_to_list_skip_separator()
5179 Self::p_namespace_use_clause(
5181 Self::p_namespace_use_kind(&c.kind, env),
5187 Ok(vec![ast::Def::mk_namespace_use(uses?)])
5189 FileAttributeSpecification(_) => {
5190 Ok(vec![ast::Def::mk_file_attributes(ast::FileAttribute {
5191 user_attributes: Self::p_user_attribute(node, env)?,
5192 namespace: Self::mk_empty_ns_env(env),
5195 _ if env.file_mode() == file_info::Mode::Mdecl => Ok(vec![]),
5196 _ => Ok(vec![ast::Def::mk_stmt(Self::p_stmt(node, env)?)]),
5200 fn post_process(env: &mut Env<'a, TF>, program: ast::Program, acc: &mut ast::Program) {
5201 use aast::{Def, Def::*, Stmt_::*};
5202 let mut saw_ns: Option<(ast::Sid, ast::Program)> = None;
5203 for def in program.into_iter() {
5204 if let Namespace(_) = &def {
5205 if let Some((n, ns_acc)) = saw_ns {
5206 acc.push(Def::mk_namespace(n, ns_acc));
5211 if let Namespace(ns) = def {
5212 let (n, defs) = *ns;
5213 if defs.is_empty() {
5214 saw_ns = Some((n, vec![]));
5216 let mut acc_ = vec![];
5217 Self::post_process(env, defs, &mut acc_);
5218 acc.push(Def::mk_namespace(n, acc_));
5224 if let Stmt(s) = &def {
5228 let raise_error = match &s.1 {
5231 if expr.as_ref().is_import()
5232 && !env.parser_options.po_disallow_toplevel_requires =>
5237 use file_info::Mode::*;
5238 let mode = env.file_mode();
5240 && env.is_typechecker()
5242 || (mode == Mpartial
5245 .error_codes_treated_strictly
5250 Self::raise_parsing_error_pos(&s.0, env, &syntax_error::toplevel_statements);
5254 if let Some((_, ns_acc)) = &mut saw_ns {
5260 if let Some((n, defs)) = saw_ns {
5261 acc.push(Def::mk_namespace(n, defs));
5265 fn p_program(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Program> {
5266 let nodes = node.syntax_node_to_list_skip_separator();
5267 let mut acc = vec![];
5270 EndOfFile(_) => break,
5271 _ => match Self::p_def(n, env) {
5272 Err(Error::MissingSyntax { .. }) if env.fail_open => {}
5273 e @ Err(_) => return e,
5274 Ok(mut def) => acc.append(&mut def),
5278 let mut program = vec![];
5279 Self::post_process(env, acc, &mut program);
5283 fn p_script(node: S<'a, T, V>, env: &mut Env<'a, TF>) -> Result<ast::Program> {
5284 match &node.children {
5285 Script(c) => Self::p_program(&c.declarations, env),
5286 _ => Self::missing_syntax("script", node, env),
5291 env: &mut Env<'a, TF>,
5292 script: S<'a, T, V>,
5293 ) -> std::result::Result<ast::Program, String> {
5294 Self::p_script(script, env).map_err(|e| match e {
5295 Error::MissingSyntax {
5301 "missing case in {:?}.\n - pos: {:?}\n - unexpected: '{:?}'\n - kind: {:?}\n",
5302 expecting.to_string(),
5304 node_name.to_string(),
5307 Error::Failwith(msg) => msg,
5312 struct PositionedSyntaxLowerer;
5313 impl<'a> Lowerer<'a, PositionedToken<'a>, PositionedValue<'a>, PositionedTokenFactory<'a>>
5314 for PositionedSyntaxLowerer
5319 env: &mut Env<'a, PositionedTokenFactory<'a>>,
5320 script: S<'a, PositionedToken<'a>, PositionedValue<'a>>,
5321 ) -> std::result::Result<ast::Program, String> {
5322 PositionedSyntaxLowerer::lower(env, script)