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::{BString, B};
11 use hash::{HashMap, HashSet};
12 use itertools::{Either, Itertools};
13 use lint_rust::LintError;
14 use naming_special_names_rust::{
15 classes as special_classes, literal, special_functions, special_idents,
16 typehints as special_typehints, user_attributes as special_attrs,
18 use ocaml_helper::{int_of_string_opt, parse_int, ParseIntError};
19 use ocamlrep::rc::RcOc;
22 aast_visitor::{AstParams, Node, Visitor},
25 doc_comment::DocComment,
26 errors::{Error as HHError, Naming, NastCheck},
28 global_options::GlobalOptions,
29 namespace_env::Env as NamespaceEnv,
32 use parser_core_types::{
33 indexed_source_text::IndexedSourceText,
34 lexable_token::{LexablePositionedToken, LexableToken},
35 source_text::SourceText,
36 syntax::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) -> Result<BString, escaper::InvalidString> {
59 Ok(escaper::unescape_single(s)?.into())
62 fn unescape_nowdoc(s: &str) -> 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,
93 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
94 pub enum SuspensionKind {
99 #[derive(Copy, Clone, Eq, PartialEq)]
109 suspension_kind: SuspensionKind,
110 readonly_this: Option<ast::ReadonlyKind>,
112 constrs: Vec<ast::WhereConstraintHint>,
113 type_parameters: Vec<ast::Tparam>,
114 parameters: Vec<ast::FunParam>,
115 contexts: Option<ast::Contexts>,
116 unsafe_contexts: Option<ast::Contexts>,
117 readonly_return: Option<ast::ReadonlyKind>,
118 return_type: Option<ast::Hint>,
122 fn make_empty(env: &Env<'_>) -> Self {
124 suspension_kind: SuspensionKind::SKSync,
126 name: ast::Id(env.mk_none_pos(), String::from("<ANONYMOUS>")),
128 type_parameters: vec![],
131 unsafe_contexts: None,
132 readonly_return: None,
140 // bool represents reification
141 pub cls_generics: HashMap<String, bool>,
142 // fn_generics also used for methods; maps are separate due to shadowing
143 pub fn_generics: HashMap<String, bool>,
144 pub in_static_method: bool,
145 pub parent_maybe_reified: bool,
146 /// This provides a generic mechanism to delay raising parsing errors;
147 /// since we're moving FFP errors away from CST to a stage after lowering
148 /// _and_ want to prioritize errors before lowering, the lowering errors
149 /// must be merely stored when the lowerer runs (until check for FFP runs (on AST)
150 /// and raised _after_ FFP error checking (unless we run the lowerer twice,
151 /// which would be expensive).
152 pub lowpri_errors: Vec<(Pos, String)>,
153 /// hh_errors captures errors after parsing, naming, nast, etc.
154 pub hh_errors: Vec<HHError>,
155 pub lint_errors: Vec<LintError>,
156 pub doc_comments: Vec<Option<DocComment>>,
158 pub local_id_counter: isize,
160 // TODO(hrust): this check is to avoid crash in Ocaml.
161 // Remove it after all Ocaml callers are eliminated.
162 pub exp_recursion_depth: usize,
165 const EXP_RECUSION_LIMIT: usize = 30_000;
170 pub keep_errors: bool,
172 /// Show errors even in quick mode. Does not override keep_errors. Hotfix
173 /// until we can properly set up saved states to surface parse errors during
174 /// typechecking properly.
175 pub show_all_errors: bool,
177 file_mode: file_info::Mode,
178 pub top_level_statements: bool, /* Whether we are (still) considering TLSs*/
180 // Cache none pos, lazy_static doesn't allow Rc.
182 pub empty_ns_env: RcOc<NamespaceEnv>,
184 pub saw_yield: bool, /* Information flowing back up */
185 pub lifted_awaits: Option<LiftedAwaits>,
186 pub tmp_var_counter: isize,
188 pub indexed_source_text: &'a IndexedSourceText<'a>,
189 pub parser_options: &'a GlobalOptions,
190 pub stack_limit: Option<&'a StackLimit>,
192 pub token_factory: PositionedTokenFactory<'a>,
195 state: Rc<RefCell<State>>,
203 show_all_errors: bool,
205 mode: file_info::Mode,
206 indexed_source_text: &'a IndexedSourceText<'a>,
207 parser_options: &'a GlobalOptions,
208 namespace_env: RcOc<NamespaceEnv>,
209 stack_limit: Option<&'a StackLimit>,
210 token_factory: PositionedTokenFactory<'a>,
220 top_level_statements: true,
226 pos_none: Pos::make_none(),
227 empty_ns_env: namespace_env,
232 state: Rc::new(RefCell::new(State {
233 cls_generics: HashMap::default(),
234 fn_generics: HashMap::default(),
235 in_static_method: false,
236 parent_maybe_reified: false,
237 lowpri_errors: vec![],
238 doc_comments: vec![],
242 exp_recursion_depth: 0,
247 fn file_mode(&self) -> file_info::Mode {
251 fn should_surface_error(&self) -> bool {
252 (!self.quick_mode || self.show_all_errors) && self.keep_errors
255 fn is_typechecker(&self) -> bool {
259 fn codegen(&self) -> bool {
263 fn source_text(&self) -> &SourceText<'a> {
264 self.indexed_source_text.source_text()
267 fn fail_open(&self) -> bool {
271 fn cls_generics_mut(&mut self) -> RefMut<'_, HashMap<String, bool>> {
272 RefMut::map(self.state.borrow_mut(), |s| &mut s.cls_generics)
275 fn fn_generics_mut(&mut self) -> RefMut<'_, HashMap<String, bool>> {
276 RefMut::map(self.state.borrow_mut(), |s| &mut s.fn_generics)
279 pub fn clear_generics(&mut self) {
280 let mut s = self.state.borrow_mut();
281 s.cls_generics = HashMap::default();
282 s.fn_generics = HashMap::default();
285 // avoids returning a reference to the env
286 pub fn get_reification(&self, id: &str) -> Option<bool> {
287 let s = self.state.borrow();
288 if let Some(reif) = s.fn_generics.get(id) {
291 s.cls_generics.get(id).copied()
295 fn in_static_method(&mut self) -> RefMut<'_, bool> {
296 RefMut::map(self.state.borrow_mut(), |s| &mut s.in_static_method)
299 fn parent_maybe_reified(&mut self) -> RefMut<'_, bool> {
300 RefMut::map(self.state.borrow_mut(), |s| &mut s.parent_maybe_reified)
303 pub fn lowpri_errors(&mut self) -> RefMut<'_, Vec<(Pos, String)>> {
304 RefMut::map(self.state.borrow_mut(), |s| &mut s.lowpri_errors)
307 pub fn hh_errors(&mut self) -> RefMut<'_, Vec<HHError>> {
308 RefMut::map(self.state.borrow_mut(), |s| &mut s.hh_errors)
311 pub fn lint_errors(&mut self) -> RefMut<'_, Vec<LintError>> {
312 RefMut::map(self.state.borrow_mut(), |s| &mut s.lint_errors)
315 fn top_docblock(&self) -> Ref<'_, Option<DocComment>> {
316 Ref::map(self.state.borrow(), |s| {
317 s.doc_comments.last().unwrap_or(&None)
321 fn exp_recursion_depth(&self) -> RefMut<'_, usize> {
322 RefMut::map(self.state.borrow_mut(), |s| &mut s.exp_recursion_depth)
325 fn next_local_id(&self) -> isize {
326 let mut id = RefMut::map(self.state.borrow_mut(), |s| &mut s.local_id_counter);
331 fn push_docblock(&mut self, doc_comment: Option<DocComment>) {
332 RefMut::map(self.state.borrow_mut(), |s| &mut s.doc_comments).push(doc_comment)
335 fn pop_docblock(&mut self) {
336 RefMut::map(self.state.borrow_mut(), |s| &mut s.doc_comments).pop();
339 fn make_tmp_var_name(&mut self) -> String {
340 let name = String::from(special_idents::TMP_VAR_PREFIX) + &self.tmp_var_counter.to_string();
341 self.tmp_var_counter += 1;
345 fn mk_none_pos(&self) -> Pos {
346 self.pos_none.clone()
349 fn clone_and_unset_toplevel_if_toplevel<'b, 'c>(
351 ) -> impl AsMut<Env<'c>> + 'b {
352 if e.top_level_statements {
353 let mut cloned = e.clone();
354 cloned.top_level_statements = false;
361 fn check_stack_limit(&self) {
362 if let Some(limit) = &self.stack_limit {
363 limit.panic_if_exceeded()
368 impl<'a> AsMut<Env<'a>> for Env<'a> {
369 fn as_mut(&mut self) -> &mut Env<'a> {
380 kind: syntax_kind::SyntaxKind,
385 type S<'arena> = &'arena Syntax<'arena, PositionedToken<'arena>, PositionedValue<'arena>>;
387 fn p_pos<'a>(node: S<'a>, env: &Env<'_>) -> Pos {
388 node.position_exclusive(env.indexed_source_text)
389 .unwrap_or_else(|| env.mk_none_pos())
392 fn raise_parsing_error<'a>(node: S<'a>, env: &mut Env<'a>, msg: &str) {
393 raise_parsing_error_(Either::Left(node), env, msg)
396 fn raise_parsing_error_pos<'a>(pos: &Pos, env: &mut Env<'a>, msg: &str) {
397 raise_parsing_error_(Either::Right(pos), env, msg)
400 fn raise_parsing_error_<'a>(node_or_pos: Either<S<'a>, &Pos>, env: &mut Env<'a>, msg: &str) {
401 if env.should_surface_error() {
402 let pos = node_or_pos.either(|node| p_pos(node, env), |pos| pos.clone());
403 env.lowpri_errors().push((pos, String::from(msg)))
404 } else if env.codegen() {
405 let pos = node_or_pos.either(
407 node.position_exclusive(env.indexed_source_text)
408 .unwrap_or_else(|| env.mk_none_pos())
412 env.lowpri_errors().push((pos, String::from(msg)))
416 fn raise_hh_error(env: &mut Env<'_>, err: HHError) {
417 env.hh_errors().push(err);
420 fn raise_lint_error(env: &mut Env<'_>, err: LintError) {
421 env.lint_errors().push(err);
424 fn failwith<N>(msg: impl Into<String>) -> Result<N, Error> {
425 Err(Error::Failwith(msg.into()))
428 fn text<'a>(node: S<'a>, env: &Env<'_>) -> String {
429 String::from(node.text(env.source_text()))
432 fn text_str<'b, 'a>(node: S<'a>, env: &'b Env<'_>) -> &'b str {
433 node.text(env.source_text())
436 fn lowering_error(env: &mut Env<'_>, pos: &Pos, text: &str, syntax_kind: &str) {
437 if env.is_typechecker() && env.lowpri_errors().is_empty() {
438 raise_parsing_error_pos(
441 &syntax_error::lowering_parsing_error(text, syntax_kind),
446 fn missing_syntax_<N>(
451 ) -> Result<N, Error> {
452 let pos = p_pos(node, env);
453 let text = text(node, env);
454 lowering_error(env, &pos, &text, expecting);
455 if let Some(x) = fallback {
460 Err(Error::MissingSyntax {
461 expecting: String::from(expecting),
462 pos: p_pos(node, env),
468 fn missing_syntax<'a, N>(expecting: &str, node: S<'a>, env: &mut Env<'a>) -> Result<N, Error> {
469 missing_syntax_(None, expecting, node, env)
472 fn mp_optional<'a, F, R>(p: F, node: S<'a>, env: &mut Env<'a>) -> Result<Option<R>, Error>
474 F: FnOnce(S<'a>, &mut Env<'a>) -> Result<R, Error>,
476 match &node.children {
478 _ => p(node, env).map(Some),
482 fn pos_qualified_name<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Sid, Error> {
483 if let QualifiedName(c) = &node.children {
484 if let SyntaxList(l) = &c.parts.children {
485 let p = p_pos(node, env);
486 let mut s = String::with_capacity(node.width());
490 s += text_str(&li.item, env);
491 s += text_str(&li.separator, env);
493 _ => s += text_str(i, env),
496 return Ok(ast::Id(p, s));
499 missing_syntax("qualified name", node, env)
502 fn pos_name<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Sid, Error> {
503 pos_name_(node, env, None)
506 fn lid_from_pos_name<'a>(pos: Pos, name: S<'a>, env: &mut Env<'a>) -> Result<ast::Lid, Error> {
507 let name = pos_name(name, env)?;
508 Ok(ast::Lid::new(pos, name.1))
511 fn lid_from_name<'a>(name: S<'a>, env: &mut Env<'a>) -> Result<ast::Lid, Error> {
512 let name = pos_name(name, env)?;
513 Ok(ast::Lid::new(name.0, name.1))
516 fn p_pstring<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Pstring, Error> {
517 p_pstring_(node, env, None)
523 drop_prefix: Option<char>,
524 ) -> Result<ast::Pstring, Error> {
525 let ast::Id(p, id) = pos_name_(node, env, drop_prefix)?;
529 fn drop_prefix(s: &str, prefix: char) -> &str {
530 if !s.is_empty() && s.starts_with(prefix) {
540 drop_prefix_c: Option<char>,
541 ) -> Result<ast::Sid, Error> {
542 match &node.children {
543 QualifiedName(_) => pos_qualified_name(node, env),
544 SimpleTypeSpecifier(c) => pos_name_(&c.specifier, env, drop_prefix_c),
546 let mut name = node.text(env.indexed_source_text.source_text());
547 if let Some(prefix) = drop_prefix_c {
548 name = drop_prefix(name, prefix);
550 let p = p_pos(node, env);
551 Ok(ast::Id(p, String::from(name)))
556 fn mk_str<'a, F>(node: S<'a>, env: &mut Env<'a>, unescaper: F, mut content: &str) -> BString
558 F: Fn(&str) -> Result<BString, InvalidString>,
560 if let Some('b') = content.chars().next() {
561 content = content.get(1..).unwrap();
564 let len = content.len();
565 let no_quotes_result = extract_unquoted_string(content, 0, len);
566 match no_quotes_result {
568 let result = unescaper(&no_quotes);
575 &format!("Malformed string literal <<{}>>", &no_quotes),
585 &format!("Malformed string literal <<{}>>", &content),
592 fn unesc_dbl(s: &str) -> Result<BString, InvalidString> {
593 let unesc_s = unescape_double(s)?;
594 if unesc_s == B("''") || unesc_s == B("\"\"") {
595 Ok(BString::from(""))
601 // TODO: return Cow<[u8]>
602 fn unesc_xhp(s: &[u8]) -> Vec<u8> {
604 static ref WHITESPACE: Regex = Regex::new("[\x20\t\n\r\x0c]+").unwrap();
606 WHITESPACE.replace_all(s, &b" "[..]).into_owned()
609 fn unesc_xhp_attr(s: &[u8]) -> Vec<u8> {
610 // TODO: change unesc_dbl to &[u8] -> BString
611 let r = get_quoted_content(s);
612 let r = unsafe { std::str::from_utf8_unchecked(r) };
613 unesc_dbl(r).unwrap().into()
616 fn get_quoted_content(s: &[u8]) -> &[u8] {
618 static ref QUOTED: Regex = Regex::new(r#"^[\x20\t\n\r\x0c]*"((?:.|\n)*)""#).unwrap();
622 .and_then(|c| c.get(1))
623 .map_or(s, |m| m.as_bytes())
626 fn token_kind<'a>(node: S<'a>) -> Option<TK> {
627 match &node.children {
628 Token(t) => Some(t.kind()),
633 fn check_valid_reified_hint<'a>(env: &mut Env<'a>, node: S<'a>, hint: &ast::Hint) {
634 struct Checker<F: FnMut(&String)>(F);
635 impl<'ast, F: FnMut(&String)> Visitor<'ast> for Checker<F> {
636 type Params = AstParams<(), ()>;
638 fn object(&mut self) -> &mut dyn Visitor<'ast, Params = Self::Params> {
642 fn visit_hint(&mut self, c: &mut (), h: &ast::Hint) -> Result<(), ()> {
644 ast::Hint_::Happly(id, _) => {
647 ast::Hint_::Haccess(_, ids) => {
648 ids.iter().for_each(|id| self.0(&id.1));
656 if *env.in_static_method() {
657 let f = |id: &String| {
658 fail_if_invalid_reified_generic(node, env, id);
660 let mut visitor = Checker(f);
661 visitor.visit_hint(&mut (), hint).unwrap();
665 fn p_closure_parameter<'a>(
668 ) -> Result<(ast::Hint, Option<ast::HfParamInfo>), Error> {
669 match &node.children {
670 ClosureParameterTypeSpecifier(c) => {
671 let kind = p_param_kind(&c.call_convention, env)?;
672 let readonlyness = mp_optional(p_readonly, &c.readonly, env)?;
673 let info = Some(ast::HfParamInfo { kind, readonlyness });
674 let hint = p_hint(&c.type_, env)?;
677 _ => missing_syntax("closure parameter", node, env),
681 fn mp_shape_expression_field<'a, F, R>(
685 ) -> Result<(ast::ShapeFieldName, R), Error>
687 F: Fn(S<'a>, &mut Env<'a>) -> Result<R, Error>,
689 match &node.children {
690 FieldInitializer(c) => {
691 let name = p_shape_field_name(&c.name, env)?;
692 let value = f(&c.value, env)?;
695 _ => missing_syntax("shape field", node, env),
699 fn p_shape_field_name<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::ShapeFieldName, Error> {
700 use ast::ShapeFieldName::*;
701 let is_valid_shape_literal = |t: &PositionedToken<'a>| {
703 t.kind() == TK::SingleQuotedStringLiteral || t.kind() == TK::DoubleQuotedStringLiteral;
704 let text = t.text(env.source_text());
705 let is_empty = text == "\'\'" || text == "\"\"";
708 if let LiteralExpression(c) = &node.children {
709 if let Token(t) = &c.expression.children {
710 if is_valid_shape_literal(t) {
711 let ast::Id(p, n) = pos_name(node, env)?;
712 let unescp = if t.kind() == TK::SingleQuotedStringLiteral {
717 let str_ = mk_str(node, env, unescp, &n);
718 if int_of_string_opt(&str_).is_some() {
719 raise_parsing_error(node, env, &syntax_error::shape_field_int_like_string)
721 return Ok(SFlitStr((p, str_)));
725 match &node.children {
726 ScopeResolutionExpression(c) => Ok(SFclassConst(
727 pos_name(&c.qualifier, env)?,
728 p_pstring(&c.name, env)?,
731 raise_parsing_error(node, env, &syntax_error::invalid_shape_field_name);
732 let ast::Id(p, n) = pos_name(node, env)?;
733 Ok(SFlitStr((p, mk_str(node, env, unesc_dbl, &n))))
738 fn p_shape_field<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::ShapeFieldInfo, Error> {
739 match &node.children {
740 FieldSpecifier(c) => {
741 let optional = !c.question.is_missing();
742 let name = p_shape_field_name(&c.name, env)?;
743 let hint = p_hint(&c.type_, env)?;
744 Ok(ast::ShapeFieldInfo {
751 let (name, hint) = mp_shape_expression_field(p_hint, node, env)?;
752 Ok(ast::ShapeFieldInfo {
761 fn p_targ<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Targ, Error> {
762 Ok(ast::Targ((), p_hint(node, env)?))
765 fn p_hint_<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Hint_, Error> {
768 |kw, ty, env: &mut Env<'a>| Ok(Happly(pos_name(kw, env)?, could_map(p_hint, ty, env)?));
769 let binary = |kw, key, ty, env: &mut Env<'a>| {
770 let kw = pos_name(kw, env)?;
771 let key = p_hint(key, env)?;
772 let value = p_hint(ty, env)?;
773 Ok(Happly(kw, vec![key, value]))
776 match &node.children {
777 Token(token) if token.kind() == TK::Variable => {
778 let ast::Id(_pos, name) = pos_name(node, env)?;
781 /* Dirty hack; CastExpression can have type represented by token */
782 Token(_) | SimpleTypeSpecifier(_) | QualifiedName(_) => {
783 let ast::Id(pos, name) = pos_name(node, env)?;
784 let mut suggest = |name: &str, canonical: &str| {
788 &syntax_error::invalid_typehint_alias(name, canonical),
791 if "integer".eq_ignore_ascii_case(&name) {
792 suggest(&name, special_typehints::INT);
793 } else if "boolean".eq_ignore_ascii_case(&name) {
794 suggest(&name, special_typehints::BOOL);
795 } else if "double".eq_ignore_ascii_case(&name) || "real".eq_ignore_ascii_case(&name) {
796 suggest(&name, special_typehints::FLOAT);
799 use naming_special_names_rust::coeffects::{CAPABILITIES, CONTEXTS};
800 if env.file_mode() != file_info::Mode::Mhhi && !env.codegen() {
801 let sn = strip_ns(&name);
802 if sn.starts_with(CONTEXTS) || sn.starts_with(CAPABILITIES) {
803 raise_parsing_error(node, env, &syntax_error::direct_coeffects_reference);
806 Ok(Happly(ast::Id(pos, name), vec![]))
808 ShapeTypeSpecifier(c) => {
809 let allows_unknown_fields = !c.ellipsis.is_missing();
810 /* if last element lacks a separator and ellipsis is present, error */
811 if allows_unknown_fields {
812 if let SyntaxList(items) = &c.fields.children {
813 if let Some(ListItem(item)) = items.last().map(|i| &i.children) {
814 if item.separator.is_missing() {
818 &syntax_error::shape_type_ellipsis_without_trailing_comma,
825 let field_map = could_map(p_shape_field, &c.fields, env)?;
826 let mut set = HashSet::default();
827 for f in field_map.iter() {
828 if !set.insert(f.name.get_name()) {
829 raise_hh_error(env, Naming::fd_name_already_bound(f.name.get_pos().clone()));
833 Ok(Hshape(ast::NastShapeInfo {
834 allows_unknown_fields,
838 TupleTypeSpecifier(c) => Ok(Htuple(could_map(p_hint, &c.types, env)?)),
839 UnionTypeSpecifier(c) => Ok(Hunion(could_map(&p_hint, &c.types, env)?)),
840 IntersectionTypeSpecifier(c) => Ok(Hintersection(could_map(&p_hint, &c.types, env)?)),
841 KeysetTypeSpecifier(c) => Ok(Happly(
842 pos_name(&c.keyword, env)?,
843 could_map(p_hint, &c.type_, env)?,
845 VectorTypeSpecifier(c) => unary(&c.keyword, &c.type_, env),
846 ClassnameTypeSpecifier(c) => unary(&c.keyword, &c.type_, env),
847 TupleTypeExplicitSpecifier(c) => unary(&c.keyword, &c.types, env),
848 VarrayTypeSpecifier(c) => unary(&c.keyword, &c.type_, env),
849 DarrayTypeSpecifier(c) => binary(&c.keyword, &c.key, &c.value, env),
850 DictionaryTypeSpecifier(c) => unary(&c.keyword, &c.members, env),
851 GenericTypeSpecifier(c) => {
852 let name = pos_name(&c.class_type, env)?;
853 let args = &c.argument_list;
854 let type_args = match &args.children {
855 TypeArguments(c) => could_map(p_hint, &c.types, env)?,
856 _ => missing_syntax("generic type arguments", args, env)?,
858 Ok(Happly(name, type_args))
860 NullableTypeSpecifier(c) => Ok(Hoption(p_hint(&c.type_, env)?)),
861 LikeTypeSpecifier(c) => Ok(Hlike(p_hint(&c.type_, env)?)),
862 SoftTypeSpecifier(c) => Ok(Hsoft(p_hint(&c.type_, env)?)),
863 ClosureTypeSpecifier(c) => {
864 let (param_list, variadic_hints): (Vec<S<'a>>, Vec<S<'a>>) = c
866 .syntax_node_to_list_skip_separator()
867 .partition(|n| match &n.children {
868 VariadicParameter(_) => false,
871 let (type_hints, info) = param_list
873 .map(|p| p_closure_parameter(p, env))
874 .collect::<Result<Vec<_>, _>>()?
877 let variadic_hints = variadic_hints
879 .map(|v| match &v.children {
880 VariadicParameter(c) => {
881 if c.type_.is_missing() {
882 raise_parsing_error(v, env, "Cannot use ... without a typehint");
884 Ok(Some(p_hint(&c.type_, env)?))
886 _ => panic!("expect variadic parameter"),
888 .collect::<Result<Vec<_>, _>>()?;
889 if variadic_hints.len() > 1 {
890 return failwith(format!(
891 "{} variadic parameters found. There should be no more than one.",
895 let ctxs = p_contexts(&c.contexts, env)?;
896 Ok(Hfun(ast::HintFun {
897 is_readonly: mp_optional(p_readonly, &c.readonly_keyword, env)?,
898 param_tys: type_hints,
900 variadic_ty: variadic_hints.into_iter().next().unwrap_or(None),
902 return_ty: p_hint(&c.return_type, env)?,
903 is_readonly_return: mp_optional(p_readonly, &c.readonly_return, env)?,
906 AttributizedSpecifier(c) => {
907 let attrs = p_user_attribute(&c.attribute_spec, env)?;
908 let hint = p_hint(&c.type_, env)?;
909 if attrs.iter().any(|attr| attr.name.1 != special_attrs::SOFT) {
910 raise_parsing_error(node, env, &syntax_error::only_soft_allowed);
912 Ok(*soften_hint(&attrs, hint).1)
914 FunctionCtxTypeSpecifier(c) => {
915 let ast::Id(_p, n) = pos_name(&c.variable, env)?;
919 let child = pos_name(&c.right_type, env)?;
920 match p_hint_(&c.left_type, env)? {
921 Haccess(root, mut cs) => {
923 Ok(Haccess(root, cs))
926 let pos = p_pos(&c.left_type, env);
927 let root = ast::Hint::new(pos, Hvar(n));
928 Ok(Haccess(root, vec![child]))
930 Happly(ty, param) => {
931 if param.is_empty() {
932 let root = ast::Hint::new(ty.0.clone(), Happly(ty, param));
933 Ok(Haccess(root, vec![child]))
935 missing_syntax("type constant base", node, env)
938 _ => missing_syntax("type constant base", node, env),
941 ReifiedTypeArgument(_) => {
942 raise_parsing_error(node, env, &syntax_error::invalid_reified);
943 missing_syntax("refied type", node, env)
945 _ => missing_syntax("type hint", node, env),
949 fn p_hint<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Hint, Error> {
950 let hint_ = p_hint_(node, env)?;
951 let pos = p_pos(node, env);
952 let hint = ast::Hint::new(pos, hint_);
953 check_valid_reified_hint(env, node, &hint);
957 fn p_simple_initializer<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Expr, Error> {
958 match &node.children {
959 SimpleInitializer(c) => p_expr(&c.value, env),
960 _ => missing_syntax("simple initializer", node, env),
964 fn p_member<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<(ast::Expr, ast::Expr), Error> {
965 match &node.children {
966 ElementInitializer(c) => Ok((p_expr(&c.key, env)?, p_expr(&c.value, env)?)),
967 _ => missing_syntax("darray intrinsic expression element", node, env),
971 fn expand_type_args<'a>(ty: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Hint>, Error> {
973 TypeArguments(c) => could_map(p_hint, &c.types, env),
978 fn p_afield<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Afield, Error> {
979 match &node.children {
980 ElementInitializer(c) => Ok(ast::Afield::AFkvalue(
981 p_expr(&c.key, env)?,
982 p_expr(&c.value, env)?,
984 _ => Ok(ast::Afield::AFvalue(p_expr(node, env)?)),
987 // We lower readonly lambda declarations as making the inner lambda have readonly_this.
988 fn process_readonly_expr(mut e: ast::Expr) -> ast::Expr_ {
991 ast::Expr(_, _, Efun(ref mut e)) => {
992 e.0.readonly_this = Some(ast::ReadonlyKind::Readonly);
994 ast::Expr(_, _, Lfun(ref mut l)) => {
995 l.0.readonly_this = Some(ast::ReadonlyKind::Readonly);
999 E_::mk_readonly_expr(e)
1002 fn check_intrinsic_type_arg_varity<'a>(
1005 tys: Vec<ast::Hint>,
1006 ) -> Option<ast::CollectionTarg> {
1007 let count = tys.len();
1008 let mut tys = tys.into_iter();
1010 2 => Some(ast::CollectionTarg::CollectionTKV(
1011 ast::Targ((), tys.next().unwrap()),
1012 ast::Targ((), tys.next().unwrap()),
1014 1 => Some(ast::CollectionTarg::CollectionTV(ast::Targ(
1016 tys.next().unwrap(),
1020 raise_parsing_error(node, env, &syntax_error::collection_intrinsic_many_typeargs);
1026 fn p_import_flavor<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::ImportFlavor, Error> {
1027 use ast::ImportFlavor::*;
1028 match token_kind(node) {
1029 Some(TK::Include) => Ok(Include),
1030 Some(TK::Require) => Ok(Require),
1031 Some(TK::Include_once) => Ok(IncludeOnce),
1032 Some(TK::Require_once) => Ok(RequireOnce),
1033 _ => missing_syntax("import flavor", node, env),
1037 fn p_null_flavor<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::OgNullFlavor, Error> {
1038 use ast::OgNullFlavor::*;
1039 match token_kind(node) {
1040 Some(TK::QuestionMinusGreaterThan) => Ok(OGNullsafe),
1041 Some(TK::MinusGreaterThan) => Ok(OGNullthrows),
1042 _ => missing_syntax("null flavor", node, env),
1046 fn wrap_unescaper<F>(unescaper: F, s: &str) -> Result<BString, Error>
1048 F: FnOnce(&str) -> Result<BString, InvalidString>,
1050 unescaper(s).map_err(|e| Error::Failwith(e.msg))
1053 fn fail_if_invalid_class_creation<'a>(node: S<'a>, env: &mut Env<'a>, id: impl AsRef<str>) {
1054 let id = id.as_ref();
1055 let is_in_static_method = *env.in_static_method();
1056 if is_in_static_method
1057 && ((id == special_classes::SELF && env.cls_generics_mut().values().any(|reif| *reif))
1058 || (id == special_classes::PARENT && *env.parent_maybe_reified()))
1060 raise_parsing_error(node, env, &syntax_error::static_method_reified_obj_creation);
1064 fn fail_if_invalid_reified_generic<'a>(node: S<'a>, env: &mut Env<'a>, id: impl AsRef<str>) {
1065 let is_in_static_method = *env.in_static_method();
1066 if is_in_static_method && *env.cls_generics_mut().get(id.as_ref()).unwrap_or(&false) {
1067 raise_parsing_error(
1070 &syntax_error::cls_reified_generic_in_static_method,
1075 fn rfind(s: &[u8], mut i: usize, c: u8) -> Result<usize, Error> {
1077 return failwith("index out of range");
1086 failwith("char not found")
1089 fn prep_string2<'a>(
1090 nodes: &'a [Syntax<'a, PositionedToken<'a>, PositionedValue<'a>>],
1092 ) -> Result<(TokenOp, TokenOp), Error> {
1094 let is_qoute = |c| c == b'\"' || c == b'`';
1095 let start_is_qoute = |s: &[u8]| {
1096 (!s.is_empty() && is_qoute(s[0])) || (s.len() > 1 && (s[0] == b'b' && s[1] == b'\"'))
1098 let last_is_qoute = |s: &[u8]| !s.is_empty() && is_qoute(s[s.len() - 1]);
1099 let is_heredoc = |s: &[u8]| (s.len() > 3 && &s[0..3] == b"<<<");
1100 let mut nodes = nodes.iter();
1101 let first = nodes.next();
1102 match first.map(|n| &n.children) {
1105 raise_parsing_error(first.unwrap(), env, "Malformed String2 SyntaxList");
1107 let text = t.text_raw(env.source_text());
1108 if start_is_qoute(text) {
1109 let first_token_op = match text[0] {
1110 b'b' if text.len() > 2 => LeftTrim(2),
1111 _ if is_qoute(text[0]) && text.len() > 1 => LeftTrim(1),
1114 if let Some(Token(t)) = nodes.last().map(|n| &n.children) {
1115 let last_text = t.text_raw(env.source_text());
1116 if last_is_qoute(last_text) {
1117 let last_taken_op = match last_text.len() {
1118 n if n > 1 => RightTrim(1),
1121 return Ok((first_token_op, last_taken_op));
1125 Ok((first_token_op, Noop))
1126 } else if is_heredoc(text) {
1127 let trim_size = text
1129 .position(|c| *c == b'\n')
1130 .ok_or_else(|| Error::Failwith(String::from("newline not found")))?
1132 let first_token_op = match trim_size {
1133 _ if trim_size == text.len() => Skip,
1134 _ => LeftTrim(trim_size),
1136 if let Some(Token(t)) = nodes.last().map(|n| &n.children) {
1137 let text = t.text_raw(env.source_text());
1138 let len = text.len();
1140 let n = rfind(text, len - 2, b'\n')?;
1141 let last_token_op = match n {
1143 _ => RightTrim(len - n),
1145 return Ok((first_token_op, last_token_op));
1149 Ok((first_token_op, Noop))
1154 _ => Ok((Noop, Noop)),
1158 fn process_token_op<'a>(
1162 ) -> Result<Option<S<'a>>, Error> {
1165 LeftTrim(n) => match &node.children {
1167 let token = env.token_factory.trim_left(t, n);
1168 let node = env.arena.alloc(Syntax::make_token(token));
1171 _ => failwith("Token expected"),
1173 RightTrim(n) => match &node.children {
1175 let token = env.token_factory.trim_right(t, n);
1176 let node = env.arena.alloc(Syntax::make_token(token));
1179 _ => failwith("Token expected"),
1186 nodes: &'a [Syntax<'a, PositionedToken<'a>, PositionedValue<'a>>],
1188 ) -> Result<Vec<ast::Expr>, Error> {
1190 let (head_op, tail_op) = prep_string2(nodes, env)?;
1191 let mut result = Vec::with_capacity(nodes.len());
1193 let last = nodes.len() - 1;
1197 _ if i == last => tail_op,
1206 let node = process_token_op(env, op, &nodes[i])?;
1207 let node = node.unwrap_or(&nodes[i]);
1209 if token_kind(node) == Some(TK::Dollar) && i < last {
1210 if let EmbeddedBracedExpression(_) = &nodes[i + 1].children {
1211 raise_parsing_error(&nodes[i + 1], env, &syntax_error::outside_dollar_str_interp);
1213 result.push(p_expr_with_loc(
1214 ExprLocation::InDoubleQuotedString,
1223 result.push(p_expr_with_loc(
1224 ExprLocation::InDoubleQuotedString,
1233 fn p_expr_l<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Expr>, Error> {
1234 let p_expr = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::Expr, Error> {
1235 p_expr_with_loc(ExprLocation::TopLevel, n, e)
1237 could_map(p_expr, node, env)
1240 fn p_expr<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Expr, Error> {
1241 p_expr_with_loc(ExprLocation::TopLevel, node, env)
1244 fn p_expr_for_function_call_arguments<'a>(
1247 ) -> Result<(ast::ParamKind, ast::Expr), Error> {
1248 match &node.children {
1249 DecoratedExpression(DecoratedExpressionChildren {
1252 }) if token_kind(decorator) == Some(TK::Inout) => Ok((
1253 ast::ParamKind::Pinout(p_pos(decorator, env)),
1254 p_expr(expression, env)?,
1256 _ => Ok((ast::ParamKind::Pnormal, p_expr(node, env)?)),
1260 fn p_expr_for_normal_argument<'a>(
1263 ) -> Result<(ast::ParamKind, ast::Expr), Error> {
1264 Ok((ast::ParamKind::Pnormal, p_expr(node, env)?))
1267 fn p_expr_with_loc<'a>(
1268 location: ExprLocation,
1271 ) -> Result<ast::Expr, Error> {
1272 p_expr_impl(location, node, env, None)
1276 location: ExprLocation,
1279 parent_pos: Option<Pos>,
1280 ) -> Result<ast::Expr, Error> {
1281 // We use location=CallReceiver to set PropOrMethod::IsMethod on ObjGet
1282 // But only if it is the immediate node.
1283 let location = match (location, &node.children) {
1285 ExprLocation::CallReceiver,
1286 MemberSelectionExpression(_)
1287 | SafeMemberSelectionExpression(_)
1288 | EmbeddedMemberSelectionExpression(_)
1289 | ScopeResolutionExpression(_),
1291 (ExprLocation::CallReceiver, _) => ExprLocation::TopLevel,
1294 match &node.children {
1295 BracedExpression(c) => {
1296 // Either a dynamic method lookup on a dynamic value:
1297 // $foo->{$meth_name}();
1298 // or an XHP splice.
1299 // <p id={$id}>hello</p>;
1300 // In both cases, unwrap, consistent with parentheses.
1301 p_expr_impl(location, &c.expression, env, parent_pos)
1303 ParenthesizedExpression(c) => p_expr_impl(location, &c.expression, env, parent_pos),
1304 ETSpliceExpression(c) => {
1305 let pos = p_pos(node, env);
1307 let inner_pos = p_pos(&c.expression, env);
1308 let inner_expr_ = p_expr_impl_(location, &c.expression, env, parent_pos)?;
1309 let inner_expr = ast::Expr::new((), inner_pos, inner_expr_);
1313 ast::Expr_::ETSplice(Box::new(inner_expr)),
1317 let pos = p_pos(node, env);
1318 let expr_ = p_expr_impl_(location, node, env, parent_pos)?;
1319 Ok(ast::Expr::new((), pos, expr_))
1325 location: ExprLocation,
1329 ) -> Result<ast::Expr_, Error> {
1330 match &expr.children {
1332 let s = expr.text(env.indexed_source_text.source_text());
1333 let check_lint_err = |e: &mut Env<'a>, s: &str, expected: &str| {
1334 if !e.codegen() && s != expected {
1335 raise_lint_error(e, LintError::lowercase_constant(p_pos(expr, e), s));
1338 match (location, token_kind(expr)) {
1339 (ExprLocation::InDoubleQuotedString, _) if env.codegen() => {
1340 Ok(E_::String(mk_str(expr, env, unesc_dbl, s)))
1342 (_, Some(TK::DecimalLiteral))
1343 | (_, Some(TK::OctalLiteral))
1344 | (_, Some(TK::HexadecimalLiteral))
1345 | (_, Some(TK::BinaryLiteral)) => {
1346 let s = s.replace("_", "");
1347 match parse_int(&s) {
1348 Err(ParseIntError::OutOfRange) => {
1349 raise_parsing_error(expr, env, &syntax_error::out_of_int_range(&s));
1351 Err(ParseIntError::InvalidDigit(int_kind)) => {
1352 raise_parsing_error(
1355 &syntax_error::invalid_integer_digit(int_kind),
1357 missing_syntax(&format!("{}", int_kind), expr, env)?;
1359 Err(ParseIntError::Empty) => {
1360 failwith("Unexpected int literal error")?;
1366 (_, Some(TK::FloatingLiteral)) => {
1367 // f64::from_str accepts more string than Hacklang, invalid Hack float literal
1368 // is caught in lexer.
1369 if f64::from_str(s).is_err() {
1370 raise_parsing_error(expr, env, &syntax_error::out_of_float_range(s))
1372 Ok(E_::Float(s.into()))
1374 (_, Some(TK::SingleQuotedStringLiteral)) => {
1375 Ok(E_::String(mk_str(expr, env, unescape_single, s)))
1377 (_, Some(TK::DoubleQuotedStringLiteral)) => {
1378 Ok(E_::String(mk_str(expr, env, unescape_double, s)))
1380 (_, Some(TK::HeredocStringLiteral)) => {
1381 Ok(E_::String(mk_str(expr, env, unescape_heredoc, s)))
1383 (_, Some(TK::NowdocStringLiteral)) => {
1384 Ok(E_::String(mk_str(expr, env, unescape_nowdoc, s)))
1386 (_, Some(TK::NullLiteral)) => {
1387 check_lint_err(env, s, literal::NULL);
1390 (_, Some(TK::BooleanLiteral)) => {
1391 if s.eq_ignore_ascii_case(literal::FALSE) {
1392 check_lint_err(env, s, literal::FALSE);
1394 } else if s.eq_ignore_ascii_case(literal::TRUE) {
1395 check_lint_err(env, s, literal::TRUE);
1398 missing_syntax(&format!("boolean (not: {})", s), expr, env)
1401 _ => missing_syntax("literal", expr, env),
1404 SyntaxList(ts) => Ok(E_::String2(p_string2(ts, env)?)),
1405 _ => missing_syntax("literal expressoin", expr, env),
1409 fn p_expr_with_loc_<'a>(
1410 location: ExprLocation,
1413 ) -> Result<ast::Expr_, Error> {
1414 p_expr_impl_(location, node, env, None)
1417 fn p_expr_impl_<'a>(
1418 location: ExprLocation,
1421 parent_pos: Option<Pos>,
1422 ) -> Result<ast::Expr_, Error> {
1423 if *env.exp_recursion_depth() >= EXP_RECUSION_LIMIT {
1424 Err(Error::Failwith("Expression recursion limit reached".into()))
1426 *env.exp_recursion_depth() += 1;
1427 let r = p_expr_impl__(location, node, env, parent_pos);
1428 *env.exp_recursion_depth() -= 1;
1433 fn split_args_vararg<'a>(
1434 arg_list_node: S<'a>,
1436 ) -> Result<(Vec<(ast::ParamKind, ast::Expr)>, Option<ast::Expr>), Error> {
1437 let mut arg_list: Vec<_> = arg_list_node.syntax_node_to_list_skip_separator().collect();
1438 if let Some(last_arg) = arg_list.last() {
1439 if let DecoratedExpression(c) = &last_arg.children {
1440 if token_kind(&c.decorator) == Some(TK::DotDotDot) {
1441 let _ = arg_list.pop();
1442 let args: Result<Vec<_>, _> = arg_list
1444 .map(|a| p_expr_for_function_call_arguments(a, e))
1447 let vararg = p_expr(&c.expression, e)?;
1448 return Ok((args, Some(vararg)));
1453 could_map(p_expr_for_function_call_arguments, arg_list_node, e)?,
1458 fn p_expr_impl__<'a>(
1459 location: ExprLocation,
1462 parent_pos: Option<Pos>,
1463 ) -> Result<ast::Expr_, Error> {
1464 env.check_stack_limit();
1466 let mk_lid = |p: Pos, s: String| ast::Lid(p, (0, s));
1467 let mk_name_lid = |name: S<'a>, env: &mut Env<'a>| {
1468 let name = pos_name(name, env)?;
1469 Ok(mk_lid(name.0, name.1))
1471 let mk_lvar = |name: S<'a>, env: &mut Env<'a>| Ok(E_::mk_lvar(mk_name_lid(name, env)?));
1472 let mk_id_expr = |name: ast::Sid| E::new((), name.0.clone(), E_::mk_id(name));
1473 let p_intri_expr = |kw, ty, v, e: &mut Env<'a>| {
1474 let hints = expand_type_args(ty, e)?;
1475 let hints = check_intrinsic_type_arg_varity(node, e, hints);
1476 Ok(E_::mk_collection(
1479 could_map(p_afield, v, e)?,
1482 let p_special_call = |recv: S<'a>, args: S<'a>, e: &mut Env<'a>| -> Result<ast::Expr_, Error> {
1483 // Mark expression as CallReceiver so that we can correctly set PropOrMethod field in ObjGet and ClassGet
1484 let recv = p_expr_with_loc(ExprLocation::CallReceiver, recv, e)?;
1485 let (args, varargs) = split_args_vararg(args, e)?;
1486 Ok(E_::mk_call(recv, vec![], args, varargs))
1489 |recv: S<'a>, op: S<'a>, name: S<'a>, e: &mut Env<'a>| -> Result<ast::Expr_, Error> {
1490 if recv.is_object_creation_expression() && !e.codegen() {
1491 raise_parsing_error(recv, e, &syntax_error::invalid_constructor_method_call);
1493 let recv = p_expr(recv, e)?;
1494 let name = p_expr_with_loc(ExprLocation::MemberSelect, name, e)?;
1495 let op = p_null_flavor(op, e)?;
1501 ExprLocation::CallReceiver => ast::PropOrMethod::IsMethod,
1502 _ => ast::PropOrMethod::IsProp,
1506 let pos = match parent_pos {
1507 None => p_pos(node, env),
1510 match &node.children {
1511 LambdaExpression(c) => {
1512 let suspension_kind = mk_suspension_kind(&c.async_);
1513 let (params, (ctxs, unsafe_ctxs), readonly_ret, ret) = match &c.signature.children {
1514 LambdaSignature(c) => {
1515 let params = could_map(p_fun_param, &c.parameters, env)?;
1516 let readonly_ret = mp_optional(p_readonly, &c.readonly_return, env)?;
1517 let ctxs = p_contexts(&c.contexts, env)?;
1518 let unsafe_ctxs = ctxs.clone();
1519 if has_polymorphic_context(env, ctxs.as_ref()) {
1520 raise_parsing_error(
1523 &syntax_error::lambda_effect_polymorphic,
1526 let ret = mp_optional(p_hint, &c.type_, env)?;
1527 (params, (ctxs, unsafe_ctxs), readonly_ret, ret)
1530 let ast::Id(p, n) = pos_name(&c.signature, env)?;
1532 vec![ast::FunParam {
1534 type_hint: ast::TypeHint((), None),
1539 callconv: ast::ParamKind::Pnormal,
1541 user_attributes: vec![],
1549 _ => missing_syntax("lambda signature", &c.signature, env)?,
1552 let (body, yield_) = if !c.body.is_compound_statement() {
1553 mp_yielding(p_function_body, &c.body, env)?
1555 let mut env1 = Env::clone_and_unset_toplevel_if_toplevel(env);
1556 mp_yielding(&p_function_body, &c.body, env1.as_mut())?
1558 let external = c.body.is_external();
1559 let fun = ast::Fun_ {
1561 readonly_this: None, // filled in by mk_unop
1564 ret: ast::TypeHint((), ret),
1565 name: ast::Id(pos, String::from(";anonymous")),
1567 where_constraints: vec![],
1568 body: ast::FuncBody { fb_ast: body },
1569 fun_kind: mk_fun_kind(suspension_kind, yield_),
1573 user_attributes: p_user_attributes(&c.attribute_spec, env)?,
1577 Ok(E_::mk_lfun(fun, vec![]))
1579 BracedExpression(c) => p_expr_with_loc_(location, &c.expression, env),
1580 EmbeddedBracedExpression(c) => p_expr_impl_(location, &c.expression, env, Some(pos)),
1581 ParenthesizedExpression(c) => p_expr_with_loc_(location, &c.expression, env),
1582 DictionaryIntrinsicExpression(c) => {
1583 p_intri_expr(&c.keyword, &c.explicit_type, &c.members, env)
1585 KeysetIntrinsicExpression(c) => p_intri_expr(&c.keyword, &c.explicit_type, &c.members, env),
1586 VectorIntrinsicExpression(c) => p_intri_expr(&c.keyword, &c.explicit_type, &c.members, env),
1587 CollectionLiteralExpression(c) => {
1588 let (collection_name, hints) = match &c.name.children {
1589 SimpleTypeSpecifier(c) => (pos_name(&c.specifier, env)?, None),
1590 GenericTypeSpecifier(c) => {
1591 let hints = expand_type_args(&c.argument_list, env)?;
1592 let hints = check_intrinsic_type_arg_varity(node, env, hints);
1593 (pos_name(&c.class_type, env)?, hints)
1595 _ => (pos_name(&c.name, env)?, None),
1597 Ok(E_::mk_collection(
1600 could_map(p_afield, &c.initializers, env)?,
1603 VarrayIntrinsicExpression(c) => {
1604 let hints = expand_type_args(&c.explicit_type, env)?;
1605 let hints = check_intrinsic_type_arg_varity(node, env, hints);
1606 let targ = match hints {
1607 Some(ast::CollectionTarg::CollectionTV(ty)) => Some(ty),
1609 _ => missing_syntax("VarrayIntrinsicExpression type args", node, env)?,
1611 Ok(E_::mk_varray(targ, could_map(p_expr, &c.members, env)?))
1613 DarrayIntrinsicExpression(c) => {
1614 let hints = expand_type_args(&c.explicit_type, env)?;
1615 let hints = check_intrinsic_type_arg_varity(node, env, hints);
1617 Some(ast::CollectionTarg::CollectionTKV(tk, tv)) => Ok(E_::mk_darray(
1619 could_map(p_member, &c.members, env)?,
1621 None => Ok(E_::mk_darray(None, could_map(p_member, &c.members, env)?)),
1622 _ => missing_syntax("DarrayIntrinsicExpression type args", node, env),
1625 ListExpression(c) => {
1626 /* TODO: Or tie in with other intrinsics and post-process to List */
1627 let p_binder_or_ignore = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::Expr, Error> {
1629 Missing => Ok(E::new((), e.mk_none_pos(), E_::Omitted)),
1633 Ok(E_::List(could_map(&p_binder_or_ignore, &c.members, env)?))
1635 EvalExpression(c) => p_special_call(&c.keyword, &c.argument, env),
1636 IssetExpression(c) => p_special_call(&c.keyword, &c.argument_list, env),
1637 TupleExpression(c) => Ok(E_::mk_tuple(could_map(p_expr, &c.items, env)?)),
1638 FunctionCallExpression(c) => {
1639 let recv = &c.receiver;
1640 let args = &c.argument_list;
1641 let get_hhas_adata = || {
1642 if text_str(recv, env) == "__hhas_adata" {
1643 if let SyntaxList(l) = &args.children {
1644 if let Some(li) = l.first() {
1645 if let ListItem(i) = &li.children {
1646 if let LiteralExpression(le) = &i.item.children {
1647 let expr = &le.expression;
1648 if token_kind(expr) == Some(TK::NowdocStringLiteral) {
1658 match get_hhas_adata() {
1660 let literal_expression_pos = p_pos(expr, env);
1661 let s = extract_unquoted_string(text_str(expr, env), 0, expr.width())
1662 .map_err(|e| Error::Failwith(e.msg))?;
1667 ast::ParamKind::Pnormal,
1668 E::new((), literal_expression_pos, E_::String(s.into())),
1674 let targs = match (&recv.children, &c.type_args.children) {
1675 (_, TypeArguments(c)) => could_map(p_targ, &c.types, env)?,
1676 /* TODO might not be needed */
1677 (GenericTypeSpecifier(c), _) => match &c.argument_list.children {
1678 TypeArguments(c) => could_map(p_targ, &c.types, env)?,
1684 // Mark expression as CallReceiver so that we can correctly set PropOrMethod field in ObjGet and ClassGet
1685 let recv = p_expr_with_loc(ExprLocation::CallReceiver, recv, env)?;
1686 let (mut args, varargs) = split_args_vararg(args, env)?;
1688 // If the function has an enum class label expression, that's
1689 // the first argument.
1690 if let EnumClassLabelExpression(e) = &c.enum_class_label.children {
1692 e.qualifier.is_missing(),
1693 "Parser error: function call with enum class labels"
1695 let pos = p_pos(&c.enum_class_label, env);
1696 let enum_class_label = ast::Expr::new(
1699 E_::mk_enum_class_label(None, pos_name(&e.expression, env)?.1),
1701 args.insert(0, (ast::ParamKind::Pnormal, enum_class_label));
1704 Ok(E_::mk_call(recv, targs, args, varargs))
1708 FunctionPointerExpression(c) => {
1709 let targs = match &c.type_args.children {
1710 TypeArguments(c) => could_map(p_targ, &c.types, env)?,
1714 let recv = p_expr(&c.receiver, env)?;
1717 aast::Expr_::Id(id) => Ok(E_::mk_function_pointer(
1718 aast::FunctionPtrId::FPId(*(id.to_owned())),
1721 aast::Expr_::ClassConst(c) => {
1722 if let aast::ClassId_::CIexpr(aast::Expr(_, _, aast::Expr_::Id(_))) = (c.0).2 {
1723 Ok(E_::mk_function_pointer(
1724 aast::FunctionPtrId::FPClassConst(c.0.to_owned(), c.1.to_owned()),
1728 raise_parsing_error(node, env, &syntax_error::function_pointer_bad_recv);
1729 missing_syntax("function or static method", node, env)
1733 raise_parsing_error(node, env, &syntax_error::function_pointer_bad_recv);
1734 missing_syntax("function or static method", node, env)
1738 QualifiedName(_) => match location {
1739 ExprLocation::InDoubleQuotedString => {
1740 let ast::Id(_, n) = pos_qualified_name(node, env)?;
1741 Ok(E_::String(n.into()))
1743 _ => Ok(E_::mk_id(pos_qualified_name(node, env)?)),
1745 VariableExpression(c) => Ok(E_::mk_lvar(lid_from_pos_name(pos, &c.expression, env)?)),
1746 PipeVariableExpression(_) => Ok(E_::mk_lvar(mk_lid(
1748 special_idents::DOLLAR_DOLLAR.into(),
1750 InclusionExpression(c) => Ok(E_::mk_import(
1751 p_import_flavor(&c.require, env)?,
1752 p_expr(&c.filename, env)?,
1754 MemberSelectionExpression(c) => p_obj_get(&c.object, &c.operator, &c.name, env),
1755 SafeMemberSelectionExpression(c) => p_obj_get(&c.object, &c.operator, &c.name, env),
1756 EmbeddedMemberSelectionExpression(c) => p_obj_get(&c.object, &c.operator, &c.name, env),
1757 PrefixUnaryExpression(_) | PostfixUnaryExpression(_) | DecoratedExpression(_) => {
1758 let (operand, op, postfix) = match &node.children {
1759 PrefixUnaryExpression(c) => (&c.operand, &c.operator, false),
1760 PostfixUnaryExpression(c) => (&c.operand, &c.operator, true),
1761 DecoratedExpression(c) => (&c.expression, &c.decorator, false),
1762 _ => missing_syntax("unary exppr", node, env)?,
1766 * FFP does not destinguish between ++$i and $i++ on the level of token
1767 * kind annotation. Prevent duplication by switching on `postfix` for
1768 * the two operatores for which AST /does/ differentiate between
1772 let mk_unop = |op, e| Ok(E_::mk_unop(op, e));
1773 let op_kind = token_kind(op);
1774 if let Some(TK::At) = op_kind {
1775 if env.parser_options.po_disallow_silence {
1776 raise_parsing_error(op, env, &syntax_error::no_silence);
1779 let expr = p_expr(operand, env)?;
1780 mk_unop(Usilence, expr)
1782 let expr = p_expr_impl(ExprLocation::TopLevel, operand, env, Some(pos))?;
1786 let expr = p_expr(operand, env)?;
1788 Some(TK::PlusPlus) if postfix => mk_unop(Upincr, expr),
1789 Some(TK::MinusMinus) if postfix => mk_unop(Updecr, expr),
1790 Some(TK::PlusPlus) => mk_unop(Uincr, expr),
1791 Some(TK::MinusMinus) => mk_unop(Udecr, expr),
1792 Some(TK::Exclamation) => mk_unop(Unot, expr),
1793 Some(TK::Tilde) => mk_unop(Utild, expr),
1794 Some(TK::Plus) => mk_unop(Uplus, expr),
1795 Some(TK::Minus) => mk_unop(Uminus, expr),
1796 Some(TK::Await) => lift_await(pos, expr, env, location),
1797 Some(TK::Readonly) => Ok(process_readonly_expr(expr)),
1798 Some(TK::Clone) => Ok(E_::mk_clone(expr)),
1799 Some(TK::Print) => Ok(E_::mk_call(
1803 E_::mk_id(ast::Id(pos, special_functions::ECHO.into())),
1806 vec![(ast::ParamKind::Pnormal, expr)],
1809 Some(TK::Dollar) => {
1810 raise_parsing_error(op, env, &syntax_error::invalid_variable_name);
1813 _ => missing_syntax("unary operator", node, env),
1817 BinaryExpression(c) => {
1818 use ExprLocation::*;
1819 let rlocation = if token_kind(&c.operator) == Some(TK::Equal) {
1821 AsStatement => RightOfAssignment,
1822 UsingStatement => RightOfAssignmentInUsingStatement,
1828 let bop_ast_node = p_bop(
1831 p_expr(&c.left_operand, env)?,
1832 p_expr_with_loc(rlocation, &c.right_operand, env)?,
1835 if let Some((ast::Bop::Eq(_), lhs, _)) = bop_ast_node.as_binop() {
1836 check_lvalue(lhs, env);
1841 use ExprLocation::*;
1842 match (location, t.kind()) {
1843 (MemberSelect, TK::Variable) => mk_lvar(node, env),
1844 (InDoubleQuotedString, TK::HeredocStringLiteral)
1845 | (InDoubleQuotedString, TK::HeredocStringLiteralHead)
1846 | (InDoubleQuotedString, TK::HeredocStringLiteralTail) => Ok(E_::String(
1847 wrap_unescaper(unescape_heredoc, text_str(node, env))?,
1849 (InDoubleQuotedString, _) => {
1850 Ok(E_::String(wrap_unescaper(unesc_dbl, text_str(node, env))?))
1855 | (UsingStatement, _)
1856 | (RightOfAssignment, _)
1857 | (RightOfAssignmentInUsingStatement, _)
1858 | (RightOfReturn, _)
1859 | (CallReceiver, _) => Ok(E_::mk_id(pos_name(node, env)?)),
1862 YieldExpression(c) => {
1863 use ExprLocation::*;
1864 env.saw_yield = true;
1865 if location != AsStatement
1866 && location != RightOfAssignment
1867 && location != RightOfAssignmentInUsingStatement
1869 raise_parsing_error(node, env, &syntax_error::invalid_yield);
1871 if c.operand.is_missing() {
1872 Ok(E_::mk_yield(ast::Afield::AFvalue(E::new(
1878 Ok(E_::mk_yield(p_afield(&c.operand, env)?))
1881 ScopeResolutionExpression(c) => {
1882 let qual = p_expr(&c.qualifier, env)?;
1883 if let E_::Id(id) = &qual.2 {
1884 fail_if_invalid_reified_generic(node, env, &id.1);
1886 match &c.name.children {
1887 Token(token) if token.kind() == TK::Variable => {
1888 let ast::Id(p, name) = pos_name(&c.name, env)?;
1889 Ok(E_::mk_class_get(
1890 ast::ClassId((), pos, ast::ClassId_::CIexpr(qual)),
1891 ast::ClassGetExpr::CGstring((p, name)),
1893 ExprLocation::CallReceiver => ast::PropOrMethod::IsMethod,
1894 _ => ast::PropOrMethod::IsProp,
1899 let E(_, p, expr_) = p_expr(&c.name, env)?;
1901 E_::String(id) => Ok(E_::mk_class_const(
1902 ast::ClassId((), pos, ast::ClassId_::CIexpr(qual)),
1905 String::from_utf8(id.into())
1906 .map_err(|e| Error::Failwith(e.to_string()))?,
1910 let ast::Id(p, n) = *id;
1911 Ok(E_::mk_class_const(
1912 ast::ClassId((), pos, ast::ClassId_::CIexpr(qual)),
1917 let ast::Lid(p, (_, n)) = *id;
1918 Ok(E_::mk_class_get(
1919 ast::ClassId((), pos, ast::ClassId_::CIexpr(qual)),
1920 ast::ClassGetExpr::CGstring((p, n)),
1922 ExprLocation::CallReceiver => ast::PropOrMethod::IsMethod,
1923 _ => ast::PropOrMethod::IsProp,
1927 _ => Ok(E_::mk_class_get(
1928 ast::ClassId((), pos, ast::ClassId_::CIexpr(qual)),
1929 ast::ClassGetExpr::CGexpr(E((), p, expr_)),
1931 ExprLocation::CallReceiver => ast::PropOrMethod::IsMethod,
1932 _ => ast::PropOrMethod::IsProp,
1939 CastExpression(c) => Ok(E_::mk_cast(
1940 p_hint(&c.type_, env)?,
1941 p_expr(&c.operand, env)?,
1943 PrefixedCodeExpression(c) => {
1944 let src_expr = p_expr(&c.expression, env)?;
1946 let hint = p_hint(&c.prefix, env)?;
1948 let desugar_result = desugar(&hint, src_expr, env);
1949 for (pos, msg) in desugar_result.errors {
1950 raise_parsing_error_pos(&pos, env, &msg);
1953 Ok(desugar_result.expr.2)
1955 ConditionalExpression(c) => {
1956 let alter = p_expr(&c.alternative, env)?;
1957 let consequence = mp_optional(p_expr, &c.consequence, env)?;
1958 let condition = p_expr(&c.test, env)?;
1959 Ok(E_::mk_eif(condition, consequence, alter))
1961 SubscriptExpression(c) => Ok(E_::mk_array_get(
1962 p_expr(&c.receiver, env)?,
1963 mp_optional(p_expr, &c.index, env)?,
1965 EmbeddedSubscriptExpression(c) => Ok(E_::mk_array_get(
1966 p_expr(&c.receiver, env)?,
1967 mp_optional(|n, e| p_expr_with_loc(location, n, e), &c.index, env)?,
1969 ShapeExpression(c) => Ok(E_::Shape(could_map(
1970 |n: S<'a>, e: &mut Env<'a>| mp_shape_expression_field(&p_expr, n, e),
1974 ObjectCreationExpression(c) => p_expr_impl_(location, &c.object, env, Some(pos)),
1975 ConstructorCall(c) => {
1976 let (args, varargs) = split_args_vararg(&c.argument_list, env)?;
1977 let (e, hl) = match &c.type_.children {
1978 GenericTypeSpecifier(c) => {
1979 let name = pos_name(&c.class_type, env)?;
1980 let hints = match &c.argument_list.children {
1981 TypeArguments(c) => could_map(p_targ, &c.types, env)?,
1982 _ => missing_syntax("generic type arguments", &c.argument_list, env)?,
1984 (mk_id_expr(name), hints)
1986 SimpleTypeSpecifier(_) => {
1987 let name = pos_name(&c.type_, env)?;
1988 (mk_id_expr(name), vec![])
1990 _ => (p_expr(&c.type_, env)?, vec![]),
1992 if let E_::Id(name) = &e.2 {
1993 fail_if_invalid_reified_generic(node, env, &name.1);
1994 fail_if_invalid_class_creation(node, env, &name.1);
1997 ast::ClassId((), pos, ast::ClassId_::CIexpr(e)),
1999 args.into_iter().map(|(_, e)| e).collect(),
2004 GenericTypeSpecifier(c) => {
2005 if !c.argument_list.is_missing() {
2006 raise_parsing_error(&c.argument_list, env, &syntax_error::targs_not_allowed)
2008 Ok(E_::mk_id(pos_name(&c.class_type, env)?))
2010 LiteralExpression(c) => p_expr_lit(location, node, &c.expression, env),
2011 PrefixedStringExpression(c) => {
2012 /* Temporarily allow only`re`- prefixed strings */
2013 let name_text = text(&c.name, env);
2014 if name_text != "re" {
2015 raise_parsing_error(node, env, &syntax_error::non_re_prefix);
2017 Ok(E_::mk_prefixed_string(name_text, p_expr(&c.str, env)?))
2019 IsExpression(c) => Ok(E_::mk_is(
2020 p_expr(&c.left_operand, env)?,
2021 p_hint(&c.right_operand, env)?,
2023 AsExpression(c) => Ok(E_::mk_as(
2024 p_expr(&c.left_operand, env)?,
2025 p_hint(&c.right_operand, env)?,
2028 NullableAsExpression(c) => Ok(E_::mk_as(
2029 p_expr(&c.left_operand, env)?,
2030 p_hint(&c.right_operand, env)?,
2033 UpcastExpression(c) => Ok(E_::mk_upcast(
2034 p_expr(&c.left_operand, env)?,
2035 p_hint(&c.right_operand, env)?,
2037 AnonymousFunction(c) => {
2038 let p_arg = |n: S<'a>, e: &mut Env<'a>| match &n.children {
2039 Token(_) => mk_name_lid(n, e),
2040 _ => missing_syntax("use variable", n, e),
2042 let ctxs = p_contexts(&c.ctx_list, env)?;
2043 let unsafe_ctxs = ctxs.clone();
2044 let p_use = |n: S<'a>, e: &mut Env<'a>| match &n.children {
2045 AnonymousFunctionUseClause(c) => could_map(p_arg, &c.variables, e),
2048 let suspension_kind = mk_suspension_kind(&c.async_keyword);
2049 let (body, yield_) = {
2050 let mut env1 = Env::clone_and_unset_toplevel_if_toplevel(env);
2051 mp_yielding(&p_function_body, &c.body, env1.as_mut())?
2053 let doc_comment = extract_docblock(node, env).or_else(|| env.top_docblock().clone());
2054 let user_attributes = p_user_attributes(&c.attribute_spec, env)?;
2055 let external = c.body.is_external();
2056 let params = could_map(p_fun_param, &c.parameters, env)?;
2057 let name_pos = p_fun_pos(node, env);
2058 let fun = ast::Fun_ {
2059 span: p_pos(node, env),
2060 readonly_this: None, // set in process_readonly_expr
2062 readonly_ret: mp_optional(p_readonly, &c.readonly_return, env)?,
2063 ret: ast::TypeHint((), mp_optional(p_hint, &c.type_, env)?),
2064 name: ast::Id(name_pos, String::from(";anonymous")),
2066 where_constraints: vec![],
2067 body: ast::FuncBody { fb_ast: body },
2068 fun_kind: mk_fun_kind(suspension_kind, yield_),
2076 let uses = p_use(&c.use_, env).unwrap_or_else(|_| vec![]);
2077 Ok(E_::mk_efun(fun, uses))
2079 AwaitableCreationExpression(c) => {
2080 let suspension_kind = mk_suspension_kind(&c.async_);
2081 let (blk, yld) = mp_yielding(&p_function_body, &c.compound_statement, env)?;
2082 let user_attributes = p_user_attributes(&c.attribute_spec, env)?;
2083 let external = c.compound_statement.is_external();
2084 let name_pos = p_fun_pos(node, env);
2085 let body = ast::Fun_ {
2088 readonly_this: None, // set in process_readonly_expr
2089 readonly_ret: None, // TODO: awaitable creation expression
2090 ret: ast::TypeHint((), None),
2091 name: ast::Id(name_pos, String::from(";anonymous")),
2093 where_constraints: vec![],
2094 body: ast::FuncBody {
2095 fb_ast: if blk.is_empty() {
2096 let pos = p_pos(&c.compound_statement, env);
2097 vec![ast::Stmt::noop(pos)]
2102 fun_kind: mk_fun_kind(suspension_kind, yld),
2104 ctxs: None, // TODO(T70095684)
2105 unsafe_ctxs: None, // TODO(T70095684)
2111 E::new((), pos, E_::mk_lfun(body, vec![])),
2117 XHPExpression(c) if c.open.is_xhp_open() => {
2118 if let XHPOpen(c1) = &c.open.children {
2119 let name = pos_name(&c1.name, env)?;
2120 let attrs = could_map(p_xhp_attr, &c1.attributes, env)?;
2121 let exprs = aggregate_xhp_tokens(env, &c.body)?
2123 .map(|n| p_xhp_embedded(unesc_xhp, n, env))
2124 .collect::<Result<Vec<_>, _>>()?;
2126 let id = if env.empty_ns_env.disable_xhp_element_mangling {
2127 ast::Id(name.0, name.1)
2129 ast::Id(name.0, String::from(":") + &name.1)
2133 // TODO: update pos_name to support prefix
2137 failwith("expect xhp open")
2140 EnumClassLabelExpression(c) => {
2141 /* Foo#Bar can be the following:
2142 * - short version: Foo is None/missing and we only have #Bar
2143 * - Foo is a name -> fully qualified Foo#Bar
2144 * - Foo is a function call prefix (can happen during auto completion)
2145 * $c->foo#Bar or C::foo#Bar
2147 let ast::Id(label_pos, label_name) = pos_name(&c.expression, env)?;
2148 if c.qualifier.is_missing() {
2149 Ok(E_::mk_enum_class_label(None, label_name))
2150 } else if c.qualifier.is_name() {
2151 let name = pos_name(&c.qualifier, env)?;
2152 Ok(E_::mk_enum_class_label(Some(name), label_name))
2153 } else if label_name.ends_with("AUTO332") {
2154 // This can happen during parsing in auto-complete mode
2155 // In such case, the "label_name" must end with AUTO332
2156 // We want to treat this as a method call even though we haven't yet seen the arguments
2157 let recv = p_expr_with_loc(ExprLocation::CallReceiver, &c.qualifier, env);
2160 let enum_class_label = E_::mk_enum_class_label(None, label_name);
2161 let enum_class_label = ast::Expr::new((), label_pos, enum_class_label);
2165 vec![(ast::ParamKind::Pnormal, enum_class_label)],
2169 Err(err) => Err(err),
2172 missing_syntax_(Some(E_::Null), "method call", node, env)
2175 _ => missing_syntax_(Some(E_::Null), "expression", node, env),
2179 fn check_lvalue<'a>(ast::Expr(_, p, expr_): &ast::Expr, env: &mut Env<'a>) {
2181 let mut raise = |s| raise_parsing_error_pos(p, env, s);
2184 if og.as_ref().3 == ast::PropOrMethod::IsMethod {
2185 raise("Invalid lvalue")
2188 (_, ast::Expr(_, _, Id(_)), ast::OgNullFlavor::OGNullsafe, _) => {
2189 raise("?-> syntax is not supported for lvalues")
2191 (_, ast::Expr(_, _, Id(sid)), _, _) if sid.1.as_bytes()[0] == b':' => {
2192 raise("->: syntax is not supported for lvalues")
2199 if let ClassConst(_) = (ag.0).2 {
2200 raise("Array-like class consts are not valid lvalues");
2205 check_lvalue(i, env);
2208 Darray(_) | Varray(_) | Shape(_) | Collection(_) | Null | True | False | Id(_)
2209 | Clone(_) | ClassConst(_) | Int(_) | Float(_) | PrefixedString(_) | String(_)
2210 | String2(_) | Yield(_) | Await(_) | Cast(_) | Unop(_) | Binop(_) | Eif(_) | New(_)
2211 | Efun(_) | Lfun(_) | Xml(_) | Import(_) | Pipe(_) | Is(_) | As(_) | Call(_) => {
2212 raise("Invalid lvalue")
2218 fn p_xhp_embedded<'a, F>(escaper: F, node: S<'a>, env: &mut Env<'a>) -> Result<ast::Expr, Error>
2220 F: FnOnce(&[u8]) -> Vec<u8>,
2222 if let Some(kind) = token_kind(node) {
2223 if env.codegen() && TK::XHPStringLiteral == kind {
2224 let p = p_pos(node, env);
2225 /* for XHP string literals (attribute values) just extract
2226 value from quotes and decode HTML entities */
2227 let text = html_entities::decode(get_quoted_content(node.full_text(env.source_text())));
2228 Ok(ast::Expr::new((), p, E_::make_string(text)))
2229 } else if env.codegen() && TK::XHPBody == kind {
2230 let p = p_pos(node, env);
2231 /* for XHP body - only decode HTML entities */
2232 let text = html_entities::decode(&unesc_xhp(node.full_text(env.source_text())));
2233 Ok(ast::Expr::new((), p, E_::make_string(text)))
2235 let p = p_pos(node, env);
2236 let s = escaper(node.full_text(env.source_text()));
2237 Ok(ast::Expr::new((), p, E_::make_string(s)))
2244 fn p_xhp_attr<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::XhpAttribute, Error> {
2245 match &node.children {
2246 XHPSimpleAttribute(c) => {
2247 let attr_expr = &c.expression;
2248 let name = p_pstring(&c.name, env)?;
2249 let expr = if attr_expr.is_braced_expression()
2250 && env.file_mode() == file_info::Mode::Mhhi
2253 ast::Expr::new((), env.mk_none_pos(), E_::Null)
2255 p_xhp_embedded(unesc_xhp_attr, attr_expr, env)?
2257 let xhp_simple = ast::XhpSimple {
2262 Ok(ast::XhpAttribute::XhpSimple(xhp_simple))
2264 XHPSpreadAttribute(c) => {
2265 let expr = p_xhp_embedded(unesc_xhp, &c.expression, env)?;
2266 Ok(ast::XhpAttribute::XhpSpread(expr))
2268 _ => missing_syntax("XHP attribute", node, env),
2272 fn aggregate_xhp_tokens<'a>(env: &mut Env<'a>, nodes: S<'a>) -> Result<Vec<S<'a>>, Error> {
2273 let nodes = nodes.syntax_node_to_list_skip_separator();
2274 let mut state = (None, None, vec![]); // (start, end, result)
2275 let mut combine = |state: &mut (Option<S<'a>>, Option<S<'a>>, Vec<S<'a>>)| {
2276 match (state.0, state.1) {
2277 (Some(s), None) => state.2.push(s),
2278 (Some(s), Some(e)) => {
2281 .concatenate(s.get_token().unwrap(), e.get_token().unwrap());
2282 let node = env.arena.alloc(Syntax::make_token(token));
2293 Token(t) if t.kind() == TK::XHPComment => {
2294 if state.0.is_some() {
2295 combine(&mut state)?;
2299 if state.0.is_none() {
2306 combine(&mut state)?;
2311 combine(&mut state)?;
2321 ) -> Result<ast::Expr_, Error> {
2323 let mk = |op, l, r| Ok(E_::mk_binop(op, l, r));
2324 let mk_eq = |op, l, r| Ok(E_::mk_binop(Eq(Some(Box::new(op))), l, r));
2325 match token_kind(node) {
2326 Some(TK::Equal) => mk(Eq(None), lhs, rhs),
2327 Some(TK::Bar) => mk(Bar, lhs, rhs),
2328 Some(TK::Ampersand) => mk(Amp, lhs, rhs),
2329 Some(TK::Plus) => mk(Plus, lhs, rhs),
2330 Some(TK::Minus) => mk(Minus, lhs, rhs),
2331 Some(TK::Star) => mk(Star, lhs, rhs),
2332 Some(TK::Carat) => mk(Xor, lhs, rhs),
2333 Some(TK::Slash) => mk(Slash, lhs, rhs),
2334 Some(TK::Dot) => mk(Dot, lhs, rhs),
2335 Some(TK::Percent) => mk(Percent, lhs, rhs),
2336 Some(TK::LessThan) => mk(Lt, lhs, rhs),
2337 Some(TK::GreaterThan) => mk(Gt, lhs, rhs),
2338 Some(TK::EqualEqual) => mk(Eqeq, lhs, rhs),
2339 Some(TK::LessThanEqual) => mk(Lte, lhs, rhs),
2340 Some(TK::GreaterThanEqual) => mk(Gte, lhs, rhs),
2341 Some(TK::StarStar) => mk(Starstar, lhs, rhs),
2342 Some(TK::ExclamationEqual) => mk(Diff, lhs, rhs),
2343 Some(TK::BarEqual) => mk_eq(Bar, lhs, rhs),
2344 Some(TK::PlusEqual) => mk_eq(Plus, lhs, rhs),
2345 Some(TK::MinusEqual) => mk_eq(Minus, lhs, rhs),
2346 Some(TK::StarEqual) => mk_eq(Star, lhs, rhs),
2347 Some(TK::StarStarEqual) => mk_eq(Starstar, lhs, rhs),
2348 Some(TK::SlashEqual) => mk_eq(Slash, lhs, rhs),
2349 Some(TK::DotEqual) => mk_eq(Dot, lhs, rhs),
2350 Some(TK::PercentEqual) => mk_eq(Percent, lhs, rhs),
2351 Some(TK::CaratEqual) => mk_eq(Xor, lhs, rhs),
2352 Some(TK::AmpersandEqual) => mk_eq(Amp, lhs, rhs),
2353 Some(TK::BarBar) => mk(Barbar, lhs, rhs),
2354 Some(TK::AmpersandAmpersand) => mk(Ampamp, lhs, rhs),
2355 Some(TK::LessThanLessThan) => mk(Ltlt, lhs, rhs),
2356 Some(TK::GreaterThanGreaterThan) => mk(Gtgt, lhs, rhs),
2357 Some(TK::EqualEqualEqual) => mk(Eqeqeq, lhs, rhs),
2358 Some(TK::LessThanLessThanEqual) => mk_eq(Ltlt, lhs, rhs),
2359 Some(TK::GreaterThanGreaterThanEqual) => mk_eq(Gtgt, lhs, rhs),
2360 Some(TK::ExclamationEqualEqual) => mk(Diff2, lhs, rhs),
2361 Some(TK::LessThanEqualGreaterThan) => mk(Cmp, lhs, rhs),
2362 Some(TK::QuestionQuestion) => mk(QuestionQuestion, lhs, rhs),
2363 Some(TK::QuestionQuestionEqual) => mk_eq(QuestionQuestion, lhs, rhs),
2364 /* The ugly duckling; In the FFP, `|>` is parsed as a
2365 * `BinaryOperator`, whereas the typed AST has separate constructors for
2366 * Pipe and Binop. This is why we don't just project onto a
2367 * `bop`, but a `expr -> expr -> expr_`.
2369 Some(TK::BarGreaterThan) => {
2371 ast::Lid::from_counter(pos, env.next_local_id(), special_idents::DOLLAR_DOLLAR);
2372 Ok(E_::mk_pipe(lid, lhs, rhs))
2374 Some(TK::QuestionColon) => Ok(E_::mk_eif(lhs, None, rhs)),
2375 _ => missing_syntax("binary operator", node, env),
2379 fn p_exprs_with_loc<'a>(n: S<'a>, e: &mut Env<'a>) -> Result<(Pos, Vec<ast::Expr>), Error> {
2380 let loc = p_pos(n, e);
2381 let p_expr = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::Expr, Error> {
2382 p_expr_with_loc(ExprLocation::UsingStatement, n, e)
2384 Ok((loc, could_map(p_expr, n, e)?))
2387 fn p_stmt_list_<'a>(
2389 mut nodes: Iter<'_, S<'a>>,
2391 ) -> Result<Vec<ast::Stmt>, Error> {
2394 match nodes.next() {
2395 Some(n) => match &n.children {
2396 UsingStatementFunctionScoped(c) => {
2397 let body = p_stmt_list_(pos, nodes, env)?;
2398 let f = |e: &mut Env<'a>| {
2401 ast::Stmt_::mk_using(ast::UsingStmt {
2402 is_block_scoped: false,
2403 has_await: !c.await_keyword.is_missing(),
2404 exprs: p_exprs_with_loc(&c.expression, e)?,
2409 let using = lift_awaits_in_statement_(f, Either::Right(pos), env)?;
2414 r.push(p_stmt(n, env)?);
2422 fn handle_loop_body<'a>(pos: Pos, node: S<'a>, env: &mut Env<'a>) -> Result<ast::Stmt, Error> {
2423 let list: Vec<_> = node.syntax_node_to_list_skip_separator().collect();
2424 let blk: Vec<_> = p_stmt_list_(&pos, list.iter(), env)?
2426 .filter(|stmt| !stmt.1.is_noop())
2428 let body = if blk.is_empty() {
2433 Ok(ast::Stmt::new(pos, ast::Stmt_::mk_block(body)))
2436 fn is_simple_assignment_await_expression<'a>(node: S<'a>) -> bool {
2437 match &node.children {
2438 BinaryExpression(c) => {
2439 token_kind(&c.operator) == Some(TK::Equal)
2440 && is_simple_await_expression(&c.right_operand)
2446 fn is_simple_await_expression<'a>(node: S<'a>) -> bool {
2447 match &node.children {
2448 PrefixUnaryExpression(c) => token_kind(&c.operator) == Some(TK::Await),
2453 fn with_new_nonconcurrent_scope<'a, F, R>(f: F, env: &mut Env<'a>) -> R
2455 F: FnOnce(&mut Env<'a>) -> R,
2457 let saved_lifted_awaits = env.lifted_awaits.take();
2458 let result = f(env);
2459 env.lifted_awaits = saved_lifted_awaits;
2463 fn with_new_concurrent_scope<'a, F, R>(
2466 ) -> Result<(LiftedAwaitExprs, R), Error>
2468 F: FnOnce(&mut Env<'a>) -> Result<R, Error>,
2470 let saved_lifted_awaits = env.lifted_awaits.replace(LiftedAwaits {
2472 lift_kind: LiftedAwaitKind::LiftedFromConcurrent,
2474 let result = f(env);
2475 let lifted_awaits = mem::replace(&mut env.lifted_awaits, saved_lifted_awaits);
2476 let result = result?;
2477 let awaits = match lifted_awaits {
2478 Some(la) => process_lifted_awaits(la)?,
2479 None => failwith("lifted awaits should not be None")?,
2481 Ok((awaits, result))
2484 fn process_lifted_awaits(mut awaits: LiftedAwaits) -> Result<LiftedAwaitExprs, Error> {
2485 for await_ in awaits.awaits.iter() {
2486 if (await_.1).1.is_none() {
2487 return failwith("none pos in lifted awaits");
2492 .sort_unstable_by(|a1, a2| Pos::cmp(&(a1.1).1, &(a2.1).1));
2496 fn clear_statement_scope<'a, F, R>(f: F, env: &mut Env<'a>) -> R
2498 F: FnOnce(&mut Env<'a>) -> R,
2500 use LiftedAwaitKind::*;
2501 match &env.lifted_awaits {
2502 Some(LiftedAwaits { lift_kind, .. }) if *lift_kind == LiftedFromStatement => {
2503 let saved_lifted_awaits = env.lifted_awaits.take();
2504 let result = f(env);
2505 env.lifted_awaits = saved_lifted_awaits;
2512 fn lift_awaits_in_statement<'a, F>(f: F, node: S<'a>, env: &mut Env<'a>) -> Result<ast::Stmt, Error>
2514 F: FnOnce(&mut Env<'a>) -> Result<ast::Stmt, Error>,
2516 lift_awaits_in_statement_(f, Either::Left(node), env)
2519 fn lift_awaits_in_statement_<'a, F>(
2521 pos: Either<S<'a>, &Pos>,
2523 ) -> Result<ast::Stmt, Error>
2525 F: FnOnce(&mut Env<'a>) -> Result<ast::Stmt, Error>,
2527 use LiftedAwaitKind::*;
2528 let (lifted_awaits, result) = match env.lifted_awaits {
2529 Some(LiftedAwaits { lift_kind, .. }) if lift_kind == LiftedFromConcurrent => {
2533 let saved = env.lifted_awaits.replace(LiftedAwaits {
2535 lift_kind: LiftedFromStatement,
2537 let result = f(env);
2538 let lifted_awaits = mem::replace(&mut env.lifted_awaits, saved);
2539 let result = result?;
2540 (lifted_awaits, result)
2543 if let Some(lifted_awaits) = lifted_awaits {
2544 if !lifted_awaits.awaits.is_empty() {
2545 let awaits = process_lifted_awaits(lifted_awaits)?;
2546 let pos = match pos {
2547 Either::Left(n) => p_pos(n, env),
2548 Either::Right(p) => p.clone(),
2550 return Ok(ast::Stmt::new(
2552 ast::Stmt_::mk_awaitall(awaits, vec![result]),
2563 location: ExprLocation,
2564 ) -> Result<ast::Expr_, Error> {
2565 use ExprLocation::*;
2566 match (&env.lifted_awaits, location) {
2567 (_, UsingStatement) | (_, RightOfAssignmentInUsingStatement) | (None, _) => {
2568 Ok(E_::mk_await(expr))
2571 if location != AsStatement {
2572 let name = env.make_tmp_var_name();
2573 let lid = ast::Lid::new(parent_pos, name.clone());
2574 let await_lid = ast::Lid::new(expr.1.clone(), name);
2575 let await_ = (Some(await_lid), expr);
2576 if let Some(aw) = env.lifted_awaits.as_mut() {
2577 aw.awaits.push(await_)
2579 Ok(E_::mk_lvar(lid))
2581 if let Some(aw) = env.lifted_awaits.as_mut() {
2582 aw.awaits.push((None, expr))
2590 fn p_stmt<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Stmt, Error> {
2591 clear_statement_scope(
2593 let docblock = extract_docblock(node, e);
2594 e.push_docblock(docblock);
2595 let result = p_stmt_(node, e);
2603 fn p_stmt_<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Stmt, Error> {
2604 let pos = p_pos(node, env);
2605 use ast::{Stmt, Stmt_ as S_};
2606 let new = Stmt::new;
2607 match &node.children {
2608 SwitchStatement(c) => {
2609 let p_label = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::GenCase, Error> {
2611 CaseLabel(c) => Ok(aast::GenCase::Case(aast::Case(
2612 p_expr(&c.expression, e)?,
2615 DefaultLabel(_) => Ok(aast::GenCase::Default(aast::DefaultCase(
2619 _ => missing_syntax("switch label", n, e),
2622 let p_section = |n: S<'a>, e: &mut Env<'a>| -> Result<Vec<ast::GenCase>, Error> {
2624 SwitchSection(c) => {
2625 let mut blk = could_map(p_stmt, &c.statements, e)?;
2626 if !c.fallthrough.is_missing() {
2627 blk.push(new(e.mk_none_pos(), S_::Fallthrough));
2629 let mut labels = could_map(p_label, &c.labels, e)?;
2630 match labels.last_mut() {
2631 Some(aast::GenCase::Default(aast::DefaultCase(_, b))) => *b = blk,
2632 Some(aast::GenCase::Case(aast::Case(_, b))) => *b = blk,
2633 _ => raise_parsing_error(n, e, "Malformed block result"),
2637 _ => missing_syntax("switch section", n, e),
2641 let f = |env: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2642 let cases = itertools::concat(could_map(p_section, &c.sections, env)?);
2644 let last_is_default = matches!(cases.last(), Some(aast::GenCase::Default(_)));
2646 let (cases, mut defaults): (Vec<ast::Case>, Vec<ast::DefaultCase>) =
2647 cases.into_iter().partition_map(|case| match case {
2648 aast::GenCase::Case(x @ aast::Case(..)) => Either::Left(x),
2649 aast::GenCase::Default(x @ aast::DefaultCase(..)) => Either::Right(x),
2652 if defaults.len() > 1 {
2653 let aast::DefaultCase(pos, _) = &defaults[1];
2654 raise_parsing_error_pos(pos, env, &syntax_error::multiple_defaults_in_switch);
2657 let default = match defaults.pop() {
2658 Some(default @ aast::DefaultCase(..)) => {
2659 if last_is_default {
2662 let aast::DefaultCase(pos, _) = default;
2663 raise_parsing_error_pos(
2666 &syntax_error::default_switch_case_not_last,
2677 S_::mk_switch(p_expr(&c.expression, env)?, cases, default),
2680 lift_awaits_in_statement(f, node, env)
2683 let p_else_if = |n: S<'a>, e: &mut Env<'a>| -> Result<(ast::Expr, ast::Block), Error> {
2685 ElseifClause(c) => {
2686 Ok((p_expr(&c.condition, e)?, p_block(true, &c.statement, e)?))
2688 _ => missing_syntax("elseif clause", n, e),
2691 let f = |env: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2692 let condition = p_expr(&c.condition, env)?;
2693 let statement = p_block(true /* remove noop */, &c.statement, env)?;
2694 let else_ = match &c.else_clause.children {
2695 ElseClause(c) => p_block(true, &c.statement, env)?,
2696 Missing => vec![mk_noop(env)],
2697 _ => missing_syntax("else clause", &c.else_clause, env)?,
2699 let else_ifs = could_map(p_else_if, &c.elseif_clauses, env)?;
2700 let else_if = else_ifs
2703 .fold(else_, |child, (cond, stmts)| {
2704 vec![new(pos.clone(), S_::mk_if(cond, stmts, child))]
2706 Ok(new(pos, S_::mk_if(condition, statement, else_if)))
2708 lift_awaits_in_statement(f, node, env)
2710 ExpressionStatement(c) => {
2711 let expr = &c.expression;
2712 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2713 if expr.is_missing() {
2714 Ok(new(pos, S_::Noop))
2718 S_::mk_expr(p_expr_with_loc(ExprLocation::AsStatement, expr, e)?),
2722 if is_simple_assignment_await_expression(expr) || is_simple_await_expression(expr) {
2725 lift_awaits_in_statement(f, node, env)
2728 CompoundStatement(c) => handle_loop_body(pos, &c.statements, env),
2729 SyntaxList(_) => handle_loop_body(pos, node, env),
2730 ThrowStatement(c) => lift_awaits_in_statement(
2731 |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2732 Ok(new(pos, S_::mk_throw(p_expr(&c.expression, e)?)))
2737 DoStatement(c) => Ok(new(
2740 p_block(false /* remove noop */, &c.body, env)?,
2741 p_expr(&c.condition, env)?,
2744 WhileStatement(c) => Ok(new(
2746 S_::mk_while(p_expr(&c.condition, env)?, p_block(true, &c.body, env)?),
2748 UsingStatementBlockScoped(c) => {
2749 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2752 S_::mk_using(ast::UsingStmt {
2753 is_block_scoped: true,
2754 has_await: !&c.await_keyword.is_missing(),
2755 exprs: p_exprs_with_loc(&c.expressions, e)?,
2756 block: p_block(false, &c.body, e)?,
2760 lift_awaits_in_statement(f, node, env)
2762 UsingStatementFunctionScoped(c) => {
2763 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2766 S_::mk_using(ast::UsingStmt {
2767 is_block_scoped: false,
2768 has_await: !&c.await_keyword.is_missing(),
2769 exprs: p_exprs_with_loc(&c.expression, e)?,
2770 block: vec![mk_noop(e)],
2774 lift_awaits_in_statement(f, node, env)
2776 ForStatement(c) => {
2777 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2778 let ini = p_expr_l(&c.initializer, e)?;
2779 let ctr = mp_optional(p_expr, &c.control, e)?;
2780 let eol = p_expr_l(&c.end_of_loop, e)?;
2781 let blk = p_block(true, &c.body, e)?;
2782 Ok(Stmt::new(pos, S_::mk_for(ini, ctr, eol, blk)))
2784 lift_awaits_in_statement(f, node, env)
2786 ForeachStatement(c) => {
2787 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2788 let col = p_expr(&c.collection, e)?;
2789 let akw = match token_kind(&c.await_keyword) {
2790 Some(TK::Await) => Some(p_pos(&c.await_keyword, e)),
2793 let value = p_expr(&c.value, e)?;
2794 let akv = match (akw, &c.key.children) {
2795 (Some(p), Missing) => ast::AsExpr::AwaitAsV(p, value),
2796 (None, Missing) => ast::AsExpr::AsV(value),
2797 (Some(p), _) => ast::AsExpr::AwaitAsKv(p, p_expr(&c.key, e)?, value),
2798 (None, _) => ast::AsExpr::AsKv(p_expr(&c.key, e)?, value),
2800 let blk = p_block(true, &c.body, e)?;
2801 Ok(new(pos, S_::mk_foreach(col, akv, blk)))
2803 lift_awaits_in_statement(f, node, env)
2805 TryStatement(c) => Ok(new(
2808 p_block(false, &c.compound_statement, env)?,
2810 |n: S<'a>, e| match &n.children {
2811 CatchClause(c) => Ok(ast::Catch(
2812 pos_name(&c.type_, e)?,
2813 lid_from_name(&c.variable, e)?,
2814 p_block(true, &c.body, e)?,
2816 _ => missing_syntax("catch clause", n, e),
2821 match &c.finally_clause.children {
2822 FinallyClause(c) => p_block(false, &c.body, env)?,
2827 ReturnStatement(c) => {
2828 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2829 let expr = match &c.expression.children {
2831 _ => Some(p_expr_with_loc(
2832 ExprLocation::RightOfReturn,
2837 Ok(ast::Stmt::new(pos, ast::Stmt_::mk_return(expr)))
2839 if is_simple_await_expression(&c.expression) {
2842 lift_awaits_in_statement(f, node, env)
2845 YieldBreakStatement(_) => Ok(ast::Stmt::new(pos, ast::Stmt_::mk_yield_break())),
2846 EchoStatement(c) => {
2847 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2848 let echo = match &c.keyword.children {
2849 QualifiedName(_) | SimpleTypeSpecifier(_) | Token(_) => {
2850 let name = pos_name(&c.keyword, e)?;
2851 ast::Expr::new((), name.0.clone(), E_::mk_id(name))
2853 _ => missing_syntax("id", &c.keyword, e)?,
2855 let args = could_map(p_expr_for_normal_argument, &c.expressions, e)?;
2858 S_::mk_expr(ast::Expr::new(
2861 E_::mk_call(echo, vec![], args, None),
2865 lift_awaits_in_statement(f, node, env)
2867 UnsetStatement(c) => {
2868 let f = |e: &mut Env<'a>| -> Result<ast::Stmt, Error> {
2869 let args = could_map(p_expr_for_normal_argument, &c.variables, e)?;
2871 .for_each(|(_, arg)| check_mutate_class_const(arg, node, e));
2872 let unset = match &c.keyword.children {
2873 QualifiedName(_) | SimpleTypeSpecifier(_) | Token(_) => {
2874 let name = pos_name(&c.keyword, e)?;
2875 ast::Expr::new((), name.0.clone(), E_::mk_id(name))
2877 _ => missing_syntax("id", &c.keyword, e)?,
2881 S_::mk_expr(ast::Expr::new(
2884 E_::mk_call(unset, vec![], args, None),
2888 lift_awaits_in_statement(f, node, env)
2890 BreakStatement(_) => Ok(new(pos, S_::Break)),
2891 ContinueStatement(_) => Ok(new(pos, S_::Continue)),
2892 ConcurrentStatement(c) => {
2893 let keyword_pos = p_pos(&c.keyword, env);
2894 let (lifted_awaits, Stmt(stmt_pos, stmt)) =
2895 with_new_concurrent_scope(|e: &mut Env<'a>| p_stmt(&c.statement, e), env)?;
2896 let stmt = match stmt {
2897 S_::Block(stmts) => {
2900 /* Reuse tmp vars from lifted_awaits, this is safe because there will
2901 * always be more awaits with tmp vars than statements with assignments */
2902 let mut tmp_vars = lifted_awaits
2904 .filter_map(|lifted_await| lifted_await.0.as_ref().map(|x| &x.1));
2905 let mut body_stmts = vec![];
2906 let mut assign_stmts = vec![];
2907 for n in stmts.into_iter() {
2908 if !n.is_assign_expr() {
2913 if let Some(tv) = tmp_vars.next() {
2914 if let Stmt(p1, S_::Expr(expr)) = n {
2915 if let E(_, p2, E_::Binop(bop)) = *expr {
2916 if let (Eq(op), e1, e2) = *bop {
2917 let tmp_n = E::mk_lvar(&e2.1, &(tv.1));
2918 if tmp_n.lvar_name() != e2.lvar_name() {
2931 body_stmts.push(new_n);
2933 let assign_stmt = new(
2938 E_::mk_binop(Eq(op), e1, tmp_n),
2941 assign_stmts.push(assign_stmt);
2947 failwith("Expect assignment stmt")?;
2949 raise_parsing_error_pos(
2952 &syntax_error::statement_without_await_in_concurrent_block,
2957 body_stmts.append(&mut assign_stmts);
2958 new(stmt_pos, S_::mk_block(body_stmts))
2960 _ => failwith("Unexpected concurrent stmt structure")?,
2962 Ok(new(keyword_pos, S_::mk_awaitall(lifted_awaits, vec![stmt])))
2964 MarkupSection(_) => p_markup(node, env),
2965 _ => missing_syntax_(
2966 Some(new(env.mk_none_pos(), S_::Noop)),
2973 fn check_mutate_class_const<'a>(e: &ast::Expr, node: S<'a>, env: &mut Env<'a>) {
2975 E_::ArrayGet(c) if c.1.is_some() => check_mutate_class_const(&c.0, node, env),
2976 E_::ClassConst(_) => raise_parsing_error(node, env, &syntax_error::const_mutation),
2981 fn p_markup<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Stmt, Error> {
2982 match &node.children {
2983 MarkupSection(c) => {
2984 let markup_hashbang = &c.hashbang;
2985 let markup_suffix = &c.suffix;
2986 let pos = p_pos(node, env);
2987 let f = pos.filename();
2988 let expected_suffix_offset = if markup_hashbang.is_missing() {
2991 markup_hashbang.width() + 1 /* for newline */
2993 if (f.has_extension("hack") || f.has_extension("hackpartial"))
2994 && !(markup_suffix.is_missing())
2996 let ext = f.path().extension().unwrap(); // has_extension ensures this is a Some
2997 raise_parsing_error(node, env, &syntax_error::error1060(ext.to_str().unwrap()));
2998 } else if f.has_extension("php")
2999 && !markup_suffix.is_missing()
3000 && markup_suffix.offset() != Some(expected_suffix_offset)
3002 raise_parsing_error(markup_suffix, env, &syntax_error::error1001);
3004 let stmt_ = ast::Stmt_::mk_markup((pos.clone(), text(markup_hashbang, env)));
3005 Ok(ast::Stmt::new(pos, stmt_))
3007 _ => failwith("invalid node"),
3011 fn p_modifiers<'a, F: Fn(R, modifier::Kind) -> R, R>(
3016 ) -> Result<(modifier::KindSet, R), Error> {
3017 let mut kind_set = modifier::KindSet::new();
3018 for n in node.syntax_node_to_list_skip_separator() {
3019 let token_kind = token_kind(n).and_then(modifier::from_token_kind);
3023 init = on_kind(init, kind);
3025 _ => missing_syntax("kind", n, env)?,
3028 Ok((kind_set, init))
3031 fn p_kinds<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<modifier::KindSet, Error> {
3032 p_modifiers(|_, _| {}, (), node, env).map(|r| r.0)
3035 /// Apply `f` to every item in `node`, and build a vec of the values returned.
3036 fn could_map<'a, R, F>(f: F, node: S<'a>, env: &mut Env<'a>) -> Result<Vec<R>, Error>
3038 F: Fn(S<'a>, &mut Env<'a>) -> Result<R, Error>,
3040 let nodes = node.syntax_node_to_list_skip_separator();
3041 let mut res = Vec::with_capacity(nodes.size_hint().0);
3043 res.push(f(n, env)?);
3048 fn p_visibility<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Option<ast::Visibility>, Error> {
3049 let first_vis = |r: Option<ast::Visibility>, kind| r.or_else(|| modifier::to_visibility(kind));
3050 p_modifiers(first_vis, None, node, env).map(|r| r.1)
3053 fn p_visibility_last_win<'a>(
3056 ) -> Result<Option<ast::Visibility>, Error> {
3057 let last_vis = |r, kind| modifier::to_visibility(kind).or(r);
3058 p_modifiers(last_vis, None, node, env).map(|r| r.1)
3061 fn p_visibility_last_win_or<'a>(
3064 default: ast::Visibility,
3065 ) -> Result<ast::Visibility, Error> {
3066 p_visibility_last_win(node, env).map(|v| v.unwrap_or(default))
3069 fn has_soft(attrs: &[ast::UserAttribute]) -> bool {
3070 attrs.iter().any(|attr| attr.name.1 == special_attrs::SOFT)
3073 fn soften_hint(attrs: &[ast::UserAttribute], hint: ast::Hint) -> ast::Hint {
3074 if has_soft(attrs) {
3075 ast::Hint::new(hint.0.clone(), ast::Hint_::Hsoft(hint))
3081 fn strip_ns(name: &str) -> &str {
3082 match name.chars().next() {
3083 Some('\\') => &name[1..],
3088 fn has_polymorphic_context_single<'a>(env: &mut Env<'a>, hint: &ast::Hint) -> bool {
3089 use ast::Hint_::{Haccess, Happly, HfunContext, Hvar};
3091 HfunContext(_) => true,
3092 Haccess(ref root, _) => match &*root.1 {
3093 Happly(oxidized::ast::Id(_, id), _) => {
3094 let s = id.as_str();
3095 /* TODO(coeffects) There is an opportunity to represent this structurally
3096 * in the AST if we refactor so generic hints lower as Habstr instead of
3097 * Happly, like we do in the direct decl parser. */
3098 strip_ns(s) == naming_special_names_rust::typehints::THIS
3099 || env.fn_generics_mut().contains_key(s)
3100 || env.cls_generics_mut().contains_key(s)
3109 fn has_polymorphic_context<'a>(env: &mut Env<'a>, contexts: Option<&ast::Contexts>) -> bool {
3110 if let Some(ast::Contexts(_, ref context_hints)) = contexts {
3111 return context_hints
3113 .any(|c| has_polymorphic_context_single(env, c));
3119 fn has_any_policied_context(contexts: Option<&ast::Contexts>) -> bool {
3120 if let Some(ast::Contexts(_, ref context_hints)) = contexts {
3121 return context_hints.iter().any(|hint| match &*hint.1 {
3122 ast::Hint_::Happly(ast::Id(_, id), _) => {
3123 naming_special_names_rust::coeffects::is_any_zoned(id)
3132 // For polymorphic context with form `ctx $f`
3133 // require that `(function (ts)[_]: t) $f` exists
3134 // rewrite as `(function (ts)[ctx $f]: t) $f`
3135 // add a type parameter named "T/[ctx $f]"
3136 fn rewrite_fun_ctx<'a>(
3138 tparams: &mut Vec<ast::Tparam>,
3139 hint: &mut ast::Hint,
3142 use ast::{Hint_, ReifyKind, Variance};
3145 |p| raise_parsing_error_pos(p, env, &syntax_error::ctx_fun_invalid_type_hint(name));
3147 Hint_::Hfun(ref mut hf) => {
3148 if let Some(ast::Contexts(ref p, ref mut hl)) = &mut hf.ctxs {
3149 if let [ref mut h] = *hl.as_mut_slice() {
3150 if let Hint_::Happly(ast::Id(ref pos, s), _) = &*h.1 {
3152 *h.1 = Hint_::HfunContext(name.to_string());
3153 tparams.push(ast::Tparam {
3154 variance: Variance::Invariant,
3155 name: ast::Id(h.0.clone(), format!("T/[ctx {}]", name)),
3157 constraints: vec![],
3158 reified: ReifyKind::Erased,
3159 user_attributes: vec![],
3174 Hint_::Hlike(ref mut h) | Hint_::Hoption(ref mut h) => {
3175 rewrite_fun_ctx(env, tparams, h, name)
3177 Hint_::Happly(ast::Id(_, ref type_name), ref mut targs)
3178 if type_name == special_typehints::SUPPORTDYN =>
3180 if let Some(ref mut h) = targs.first_mut() {
3181 rewrite_fun_ctx(env, tparams, h, name)
3186 _ => invalid(&hint.0),
3189 fn rewrite_effect_polymorphism<'a>(
3191 params: &mut Vec<ast::FunParam>,
3192 tparams: &mut Vec<ast::Tparam>,
3193 contexts: Option<&ast::Contexts>,
3194 where_constraints: &mut Vec<ast::WhereConstraintHint>,
3196 use ast::{Hint, Hint_, ReifyKind, Variance};
3197 use Hint_::{Haccess, Happly, HfunContext, Hvar};
3199 if !has_polymorphic_context(env, contexts) {
3202 let ast::Contexts(ref _p, ref context_hints) = contexts.as_ref().unwrap();
3203 let tp = |name, v| ast::Tparam {
3204 variance: Variance::Invariant,
3208 reified: ReifyKind::Erased,
3209 user_attributes: vec![],
3212 // For polymorphic context with form `$g::C`
3213 // if $g's type is not a type parameter
3214 // add one named "T/$g" constrained by $g's type
3215 // replace $g's type hint
3216 // let Tg denote $g's final type (must be a type parameter).
3217 // add a type parameter "T/[$g::C]"
3218 // add a where constraint T/[$g::C] = Tg :: C
3219 let rewrite_arg_ctx = |
3221 tparams: &mut Vec<ast::Tparam>,
3222 where_constraints: &mut Vec<ast::WhereConstraintHint>,
3229 Happly(ast::Id(_, ref type_name), _) => {
3230 if !tparams.iter().any(|h| h.name.1 == *type_name) {
3231 // If the parameter is X $g, create tparam `T$g as X` and replace $g's type hint
3232 let id = ast::Id(param_pos.clone(), "T/".to_string() + name);
3235 vec![(ast::ConstraintKind::ConstraintAs, hint.clone())],
3237 *hint = ast::Hint::new(param_pos.clone(), Happly(id, vec![]));
3239 let right = ast::Hint::new(
3240 context_pos.clone(),
3241 Haccess(hint.clone(), vec![cst.clone()]),
3243 let left_id = ast::Id(context_pos.clone(), format!("T/[{}::{}]", name, &cst.1));
3244 tparams.push(tp(left_id.clone(), vec![]));
3245 let left = ast::Hint::new(context_pos.clone(), Happly(left_id, vec![]));
3246 where_constraints.push(ast::WhereConstraintHint(
3248 ast::ConstraintKind::ConstraintEq,
3252 _ => raise_parsing_error_pos(&hint.0, env, &syntax_error::ctx_var_invalid_type_hint(name)),
3255 let mut hint_by_param: HashMap<&str, (&mut Option<ast::Hint>, &Pos, aast::IsVariadic)> =
3257 for param in params.iter_mut() {
3258 hint_by_param.insert(
3259 param.name.as_ref(),
3260 (&mut param.type_hint.1, ¶m.pos, param.is_variadic),
3265 for context_hint in context_hints {
3266 match *context_hint.1 {
3267 HfunContext(ref name) => match hint_by_param.get_mut::<str>(name) {
3268 Some((hint_opt, param_pos, _is_variadic)) => match hint_opt {
3269 Some(_) if env.codegen() => {}
3270 Some(ref mut param_hint) => rewrite_fun_ctx(env, tparams, param_hint, name),
3271 None => raise_parsing_error_pos(
3274 &syntax_error::ctx_var_missing_type_hint(name),
3278 None => raise_parsing_error_pos(
3281 &syntax_error::ctx_var_invalid_parameter(name),
3284 Haccess(ref root, ref csts) => {
3285 if let Hvar(ref name) = *root.1 {
3286 match hint_by_param.get_mut::<str>(name) {
3287 Some((hint_opt, param_pos, is_variadic)) => {
3289 raise_parsing_error_pos(
3292 &syntax_error::ctx_var_variadic(name),
3296 Some(_) if env.codegen() => {}
3297 Some(ref mut param_hint) => {
3298 let mut rewrite = |h| {
3310 match *param_hint.1 {
3311 Hint_::Hlike(ref mut h) => match *h.1 {
3312 Hint_::Hoption(ref mut hinner) => rewrite(hinner),
3315 Hint_::Hoption(ref mut h) => rewrite(h),
3316 _ => rewrite(param_hint),
3319 None => raise_parsing_error_pos(
3322 &syntax_error::ctx_var_missing_type_hint(name),
3327 None => raise_parsing_error_pos(
3330 &syntax_error::ctx_var_invalid_parameter(name),
3333 } else if let Happly(ast::Id(_, ref id), _) = *root.1 {
3334 // For polymorphic context with form `T::*::C` where `T` is a reified generic
3335 // add a type parameter "T/[T::*::C]"
3336 // add a where constraint T/[T::*::C] = T :: C
3337 let haccess_string = |id, csts: &Vec<aast::Sid>| {
3338 format!("{}::{}", id, csts.iter().map(|c| c.1.clone()).join("::"))
3341 match env.get_reification(id) {
3342 None => {} // not a generic
3343 Some(false) => raise_parsing_error_pos(
3346 &syntax_error::ctx_generic_invalid(id, haccess_string(id, csts)),
3348 Some(true) if env.codegen() => {}
3350 let left_id = ast::Id(
3351 context_hint.0.clone(),
3352 format!("T/[{}]", haccess_string(id, csts)),
3354 tparams.push(tp(left_id.clone(), vec![]));
3356 ast::Hint::new(context_hint.0.clone(), Happly(left_id, vec![]));
3357 where_constraints.push(ast::WhereConstraintHint(
3359 ast::ConstraintKind::ConstraintEq,
3360 context_hint.clone(),
3371 fn p_fun_param_default_value<'a>(
3374 ) -> Result<Option<ast::Expr>, Error> {
3375 match &node.children {
3376 SimpleInitializer(c) => mp_optional(p_expr, &c.value, env),
3381 fn p_param_kind<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::ParamKind, Error> {
3382 match token_kind(node) {
3383 Some(TK::Inout) => Ok(ast::ParamKind::Pinout(p_pos(node, env))),
3384 None => Ok(ast::ParamKind::Pnormal),
3385 _ => missing_syntax("param kind", node, env),
3389 fn p_readonly<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::ReadonlyKind, Error> {
3390 match token_kind(node) {
3391 Some(TK::Readonly) => Ok(ast::ReadonlyKind::Readonly),
3392 _ => missing_syntax("readonly", node, env),
3396 fn param_template<'a>(node: S<'a>, env: &Env<'_>) -> ast::FunParam {
3397 let pos = p_pos(node, env);
3400 type_hint: ast::TypeHint((), None),
3403 name: text(node, env),
3405 callconv: ast::ParamKind::Pnormal,
3407 user_attributes: vec![],
3412 fn p_fun_param<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::FunParam, Error> {
3413 match &node.children {
3414 ParameterDeclaration(ParameterDeclarationChildren {
3423 let (is_variadic, name) = match &name.children {
3424 DecoratedExpression(DecoratedExpressionChildren {
3428 let decorator = text_str(decorator, env);
3429 match &expression.children {
3430 DecoratedExpression(c) => {
3431 let nested_expression = &c.expression;
3432 let nested_decorator = text_str(&c.decorator, env);
3434 decorator == "..." || nested_decorator == "...",
3438 _ => (decorator == "...", expression),
3443 let user_attributes = p_user_attributes(attribute, env)?;
3444 let pos = p_pos(name, env);
3445 let name = text(name, env);
3446 let hint = mp_optional(p_hint, type_, env)?;
3447 let hint = hint.map(|h| soften_hint(&user_attributes, h));
3449 if is_variadic && !user_attributes.is_empty() {
3450 raise_parsing_error(
3453 &syntax_error::no_attributes_on_variadic_parameter,
3458 type_hint: ast::TypeHint((), hint),
3463 expr: p_fun_param_default_value(default_value, env)?,
3464 callconv: p_param_kind(call_convention, env)?,
3465 readonly: mp_optional(p_readonly, readonly, env)?,
3466 /* implicit field via constructor parameter.
3467 * This is always None except for constructors and the modifier
3468 * can be only Public or Protected or Private.
3470 visibility: p_visibility(visibility, env)?,
3473 VariadicParameter(_) => {
3474 let mut param = param_template(node, env);
3475 param.is_variadic = true;
3478 Token(_) if text_str(node, env) == "..." => {
3479 let mut param = param_template(node, env);
3480 param.is_variadic = true;
3483 _ => missing_syntax("function parameter", node, env),
3487 fn p_tconstraint_ty<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Hint, Error> {
3488 match &node.children {
3489 TypeConstraint(c) => p_hint(&c.type_, env),
3490 _ => missing_syntax("type constraint", node, env),
3494 fn p_tconstraint<'a>(
3497 ) -> Result<(ast::ConstraintKind, ast::Hint), Error> {
3498 match &node.children {
3499 TypeConstraint(c) => Ok((
3500 match token_kind(&c.keyword) {
3501 Some(TK::As) => ast::ConstraintKind::ConstraintAs,
3502 Some(TK::Super) => ast::ConstraintKind::ConstraintSuper,
3503 Some(TK::Equal) => ast::ConstraintKind::ConstraintEq,
3504 _ => missing_syntax("constraint operator", &c.keyword, env)?,
3506 p_hint(&c.type_, env)?,
3508 _ => missing_syntax("type constraint", node, env),
3512 fn p_tparam<'a>(is_class: bool, node: S<'a>, env: &mut Env<'a>) -> Result<ast::Tparam, Error> {
3513 match &node.children {
3514 TypeParameter(TypeParameterChildren {
3522 let user_attributes = p_user_attributes(attribute_spec, env)?;
3523 let is_reified = !reified.is_missing();
3525 let type_name = text(name, env);
3527 env.cls_generics_mut().insert(type_name, is_reified);
3529 // this is incorrect for type aliases, but it doesn't affect any check
3530 env.fn_generics_mut().insert(type_name, is_reified);
3533 let variance = match token_kind(variance) {
3534 Some(TK::Plus) => ast::Variance::Covariant,
3535 Some(TK::Minus) => ast::Variance::Contravariant,
3536 _ => ast::Variance::Invariant,
3538 if is_reified && variance != ast::Variance::Invariant {
3539 raise_parsing_error(node, env, &syntax_error::non_invariant_reified_generic);
3541 let reified = match (is_reified, has_soft(&user_attributes)) {
3542 (true, true) => ast::ReifyKind::SoftReified,
3543 (true, false) => ast::ReifyKind::Reified,
3544 _ => ast::ReifyKind::Erased,
3546 let parameters = p_tparam_l(is_class, param_params, env)?;
3549 name: pos_name(name, env)?,
3551 constraints: could_map(p_tconstraint, constraints, env)?,
3556 _ => missing_syntax("type parameter", node, env),
3564 ) -> Result<Vec<ast::Tparam>, Error> {
3565 match &node.children {
3566 Missing => Ok(vec![]),
3567 TypeParameters(c) => could_map(|n, e| p_tparam(is_class, n, e), &c.parameters, env),
3568 _ => missing_syntax("type parameter", node, env),
3572 /// Lowers multiple constraints into a hint pair (lower_bound, upper_bound)
3573 fn p_ctx_constraints<'a>(
3576 ) -> Result<(Option<ast::Hint>, Option<ast::Hint>), Error> {
3577 let constraints = could_map(
3579 if let ContextConstraint(c) = &node.children {
3580 if let Some(hint) = p_context_list_to_intersection(&c.ctx_list, env)? {
3581 Ok(match token_kind(&c.keyword) {
3582 Some(TK::Super) => Either::Left(hint),
3583 Some(TK::As) => Either::Right(hint),
3584 _ => missing_syntax("constraint operator", &c.keyword, env)?,
3587 missing_syntax("contexts", &c.keyword, env)?
3590 missing_syntax("context constraint", node, env)?
3596 let (super_constraint, as_constraint) = constraints.into_iter().partition_map(|x| x);
3597 let require_one = &mut |kind: &str, cs: Vec<_>| {
3600 "Multiple `{}` constraints on a ctx constant are not allowed",
3603 raise_parsing_error(node, env, &msg);
3605 cs.into_iter().next()
3608 require_one("super", super_constraint),
3609 require_one("as", as_constraint),
3613 fn p_contexts<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Option<ast::Contexts>, Error> {
3614 match &node.children {
3615 Missing => Ok(None),
3617 let hints = could_map(&p_hint, &c.types, env)?;
3618 let pos = p_pos(node, env);
3619 let ctxs = ast::Contexts(pos, hints);
3622 _ => missing_syntax("contexts", node, env),
3626 fn p_context_list_to_intersection<'a>(
3629 ) -> Result<Option<ast::Hint>, Error> {
3630 Ok(p_contexts(ctx_list, env)?.map(|t| ast::Hint::new(t.0, ast::Hint_::Hintersection(t.1))))
3633 fn p_fun_hdr<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<FunHdr, Error> {
3634 match &node.children {
3635 FunctionDeclarationHeader(FunctionDeclarationHeaderChildren {
3639 type_parameter_list,
3646 if name.value.is_missing() {
3647 raise_parsing_error(name, env, &syntax_error::empty_method_name);
3649 let kinds = p_kinds(modifiers, env)?;
3650 let has_async = kinds.has(modifier::ASYNC);
3651 let readonly_this = if kinds.has(modifier::READONLY) {
3652 Some(ast::ReadonlyKind::Readonly)
3656 let readonly_ret = mp_optional(p_readonly, readonly_return, env)?;
3657 let mut type_parameters = p_tparam_l(false, type_parameter_list, env)?;
3658 let mut parameters = could_map(p_fun_param, parameter_list, env)?;
3659 let contexts = p_contexts(contexts, env)?;
3660 let mut constrs = p_where_constraint(false, node, where_clause, env)?;
3661 rewrite_effect_polymorphism(
3664 &mut type_parameters,
3668 let return_type = mp_optional(p_hint, type_, env)?;
3669 let suspension_kind = mk_suspension_kind_(has_async);
3670 let name = pos_name(name, env)?;
3671 let unsafe_contexts = contexts.clone();
3681 readonly_return: readonly_ret,
3685 LambdaSignature(LambdaSignatureChildren {
3692 let readonly_ret = mp_optional(p_readonly, readonly_return, env)?;
3693 let mut header = FunHdr::make_empty(env);
3694 header.parameters = could_map(p_fun_param, parameters, env)?;
3695 let contexts = p_contexts(contexts, env)?;
3696 let unsafe_contexts = contexts.clone();
3697 header.contexts = contexts;
3698 header.unsafe_contexts = unsafe_contexts;
3699 header.return_type = mp_optional(p_hint, type_, env)?;
3700 header.readonly_return = readonly_ret;
3703 Token(_) => Ok(FunHdr::make_empty(env)),
3704 _ => missing_syntax("function header", node, env),
3708 fn p_fun_pos<'a>(node: S<'a>, env: &Env<'_>) -> Pos {
3709 let get_pos = |n: S<'a>, p: Pos| -> Pos {
3710 if let FunctionDeclarationHeader(c1) = &n.children {
3711 if !c1.keyword.is_missing() {
3712 return Pos::btw_nocheck(p_pos(&c1.keyword, env), p);
3717 let p = p_pos(node, env);
3718 match &node.children {
3719 FunctionDeclaration(c) if env.codegen() => get_pos(&c.declaration_header, p),
3720 MethodishDeclaration(c) if env.codegen() => get_pos(&c.function_decl_header, p),
3725 fn p_block<'a>(remove_noop: bool, node: S<'a>, env: &mut Env<'a>) -> Result<ast::Block, Error> {
3726 let ast::Stmt(p, stmt_) = p_stmt(node, env)?;
3727 if let ast::Stmt_::Block(blk) = stmt_ {
3728 if remove_noop && blk.len() == 1 && blk[0].1.is_noop() {
3733 Ok(vec![ast::Stmt(p, stmt_)])
3737 fn mk_noop(env: &Env<'_>) -> ast::Stmt {
3738 ast::Stmt::noop(env.mk_none_pos())
3741 fn p_function_body<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Block, Error> {
3742 let mk_noop_result = |e: &Env<'_>| Ok(vec![mk_noop(e)]);
3743 let f = |e: &mut Env<'a>| -> Result<ast::Block, Error> {
3744 match &node.children {
3745 Missing => Ok(vec![]),
3746 CompoundStatement(c) => {
3747 let compound_statements = &c.statements.children;
3748 let compound_right_brace = &c.right_brace.children;
3749 match (compound_statements, compound_right_brace) {
3750 (Missing, Token(_)) => mk_noop_result(e),
3751 (SyntaxList(t), _) if t.len() == 1 && t[0].is_yield() => {
3756 if !e.top_level_statements
3757 && ((e.file_mode() == file_info::Mode::Mhhi && !e.codegen())
3762 p_block(false /*remove noop*/, node, e)
3768 let f = |e: &mut Env<'a>| {
3769 let expr = p_expr(node, e)?;
3772 ast::Stmt_::mk_return(Some(expr)),
3775 if is_simple_await_expression(node) {
3778 Ok(vec![lift_awaits_in_statement(f, node, e)?])
3783 with_new_nonconcurrent_scope(f, env)
3786 fn mk_suspension_kind<'a>(async_keyword: S<'a>) -> SuspensionKind {
3787 mk_suspension_kind_(!async_keyword.is_missing())
3790 fn mk_suspension_kind_(has_async: bool) -> SuspensionKind {
3792 SuspensionKind::SKAsync
3794 SuspensionKind::SKSync
3798 fn mk_fun_kind(suspension_kind: SuspensionKind, yield_: bool) -> ast::FunKind {
3799 use ast::FunKind::*;
3800 use SuspensionKind::*;
3801 match (suspension_kind, yield_) {
3802 (SKSync, true) => FGenerator,
3803 (SKAsync, true) => FAsyncGenerator,
3804 (SKSync, false) => FSync,
3805 (SKAsync, false) => FAsync,
3809 fn process_attribute_constructor_call<'a>(
3811 constructor_call_argument_list: S<'a>,
3812 constructor_call_type: S<'a>,
3814 ) -> Result<ast::UserAttribute, Error> {
3815 let name = pos_name(constructor_call_type, env)?;
3816 if name.1.eq_ignore_ascii_case("__reified") || name.1.eq_ignore_ascii_case("__hasreifiedparent")
3818 raise_parsing_error(node, env, &syntax_error::reified_attribute);
3819 } else if name.1.eq_ignore_ascii_case(special_attrs::SOFT)
3820 && constructor_call_argument_list
3821 .syntax_node_to_list_skip_separator()
3825 raise_parsing_error(node, env, &syntax_error::soft_no_arguments);
3827 let params = could_map(
3828 |n: S<'a>, e: &mut Env<'a>| -> Result<ast::Expr, Error> {
3829 is_valid_attribute_arg(n, e);
3832 constructor_call_argument_list,
3835 Ok(ast::UserAttribute { name, params })
3838 // Arguments to attributes must be literals (int, string, etc), collections
3839 // (eg vec, dict, keyset, etc), Foo::class strings, shapes, string
3840 // concatenations, or tuples.
3841 fn is_valid_attribute_arg<'a>(node: S<'a>, env: &mut Env<'a>) {
3842 let mut is_valid_list = |nodes: S<'a>| {
3845 is_valid_attribute_arg(n, e);
3852 match &node.children {
3853 ParenthesizedExpression(c) => is_valid_attribute_arg(&c.expression, env),
3854 // Normal literals (string, int, etc)
3855 LiteralExpression(_) => {}
3857 ScopeResolutionExpression(c) => {
3858 if let Some(TK::Class) = token_kind(&c.name) {
3860 raise_parsing_error(node, env, &syntax_error::expression_as_attribute_arguments);
3864 PrefixUnaryExpression(c) => {
3865 is_valid_attribute_arg(&c.operand, env);
3866 match token_kind(&c.operator) {
3867 Some(TK::Minus) => {}
3868 Some(TK::Plus) => {}
3870 raise_parsing_error(node, env, &syntax_error::expression_as_attribute_arguments)
3874 // String concatenation
3875 BinaryExpression(c) => {
3876 if let Some(TK::Dot) = token_kind(&c.operator) {
3877 is_valid_attribute_arg(&c.left_operand, env);
3878 is_valid_attribute_arg(&c.right_operand, env);
3880 raise_parsing_error(node, env, &syntax_error::expression_as_attribute_arguments);
3883 // Top-level Collections
3884 DarrayIntrinsicExpression(c) => is_valid_list(&c.members),
3885 DictionaryIntrinsicExpression(c) => is_valid_list(&c.members),
3886 KeysetIntrinsicExpression(c) => is_valid_list(&c.members),
3887 VarrayIntrinsicExpression(c) => is_valid_list(&c.members),
3888 VectorIntrinsicExpression(c) => is_valid_list(&c.members),
3889 ShapeExpression(c) => is_valid_list(&c.fields),
3890 TupleExpression(c) => is_valid_list(&c.items),
3891 // Collection Internals
3892 FieldInitializer(c) => {
3893 is_valid_attribute_arg(&c.name, env);
3894 is_valid_attribute_arg(&c.value, env);
3896 ElementInitializer(c) => {
3897 is_valid_attribute_arg(&c.key, env);
3898 is_valid_attribute_arg(&c.value, env);
3900 // Everything else is not allowed
3901 _ => raise_parsing_error(node, env, &syntax_error::expression_as_attribute_arguments),
3905 fn p_user_attribute<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::UserAttribute>, Error> {
3906 let p_attr = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::UserAttribute, Error> {
3908 ConstructorCall(c) => {
3909 process_attribute_constructor_call(node, &c.argument_list, &c.type_, e)
3911 _ => missing_syntax("attribute", node, e),
3914 match &node.children {
3915 FileAttributeSpecification(c) => could_map(p_attr, &c.attributes, env),
3916 OldAttributeSpecification(c) => could_map(p_attr, &c.attributes, env),
3917 AttributeSpecification(c) => could_map(
3918 |n: S<'a>, e: &mut Env<'a>| -> Result<ast::UserAttribute, Error> {
3920 Attribute(c) => p_attr(&c.attribute_name, e),
3921 _ => missing_syntax("attribute", node, e),
3927 _ => missing_syntax("attribute specification", node, env),
3931 fn p_user_attributes<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::UserAttribute>, Error> {
3932 let attributes = could_map(&p_user_attribute, node, env)?;
3933 Ok(attributes.into_iter().flatten().collect())
3936 fn mp_yielding<'a, F, R>(p: F, node: S<'a>, env: &mut Env<'a>) -> Result<(R, bool), Error>
3938 F: FnOnce(S<'a>, &mut Env<'a>) -> Result<R, Error>,
3940 let outer_saw_yield = env.saw_yield;
3941 env.saw_yield = false;
3942 let r = p(node, env);
3943 let saw_yield = env.saw_yield;
3944 env.saw_yield = outer_saw_yield;
3948 fn mk_empty_ns_env(env: &Env<'_>) -> RcOc<NamespaceEnv> {
3949 RcOc::clone(&env.empty_ns_env)
3952 fn extract_docblock<'a>(node: S<'a>, env: &Env<'_>) -> Option<DocComment> {
3953 #[derive(Copy, Clone, Eq, PartialEq)]
3966 // `parse` mixes loop and recursion to use less stack space.
3972 ) -> Option<(usize, usize, String)> {
3973 let is_whitespace = |c| c == ' ' || c == '\t' || c == '\n' || c == '\r';
3974 let mut s = (start, state, idx);
3975 let chars = str.as_bytes();
3977 if s.2 == str.len() {
3982 match (s.1, chars[s.2] as char) {
3983 (LineCmt, '\n') => s = (next, Free, next),
3984 (EndEmbedded, '/') => s = (next, Free, next),
3986 let r = parse(str, next, Free, next);
3988 d @ Some(_) => break d,
3989 None => break Some((s.0, s.2 + 1, String::from(&str[s.0..s.2 + 1]))),
3992 /* PHP has line comments delimited by a # */
3993 (Free, '#') => s = (next, LineCmt, next),
3994 /* All other comment delimiters start with a / */
3995 (Free, '/') => s = (s.2, SawSlash, next),
3996 /* After a / in trivia, we must see either another / or a * */
3997 (SawSlash, '/') => s = (next, LineCmt, next),
3998 (SawSlash, '*') => s = (s.0, MaybeDoc, next),
3999 (MaybeDoc, '*') => s = (s.0, MaybeDoc2, next),
4000 (MaybeDoc, _) => s = (s.0, EmbeddedCmt, next),
4001 (MaybeDoc2, '/') => s = (next, Free, next),
4002 /* Doc comments have a space after the second star */
4003 (MaybeDoc2, c) if is_whitespace(c) => s = (s.0, DocComment, s.2),
4004 (MaybeDoc2, _) => s = (s.0, EmbeddedCmt, next),
4005 (DocComment, '*') => s = (s.0, EndDoc, next),
4006 (DocComment, _) => s = (s.0, DocComment, next),
4007 (EndDoc, _) => s = (s.0, DocComment, next),
4008 /* A * without a / does not end an embedded comment */
4009 (EmbeddedCmt, '*') => s = (s.0, EndEmbedded, next),
4010 (EndEmbedded, '*') => s = (s.0, EndEmbedded, next),
4011 (EndEmbedded, _) => s = (s.0, EmbeddedCmt, next),
4012 /* Whitespace skips everywhere else */
4013 (_, c) if is_whitespace(c) => s = (s.0, s.1, next),
4014 /* When scanning comments, anything else is accepted */
4015 (LineCmt, _) => s = (s.0, s.1, next),
4016 (EmbeddedCmt, _) => s = (s.0, s.1, next),
4021 let str = node.leading_text(env.indexed_source_text.source_text());
4022 parse(str, 0, Free, 0).map(|(start, end, txt)| {
4023 let anchor = node.leading_start_offset();
4025 .indexed_source_text
4026 .relative_pos(anchor + start, anchor + end);
4027 let ps = (pos, txt);
4028 oxidized::doc_comment::DocComment::new(ps)
4032 fn p_xhp_child<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::XhpChild, Error> {
4033 use ast::XhpChild::*;
4034 use ast::XhpChildOp::*;
4035 match &node.children {
4036 Token(_) => pos_name(node, env).map(ChildName),
4037 PostfixUnaryExpression(c) => {
4038 let operand = p_xhp_child(&c.operand, env)?;
4039 let operator = match token_kind(&c.operator) {
4040 Some(TK::Question) => ChildQuestion,
4041 Some(TK::Plus) => ChildPlus,
4042 Some(TK::Star) => ChildStar,
4043 _ => missing_syntax("xhp children operator", node, env)?,
4045 Ok(ChildUnary(Box::new(operand), operator))
4047 BinaryExpression(c) => {
4048 let left = p_xhp_child(&c.left_operand, env)?;
4049 let right = p_xhp_child(&c.right_operand, env)?;
4050 Ok(ChildBinary(Box::new(left), Box::new(right)))
4052 XHPChildrenParenthesizedList(c) => {
4053 let children: Result<Vec<_>, _> = c
4055 .syntax_node_to_list_skip_separator()
4056 .map(|c| p_xhp_child(c, env))
4058 Ok(ChildList(children?))
4060 _ => missing_syntax("xhp children", node, env),
4064 fn p_class_elt_<'a>(class: &mut ast::Class_, node: S<'a>, env: &mut Env<'a>) -> Result<(), Error> {
4065 use ast::Visibility;
4066 let doc_comment_opt = extract_docblock(node, env);
4067 let has_fun_header =
4068 |m: &MethodishDeclarationChildren<'_, PositionedToken<'a>, PositionedValue<'a>>| {
4070 m.function_decl_header.children,
4071 FunctionDeclarationHeader(_)
4075 |node: S<'a>, name_pos: &Pos, env: &mut Env<'a>| -> Result<Visibility, Error> {
4076 match p_visibility_last_win(node, env)? {
4078 raise_hh_error(env, Naming::method_needs_visibility(name_pos.clone()));
4079 Ok(Visibility::Public)
4084 match &node.children {
4085 ConstDeclaration(c) => {
4086 let user_attributes = p_user_attributes(&c.attribute_spec, env)?;
4087 let kinds = p_kinds(&c.modifiers, env)?;
4088 let has_abstract = kinds.has(modifier::ABSTRACT);
4089 // TODO: make wrap `type_` `doc_comment` by `Rc` in ClassConst to avoid clone
4090 let type_ = mp_optional(p_hint, &c.type_specifier, env)?;
4091 // ocaml's behavior is that if anything throw, it will
4092 // discard all lowered elements. So adding to class
4093 // must be at the last.
4094 let mut class_consts = could_map(
4095 |n: S<'a>, e: &mut Env<'a>| -> Result<ast::ClassConst, Error> {
4097 ConstantDeclarator(c) => {
4098 let id = pos_name(&c.name, e)?;
4099 use aast::ClassConstKind::*;
4100 let kind = if has_abstract {
4101 CCAbstract(mp_optional(p_simple_initializer, &c.initializer, e)?)
4103 CCConcrete(p_simple_initializer(&c.initializer, e)?)
4105 Ok(ast::ClassConst {
4106 user_attributes: user_attributes.clone(),
4107 type_: type_.clone(),
4110 doc_comment: doc_comment_opt.clone(),
4113 _ => missing_syntax("constant declarator", n, e),
4119 Ok(class.consts.append(&mut class_consts))
4121 TypeConstDeclaration(c) => {
4122 use ast::ClassTypeconst::{TCAbstract, TCConcrete};
4123 if !c.type_parameters.is_missing() {
4124 raise_parsing_error(node, env, &syntax_error::tparams_in_tconst);
4126 let user_attributes = p_user_attributes(&c.attribute_spec, env)?;
4127 let type__ = mp_optional(p_hint, &c.type_specifier, env)?
4128 .map(|hint| soften_hint(&user_attributes, hint));
4129 let kinds = p_kinds(&c.modifiers, env)?;
4130 let name = pos_name(&c.name, env)?;
4131 let as_constraint = mp_optional(p_tconstraint_ty, &c.type_constraint, env)?;
4132 let span = p_pos(node, env);
4133 let has_abstract = kinds.has(modifier::ABSTRACT);
4134 let kind = if has_abstract {
4135 TCAbstract(ast::ClassAbstractTypeconst {
4137 super_constraint: None,
4140 } else if let Some(type_) = type__ {
4141 if env.is_typechecker() && as_constraint.is_some() {
4144 NastCheck::partially_abstract_typeconst_definition(name.0.clone()),
4147 TCConcrete(ast::ClassConcreteTypeconst { c_tc_type: type_ })
4151 NastCheck::not_abstract_without_typeconst(name.0.clone()),
4153 missing_syntax("value for the type constant", node, env)?
4155 Ok(class.typeconsts.push(ast::ClassTypeconstDef {
4160 doc_comment: doc_comment_opt,
4164 ContextConstDeclaration(c) => {
4165 use ast::ClassTypeconst::{TCAbstract, TCConcrete};
4166 if !c.type_parameters.is_missing() {
4167 raise_parsing_error(node, env, &syntax_error::tparams_in_tconst);
4169 let name = pos_name(&c.name, env)?;
4170 let context = p_context_list_to_intersection(&c.ctx_list, env)?;
4171 if let Some(ref hint) = context {
4172 use ast::Hint_::{Happly, Hintersection};
4173 let ast::Hint(_, ref h) = hint;
4174 if let Hintersection(hl) = &**h {
4176 if has_polymorphic_context_single(env, h) {
4177 raise_parsing_error(
4180 "Polymorphic contexts on ctx constants are not allowed",
4183 let ast::Hint(_, ref h) = h;
4184 if let Happly(oxidized::ast::Id(_, id), _) = &**h {
4185 if id.as_str().ends_with("_local") {
4186 raise_parsing_error(
4189 "Local contexts on ctx constants are not allowed",
4196 let span = p_pos(node, env);
4197 let kinds = p_kinds(&c.modifiers, env)?;
4198 let has_abstract = kinds.has(modifier::ABSTRACT);
4199 let (super_constraint, as_constraint) = p_ctx_constraints(&c.constraint, env)?;
4200 let kind = if has_abstract {
4201 TCAbstract(ast::ClassAbstractTypeconst {
4206 } else if let Some(c_tc_type) = context {
4207 if env.is_typechecker() && (super_constraint.is_some() || as_constraint.is_some()) {
4208 raise_parsing_error(
4211 "Constraints on a context constant requires it to be abstract",
4214 TCConcrete(ast::ClassConcreteTypeconst { c_tc_type })
4218 NastCheck::not_abstract_without_typeconst(name.0.clone()),
4220 missing_syntax("value for the context constant", node, env)?
4222 Ok(class.typeconsts.push(ast::ClassTypeconstDef {
4225 user_attributes: vec![],
4227 doc_comment: doc_comment_opt,
4231 PropertyDeclaration(c) => {
4232 let user_attributes = p_user_attributes(&c.attribute_spec, env)?;
4234 mp_optional(p_hint, &c.type_, env)?.map(|t| soften_hint(&user_attributes, t));
4235 let kinds = p_kinds(&c.modifiers, env)?;
4236 let vis = p_visibility_last_win_or(&c.modifiers, env, Visibility::Public)?;
4237 let doc_comment = if env.quick_mode {
4242 let name_exprs = could_map(
4243 |n, e| -> Result<(Pos, ast::Sid, Option<ast::Expr>), Error> {
4245 PropertyDeclarator(c) => {
4246 let name = pos_name_(&c.name, e, Some('$'))?;
4247 let pos = p_pos(n, e);
4248 let expr = mp_optional(p_simple_initializer, &c.initializer, e)?;
4249 Ok((pos, name, expr))
4251 _ => missing_syntax("property declarator", n, e),
4258 for (i, name_expr) in name_exprs.into_iter().enumerate() {
4259 class.vars.push(ast::ClassVar {
4260 final_: kinds.has(modifier::FINAL),
4262 abstract_: kinds.has(modifier::ABSTRACT),
4263 readonly: kinds.has(modifier::READONLY),
4265 type_: ast::TypeHint((), type_.clone()),
4268 user_attributes: user_attributes.clone(),
4269 doc_comment: if i == 0 { doc_comment.clone() } else { None },
4270 is_promoted_variadic: false,
4271 is_static: kinds.has(modifier::STATIC),
4277 MethodishDeclaration(c) if has_fun_header(c) => {
4278 // keep cls_generics
4279 *env.fn_generics_mut() = HashMap::default();
4280 let classvar_init = |param: &ast::FunParam| -> (ast::Stmt, ast::ClassVar) {
4281 let cvname = drop_prefix(¶m.name, '$');
4283 let span = match ¶m.expr {
4284 Some(ast::Expr(_, pos_end, _)) => {
4285 Pos::btw(p, pos_end).unwrap_or_else(|_| p.clone())
4289 let e = |expr_: ast::Expr_| -> ast::Expr { ast::Expr::new((), p.clone(), expr_) };
4290 let lid = |s: &str| -> ast::Lid { ast::Lid(p.clone(), (0, s.to_string())) };
4294 ast::Stmt_::mk_expr(e(E_::mk_binop(
4297 e(E_::mk_lvar(lid(special_idents::THIS))),
4298 e(E_::mk_id(ast::Id(p.clone(), cvname.to_string()))),
4299 ast::OgNullFlavor::OGNullthrows,
4300 ast::PropOrMethod::IsProp,
4302 e(E_::mk_lvar(lid(¶m.name))),
4309 // We use the param readonlyness here to represent the
4310 // ClassVar's readonlyness once lowered
4311 // TODO(jjwu): Convert this to an enum when we support
4312 // multiple types of readonlyness
4313 readonly: param.readonly.is_some(),
4314 visibility: param.visibility.unwrap(),
4315 type_: param.type_hint.clone(),
4316 id: ast::Id(p.clone(), cvname.to_string()),
4318 user_attributes: param.user_attributes.clone(),
4320 is_promoted_variadic: param.is_variadic,
4326 let header = &c.function_decl_header;
4327 let h = match &header.children {
4328 FunctionDeclarationHeader(h) => h,
4331 let hdr = p_fun_hdr(header, env)?;
4332 let (mut member_init, mut member_def): (Vec<ast::Stmt>, Vec<ast::ClassVar>) = hdr
4335 .filter_map(|p| p.visibility.map(|_| classvar_init(p)))
4338 let kinds = p_kinds(&h.modifiers, env)?;
4339 let visibility = p_method_vis(&h.modifiers, &hdr.name.0, env)?;
4340 let is_static = kinds.has(modifier::STATIC);
4341 let readonly_this = kinds.has(modifier::READONLY);
4342 *env.in_static_method() = is_static;
4343 check_effect_polymorphic_reification(hdr.contexts.as_ref(), env, node);
4344 let (mut body, body_has_yield) = mp_yielding(p_function_body, &c.function_body, env)?;
4346 member_init.reverse();
4348 member_init.append(&mut body);
4349 let body = member_init;
4350 *env.in_static_method() = false;
4351 let is_abstract = kinds.has(modifier::ABSTRACT);
4352 let is_external = !is_abstract && c.function_body.is_external();
4353 let user_attributes = p_user_attributes(&c.attribute, env)?;
4354 check_effect_memoized(hdr.contexts.as_ref(), &user_attributes, "method", env);
4355 let method = ast::Method_ {
4356 span: p_fun_pos(node, env),
4358 final_: kinds.has(modifier::FINAL),
4360 abstract_: is_abstract,
4364 tparams: hdr.type_parameters,
4365 where_constraints: hdr.constrs,
4366 params: hdr.parameters,
4368 unsafe_ctxs: hdr.unsafe_contexts,
4369 body: ast::FuncBody { fb_ast: body },
4370 fun_kind: mk_fun_kind(hdr.suspension_kind, body_has_yield),
4372 readonly_ret: hdr.readonly_return,
4373 ret: ast::TypeHint((), hdr.return_type),
4374 external: is_external,
4375 doc_comment: doc_comment_opt,
4377 class.vars.append(&mut member_def);
4378 Ok(class.methods.push(method))
4380 TraitUseConflictResolution(c) => {
4381 type Ret = Result<Either<ast::InsteadofAlias, ast::UseAsAlias>, Error>;
4382 let p_item = |n: S<'a>, e: &mut Env<'a>| -> Ret {
4384 TraitUsePrecedenceItem(c) => {
4385 let (qualifier, name) = match &c.name.children {
4386 ScopeResolutionExpression(c) => {
4387 (pos_name(&c.qualifier, e)?, p_pstring(&c.name, e)?)
4389 _ => missing_syntax("trait use precedence item", n, e)?,
4391 let removed_names = could_map(pos_name, &c.removed_names, e)?;
4392 raise_hh_error(e, Naming::unsupported_instead_of(name.0.clone()));
4393 Ok(Either::Left(ast::InsteadofAlias(
4399 TraitUseAliasItem(c) => {
4400 let (qualifier, name) = match &c.aliasing_name.children {
4401 ScopeResolutionExpression(c) => {
4402 (Some(pos_name(&c.qualifier, e)?), p_pstring(&c.name, e)?)
4404 _ => (None, p_pstring(&c.aliasing_name, e)?),
4406 let (kinds, mut vis_raw) = p_modifiers(
4407 |mut acc, kind| -> Vec<ast::UseAsVisibility> {
4408 if let Some(v) = modifier::to_use_as_visibility(kind) {
4417 let vis = if kinds.is_empty() || kinds.has_any(modifier::VISIBILITIES) {
4420 let mut v = vec![ast::UseAsVisibility::UseAsPublic];
4421 v.append(&mut vis_raw);
4424 let aliased_name = if !c.aliased_name.is_missing() {
4425 Some(pos_name(&c.aliased_name, e)?)
4429 raise_hh_error(e, Naming::unsupported_trait_use_as(name.0.clone()));
4430 Ok(Either::Right(ast::UseAsAlias(
4437 _ => missing_syntax("trait use conflict resolution item", n, e),
4440 let mut uses = could_map(p_hint, &c.names, env)?;
4441 let elts = could_map(p_item, &c.clauses, env)?;
4442 class.uses.append(&mut uses);
4443 for elt in elts.into_iter() {
4445 Either::Left(l) => class.insteadof_alias.push(l),
4446 Either::Right(r) => class.use_as_alias.push(r),
4452 let mut uses = could_map(p_hint, &c.names, env)?;
4453 Ok(class.uses.append(&mut uses))
4455 RequireClause(c) => {
4456 let hint = p_hint(&c.name, env)?;
4457 let is_extends = match token_kind(&c.kind) {
4458 Some(TK::Implements) => false,
4459 Some(TK::Extends) => true,
4460 _ => missing_syntax("trait require kind", &c.kind, env)?,
4462 Ok(class.reqs.push((hint, is_extends)))
4464 XHPClassAttributeDeclaration(c) => {
4465 type Ret = Result<Either<ast::XhpAttr, ast::Hint>, Error>;
4466 let p_attr = |node: S<'a>, env: &mut Env<'a>| -> Ret {
4467 let mk_attr_use = |n: S<'a>, env: &mut Env<'a>| {
4468 Ok(Either::Right(ast::Hint(
4470 Box::new(ast::Hint_::Happly(pos_name(n, env)?, vec![])),
4473 match &node.children {
4474 XHPClassAttribute(c) => {
4475 let ast::Id(p, name) = pos_name(&c.name, env)?;
4476 if let TypeConstant(_) = &c.type_.children {
4477 if env.is_typechecker() {
4478 raise_parsing_error(
4481 &syntax_error::xhp_class_attribute_type_constant,
4485 let req = match &c.required.children {
4486 XHPRequired(_) => Some(ast::XhpAttrTag::Required),
4487 XHPLateinit(_) => Some(ast::XhpAttrTag::LateInit),
4490 let pos = if c.initializer.is_missing() {
4493 Pos::btw(&p, &p_pos(&c.initializer, env)).map_err(Error::Failwith)?
4495 let (hint, like, enum_values, enum_) = match &c.type_.children {
4496 XHPEnumType(c1) => {
4497 let p = p_pos(&c.type_, env);
4498 let like = match &c1.like.children {
4500 _ => Some(p_pos(&c1.like, env)),
4502 let vals = could_map(p_expr, &c1.values, env)?;
4503 let mut enum_vals = vec![];
4504 for val in vals.clone() {
4506 ast::Expr(_, _, E_::String(xev)) => enum_vals
4507 .push(ast::XhpEnumValue::XEVString(xev.to_string())),
4508 ast::Expr(_, _, E_::Int(xev)) => match xev.parse() {
4509 Ok(n) => enum_vals.push(ast::XhpEnumValue::XEVInt(n)),
4511 // Since we have parse checks for
4512 // malformed integer literals already,
4513 // we assume this won't happen and ignore
4520 (None, like, enum_vals, Some((p, vals)))
4522 _ => (Some(p_hint(&c.type_, env)?), None, vec![], None),
4524 let init_expr = mp_optional(p_simple_initializer, &c.initializer, env)?;
4525 let xhp_attr = ast::XhpAttr(
4526 ast::TypeHint((), hint.clone()),
4529 xhp_attr: Some(ast::XhpAttrInfo {
4536 visibility: ast::Visibility::Public,
4537 type_: ast::TypeHint((), hint),
4538 id: ast::Id(p, String::from(":") + &name),
4540 user_attributes: vec![],
4542 is_promoted_variadic: false,
4549 Ok(Either::Left(xhp_attr))
4551 XHPSimpleClassAttribute(c) => mk_attr_use(&c.type_, env),
4552 Token(_) => mk_attr_use(node, env),
4553 _ => missing_syntax("XHP attribute", node, env),
4556 let attrs = could_map(p_attr, &c.attributes, env)?;
4557 for attr in attrs.into_iter() {
4559 Either::Left(attr) => class.xhp_attrs.push(attr),
4560 Either::Right(xhp_attr_use) => class.xhp_attr_uses.push(xhp_attr_use),
4565 XHPChildrenDeclaration(c) => {
4566 let p = p_pos(node, env);
4569 .push((p, p_xhp_child(&c.expression, env)?)))
4571 XHPCategoryDeclaration(c) => {
4572 let p = p_pos(node, env);
4573 let categories = could_map(|n, e| p_pstring_(n, e, Some('%')), &c.categories, env)?;
4574 if let Some((_, cs)) = &class.xhp_category {
4575 if let Some(category) = cs.first() {
4576 raise_hh_error(env, NastCheck::multiple_xhp_category(category.0.clone()))
4579 Ok(class.xhp_category = Some((p, categories)))
4581 _ => missing_syntax("class element", node, env),
4585 fn p_class_elt<'a>(class: &mut ast::Class_, node: S<'a>, env: &mut Env<'a>) -> Result<(), Error> {
4586 let r = p_class_elt_(class, node, env);
4588 // match ocaml behavior, don't throw if missing syntax when fail_open is true
4589 Err(Error::MissingSyntax { .. }) if env.fail_open() => Ok(()),
4594 fn contains_class_body<'a>(
4595 c: &ClassishDeclarationChildren<'_, PositionedToken<'a>, PositionedValue<'a>>,
4597 matches!(&c.body.children, ClassishBody(_))
4600 fn p_where_constraint<'a>(
4605 ) -> Result<Vec<ast::WhereConstraintHint>, Error> {
4606 match &node.children {
4607 Missing => Ok(vec![]),
4609 let f = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::WhereConstraintHint, Error> {
4611 WhereConstraint(c) => {
4612 use ast::ConstraintKind::*;
4613 let l = p_hint(&c.left_type, e)?;
4614 let o = &c.operator;
4615 let o = match token_kind(o) {
4616 Some(TK::Equal) => ConstraintEq,
4617 Some(TK::As) => ConstraintAs,
4618 Some(TK::Super) => ConstraintSuper,
4619 _ => missing_syntax("constraint operator", o, e)?,
4621 Ok(ast::WhereConstraintHint(l, o, p_hint(&c.right_type, e)?))
4623 _ => missing_syntax("where constraint", n, e),
4627 .syntax_node_to_list_skip_separator()
4633 missing_syntax("classish declaration constraints", parent, env)
4635 missing_syntax("function header constraints", parent, env)
4641 fn p_namespace_use_kind<'a>(kind: S<'a>, env: &mut Env<'a>) -> Result<ast::NsKind, Error> {
4643 match &kind.children {
4644 Missing => Ok(NSClassAndNamespace),
4645 _ => match token_kind(kind) {
4646 Some(TK::Namespace) => Ok(NSNamespace),
4647 Some(TK::Type) => Ok(NSClass),
4648 Some(TK::Function) => Ok(NSFun),
4649 Some(TK::Const) => Ok(NSConst),
4650 _ => missing_syntax("namespace use kind", kind, env),
4655 fn p_namespace_use_clause<'a>(
4656 prefix: Option<S<'a>>,
4657 kind: Result<ast::NsKind, Error>,
4660 ) -> Result<(ast::NsKind, ast::Sid, ast::Sid), Error> {
4662 static ref NAMESPACE_USE: regex::Regex = regex::Regex::new("[^\\\\]*$").unwrap();
4665 match &node.children {
4666 NamespaceUseClause(NamespaceUseClauseChildren {
4672 let ast::Id(p, n) = match (prefix, pos_name(name, env)?) {
4674 (Some(prefix), ast::Id(p, n)) => ast::Id(p, pos_name(prefix, env)?.1 + &n),
4676 let alias = if alias.is_missing() {
4677 let x = NAMESPACE_USE.find(&n).unwrap().as_str();
4678 ast::Id(p.clone(), x.to_string())
4680 pos_name(alias, env)?
4682 let kind = if clause_kind.is_missing() {
4685 p_namespace_use_kind(clause_kind, env)
4691 if !n.is_empty() && n.starts_with('\\') {
4694 String::from("\\") + &n
4700 _ => missing_syntax("namespace use clause", node, env),
4704 fn check_effect_memoized<'a>(
4705 contexts: Option<&ast::Contexts>,
4706 user_attributes: &[aast::UserAttribute<(), ()>],
4710 if has_polymorphic_context(env, contexts) {
4711 if let Some(u) = user_attributes
4713 .find(|u| naming_special_names_rust::user_attributes::is_memoized(&u.name.1))
4715 raise_parsing_error_pos(
4718 &syntax_error::effect_polymorphic_memoized(kind),
4722 let has_policied = has_any_policied_context(contexts);
4724 if let Some(u) = user_attributes
4726 .find(|u| naming_special_names_rust::user_attributes::is_memoized_regular(&u.name.1))
4728 raise_parsing_error_pos(
4731 &syntax_error::effect_policied_memoized(kind),
4735 if let Some(u) = user_attributes
4737 .find(|u| naming_special_names_rust::user_attributes::is_memoized_policy_sharded(&u.name.1))
4740 raise_parsing_error_pos(
4743 &syntax_error::policy_sharded_memoized_without_policied(kind),
4749 fn check_context_has_this<'a>(contexts: Option<&ast::Contexts>, env: &mut Env<'a>) {
4750 use ast::Hint_::{Haccess, Happly};
4751 if let Some(ast::Contexts(pos, ref context_hints)) = contexts {
4752 context_hints.iter().for_each(|c| match *c.1 {
4753 Haccess(ref root, _) => match &*root.1 {
4754 Happly(oxidized::ast::Id(_, id), _)
4755 if strip_ns(id.as_str()) == naming_special_names_rust::typehints::THIS =>
4757 raise_parsing_error_pos(
4760 "this:: context is not allowed on top level functions",
4770 fn check_effect_polymorphic_reification<'a>(
4771 contexts: Option<&ast::Contexts>,
4775 use ast::Hint_::{Haccess, Happly};
4776 if let Some(ast::Contexts(_, ref context_hints)) = contexts {
4777 context_hints.iter().for_each(|c| match *c.1 {
4778 Haccess(ref root, _) => match &*root.1 {
4779 Happly(oxidized::ast::Id(_, id), _) => {
4780 fail_if_invalid_reified_generic(node, env, strip_ns(id.as_str()))
4789 fn p_def<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Def>, Error> {
4790 let doc_comment_opt = extract_docblock(node, env);
4791 match &node.children {
4792 FunctionDeclaration(FunctionDeclarationChildren {
4797 let mut env = Env::clone_and_unset_toplevel_if_toplevel(env);
4798 let env = env.as_mut();
4799 env.clear_generics();
4800 let hdr = p_fun_hdr(declaration_header, env)?;
4801 let is_external = body.is_external();
4802 let (block, yield_) = if is_external {
4805 mp_yielding(&p_function_body, body, env)?
4807 let user_attributes = p_user_attributes(attribute_spec, env)?;
4808 check_effect_memoized(hdr.contexts.as_ref(), &user_attributes, "function", env);
4809 check_context_has_this(hdr.contexts.as_ref(), env);
4810 let ret = ast::TypeHint((), hdr.return_type);
4812 let fun = ast::Fun_ {
4813 span: p_fun_pos(node, env),
4814 readonly_this: hdr.readonly_this,
4817 readonly_ret: hdr.readonly_return,
4819 tparams: hdr.type_parameters,
4820 where_constraints: hdr.constrs,
4821 params: hdr.parameters,
4823 unsafe_ctxs: hdr.unsafe_contexts,
4824 body: ast::FuncBody { fb_ast: block },
4825 fun_kind: mk_fun_kind(hdr.suspension_kind, yield_),
4827 external: is_external,
4828 doc_comment: doc_comment_opt,
4831 Ok(vec![ast::Def::mk_fun(ast::FunDef {
4832 namespace: mk_empty_ns_env(env),
4833 file_attributes: vec![],
4834 mode: env.file_mode(),
4838 ClassishDeclaration(c) if contains_class_body(c) => {
4839 let mut env = Env::clone_and_unset_toplevel_if_toplevel(env);
4840 let env = env.as_mut();
4841 let mode = env.file_mode();
4842 let user_attributes = p_user_attributes(&c.attribute, env)?;
4843 let kinds = p_kinds(&c.modifiers, env)?;
4844 let final_ = kinds.has(modifier::FINAL);
4845 let is_xhp = matches!(
4846 token_kind(&c.name),
4847 Some(TK::XHPElementName) | Some(TK::XHPClassName)
4849 let has_xhp_keyword = matches!(token_kind(&c.xhp), Some(TK::XHP));
4850 let name = pos_name(&c.name, env)?;
4851 env.clear_generics();
4852 let tparams = p_tparam_l(true, &c.type_parameters, env)?;
4853 let class_kind = match token_kind(&c.keyword) {
4854 Some(TK::Class) if kinds.has(modifier::ABSTRACT) => {
4855 ast::ClassishKind::Cclass(ast::Abstraction::Abstract)
4857 Some(TK::Class) => ast::ClassishKind::Cclass(ast::Abstraction::Concrete),
4858 Some(TK::Interface) => ast::ClassishKind::Cinterface,
4859 Some(TK::Trait) => ast::ClassishKind::Ctrait,
4860 Some(TK::Enum) => ast::ClassishKind::Cenum,
4861 _ => missing_syntax("class kind", &c.keyword, env)?,
4863 let extends = could_map(p_hint, &c.extends_list, env)?;
4864 *env.parent_maybe_reified() = match extends.first().map(|h| h.1.as_ref()) {
4865 Some(ast::Hint_::Happly(_, hl)) => !hl.is_empty(),
4868 let implements = could_map(p_hint, &c.implements_list, env)?;
4869 let where_constraints = p_where_constraint(true, node, &c.where_clause, env)?;
4870 let namespace = mk_empty_ns_env(env);
4871 let span = p_pos(node, env);
4872 let mut class_ = ast::Class_ {
4884 use_as_alias: vec![],
4885 insteadof_alias: vec![],
4886 xhp_attr_uses: vec![],
4895 // TODO: what is this attbiute? check ast_to_aast
4897 xhp_children: vec![],
4901 file_attributes: vec![],
4903 doc_comment: doc_comment_opt,
4906 match &c.body.children {
4907 ClassishBody(c1) => {
4908 for elt in c1.elements.syntax_node_to_list_skip_separator() {
4909 p_class_elt(&mut class_, elt, env)?;
4912 _ => missing_syntax("classish body", &c.body, env)?,
4914 Ok(vec![ast::Def::mk_class(class_)])
4916 ConstDeclaration(c) => {
4917 let ty = &c.type_specifier;
4918 let decls = c.declarators.syntax_node_to_list_skip_separator();
4919 let mut defs = vec![];
4921 let def = match &decl.children {
4922 ConstantDeclarator(c) => {
4924 let init = &c.initializer;
4925 let gconst = ast::Gconst {
4927 mode: env.file_mode(),
4928 name: pos_name(name, env)?,
4929 type_: mp_optional(p_hint, ty, env)?,
4930 value: p_simple_initializer(init, env)?,
4931 namespace: mk_empty_ns_env(env),
4932 span: p_pos(node, env),
4935 ast::Def::mk_constant(gconst)
4937 _ => missing_syntax("constant declaration", decl, env)?,
4943 AliasDeclaration(c) => {
4944 let tparams = p_tparam_l(false, &c.generic_parameter, env)?;
4945 for tparam in tparams.iter() {
4946 if tparam.reified != ast::ReifyKind::Erased {
4947 raise_parsing_error(node, env, &syntax_error::invalid_reified)
4950 Ok(vec![ast::Def::mk_typedef(ast::Typedef {
4952 name: pos_name(&c.name, env)?,
4954 constraint: mp_optional(p_tconstraint, &c.constraint, env)?.map(|x| x.1),
4955 user_attributes: itertools::concat(
4957 .syntax_node_to_list_skip_separator()
4958 .map(|attr| p_user_attribute(attr, env))
4959 .collect::<Result<Vec<Vec<_>>, _>>()?,
4961 file_attributes: vec![],
4962 namespace: mk_empty_ns_env(env),
4963 mode: env.file_mode(),
4964 vis: match token_kind(&c.keyword) {
4965 Some(TK::Type) => ast::TypedefVisibility::Transparent,
4966 Some(TK::Newtype) => ast::TypedefVisibility::Opaque,
4967 _ => missing_syntax("kind", &c.keyword, env)?,
4969 kind: p_hint(&c.type_, env)?,
4970 span: p_pos(node, env),
4975 ContextAliasDeclaration(c) => {
4976 let (_super_constraint, as_constraint) = p_ctx_constraints(&c.as_constraint, env)?;
4978 let pos_name = pos_name(&c.name, env)?;
4979 if let Some(first_char) = pos_name.1.chars().next() {
4980 if first_char.is_lowercase() {
4981 raise_parsing_error(
4984 &syntax_error::user_ctx_should_be_caps(&pos_name.1),
4988 if as_constraint.is_none() {
4989 raise_parsing_error(
4992 &syntax_error::user_ctx_require_as(&pos_name.1),
4995 let kind = match p_context_list_to_intersection(&c.context, env)? {
4998 let pos = pos_name.0.clone();
5000 ast::Hint_::Happly(ast::Id(pos.clone(), String::from("defaults")), vec![]);
5001 ast::Hint::new(pos, hint_)
5004 Ok(vec![ast::Def::mk_typedef(ast::Typedef {
5008 constraint: as_constraint,
5009 user_attributes: itertools::concat(
5011 .syntax_node_to_list_skip_separator()
5012 .map(|attr| p_user_attribute(attr, env))
5013 .collect::<Result<Vec<Vec<_>>, _>>()?,
5015 namespace: mk_empty_ns_env(env),
5016 mode: env.file_mode(),
5017 file_attributes: vec![],
5018 vis: ast::TypedefVisibility::Opaque,
5020 span: p_pos(node, env),
5025 EnumDeclaration(c) => {
5026 let p_enumerator = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::ClassConst, Error> {
5028 Enumerator(c) => Ok(ast::ClassConst {
5029 user_attributes: vec![],
5031 id: pos_name(&c.name, e)?,
5032 kind: ast::ClassConstKind::CCConcrete(p_expr(&c.value, e)?),
5035 _ => missing_syntax("enumerator", n, e),
5039 let mut includes = vec![];
5041 let mut p_enum_use = |n: S<'a>, e: &mut Env<'a>| -> Result<(), Error> {
5044 let mut uses = could_map(p_hint, &c.names, e)?;
5045 Ok(includes.append(&mut uses))
5047 _ => missing_syntax("enum_use", node, e),
5051 for elt in c.use_clauses.syntax_node_to_list_skip_separator() {
5052 p_enum_use(elt, env)?;
5055 Ok(vec![ast::Def::mk_class(ast::Class_ {
5057 mode: env.file_mode(),
5058 user_attributes: p_user_attributes(&c.attribute_spec, env)?,
5059 file_attributes: vec![],
5061 kind: ast::ClassishKind::Cenum,
5063 has_xhp_keyword: false,
5064 name: pos_name(&c.name, env)?,
5068 where_constraints: vec![],
5069 consts: could_map(p_enumerator, &c.enumerators, env)?,
5070 namespace: mk_empty_ns_env(env),
5071 span: p_pos(node, env),
5072 enum_: Some(ast::Enum_ {
5073 base: p_hint(&c.base, env)?,
5074 constraint: mp_optional(p_tconstraint_ty, &c.type_, env)?,
5077 doc_comment: doc_comment_opt,
5079 use_as_alias: vec![],
5080 insteadof_alias: vec![],
5081 xhp_attr_uses: vec![],
5088 xhp_children: vec![],
5094 EnumClassDeclaration(c) => {
5095 let name = pos_name(&c.name, env)?;
5096 // Adding __EnumClass
5097 let mut user_attributes = p_user_attributes(&c.attribute_spec, env)?;
5098 let enum_class_attribute = ast::UserAttribute {
5099 name: ast::Id(name.0.clone(), special_attrs::ENUM_CLASS.to_string()),
5102 user_attributes.push(enum_class_attribute);
5103 // During lowering we store the base type as is. It will be updated during
5105 let base_type = p_hint(&c.base, env)?;
5107 let name_s = name.1.clone(); // TODO: can I avoid this clone ?
5109 let kinds = p_kinds(&c.modifiers, env)?;
5111 let class_kind = if kinds.has(modifier::ABSTRACT) {
5112 ast::ClassishKind::CenumClass(ast::Abstraction::Abstract)
5114 ast::ClassishKind::CenumClass(ast::Abstraction::Concrete)
5117 // Helper to build X -> HH\MemberOf<enum_name, X>
5118 let build_elt = |p: Pos, ty: ast::Hint| -> ast::Hint {
5119 let enum_name = ast::Id(p.clone(), name_s.clone());
5120 let enum_class = ast::Hint_::mk_happly(enum_name, vec![]);
5121 let enum_class = ast::Hint::new(p.clone(), enum_class);
5122 let elt_id = ast::Id(p.clone(), special_classes::MEMBER_OF.to_string());
5123 let full_type = ast::Hint_::mk_happly(elt_id, vec![enum_class, ty]);
5124 ast::Hint::new(p, full_type)
5127 let extends = could_map(p_hint, &c.extends_list, env)?;
5129 let mut enum_class = ast::Class_ {
5131 mode: env.file_mode(),
5133 file_attributes: vec![],
5134 final_: false, // TODO(T77095784): support final EDTs
5137 has_xhp_keyword: false,
5140 extends: extends.clone(),
5142 where_constraints: vec![],
5144 namespace: mk_empty_ns_env(env),
5145 span: p_pos(node, env),
5146 enum_: Some(ast::Enum_ {
5151 doc_comment: doc_comment_opt,
5153 use_as_alias: vec![],
5154 insteadof_alias: vec![],
5155 xhp_attr_uses: vec![],
5162 xhp_children: vec![],
5167 for n in c.elements.syntax_node_to_list_skip_separator() {
5169 // TODO(T77095784): check pos and span usage
5170 EnumClassEnumerator(c) => {
5172 // - type name = expression;
5174 // - const MemberOf<enum_name, type> name = expression
5175 let name = pos_name(&c.name, env)?;
5177 let kinds = p_kinds(&c.modifiers, env)?;
5178 let has_abstract = kinds.has(modifier::ABSTRACT);
5179 let elt_type = p_hint(&c.type_, env)?;
5180 let full_type = build_elt(pos.clone(), elt_type);
5181 let kind = if has_abstract {
5182 ast::ClassConstKind::CCAbstract(None)
5184 ast::ClassConstKind::CCConcrete(p_simple_initializer(
5189 let class_const = ast::ClassConst {
5190 user_attributes: vec![],
5191 type_: Some(full_type),
5196 enum_class.consts.push(class_const)
5199 let pos = p_pos(n, env);
5200 raise_parsing_error_pos(
5203 &syntax_error::invalid_enum_class_enumerator,
5208 Ok(vec![ast::Def::mk_class(enum_class)])
5210 InclusionDirective(c) if env.file_mode() != file_info::Mode::Mhhi || env.codegen() => {
5211 let expr = p_expr(&c.expression, env)?;
5212 Ok(vec![ast::Def::mk_stmt(ast::Stmt::new(
5214 ast::Stmt_::mk_expr(expr),
5217 NamespaceDeclaration(c) => {
5218 let name = if let NamespaceDeclarationHeader(h) = &c.header.children {
5221 return missing_syntax("namespace_declaration_header", node, env);
5223 let defs = match &c.body.children {
5224 NamespaceBody(c) => {
5225 let mut env1 = Env::clone_and_unset_toplevel_if_toplevel(env);
5226 let env1 = env1.as_mut();
5229 .syntax_node_to_list_skip_separator()
5230 .map(|n| p_def(n, env1))
5231 .collect::<Result<Vec<Vec<_>>, _>>()?,
5236 Ok(vec![ast::Def::mk_namespace(pos_name(name, env)?, defs)])
5238 NamespaceGroupUseDeclaration(c) => {
5239 let uses: Result<Vec<_>, _> = c
5241 .syntax_node_to_list_skip_separator()
5243 p_namespace_use_clause(
5245 p_namespace_use_kind(&c.kind, env),
5251 Ok(vec![ast::Def::mk_namespace_use(uses?)])
5253 NamespaceUseDeclaration(c) => {
5254 let uses: Result<Vec<_>, _> = c
5256 .syntax_node_to_list_skip_separator()
5257 .map(|n| p_namespace_use_clause(None, p_namespace_use_kind(&c.kind, env), n, env))
5259 Ok(vec![ast::Def::mk_namespace_use(uses?)])
5261 FileAttributeSpecification(_) => {
5262 Ok(vec![ast::Def::mk_file_attributes(ast::FileAttribute {
5263 user_attributes: p_user_attribute(node, env)?,
5264 namespace: mk_empty_ns_env(env),
5267 ModuleDeclaration(md) => Ok(vec![ast::Def::mk_module(ast::ModuleDef {
5268 name: pos_name(&md.name, env)?,
5269 user_attributes: p_user_attributes(&md.attribute_spec, env)?,
5270 span: p_pos(node, env),
5271 mode: env.file_mode(),
5273 _ if env.file_mode() == file_info::Mode::Mhhi => Ok(vec![]),
5274 _ => Ok(vec![ast::Def::mk_stmt(p_stmt(node, env)?)]),
5278 fn post_process<'a>(env: &mut Env<'a>, program: Vec<ast::Def>, acc: &mut Vec<ast::Def>) {
5279 use aast::{Def, Def::*, Stmt_::*};
5280 let mut saw_ns: Option<(ast::Sid, Vec<ast::Def>)> = None;
5281 for def in program.into_iter() {
5282 if let Namespace(_) = &def {
5283 if let Some((n, ns_acc)) = saw_ns {
5284 acc.push(Def::mk_namespace(n, ns_acc));
5289 if let Namespace(ns) = def {
5290 let (n, defs) = *ns;
5291 if defs.is_empty() {
5292 saw_ns = Some((n, vec![]));
5294 let mut acc_ = vec![];
5295 post_process(env, defs, &mut acc_);
5296 acc.push(Def::mk_namespace(n, acc_));
5302 if let Stmt(s) = &def {
5306 let raise_error = match &s.1 {
5309 if expr.as_ref().is_import()
5310 && !env.parser_options.po_disallow_toplevel_requires =>
5315 use file_info::Mode::*;
5316 let mode = env.file_mode();
5317 env.keep_errors && env.is_typechecker() && (mode == Mstrict)
5321 raise_parsing_error_pos(&s.0, env, &syntax_error::toplevel_statements);
5325 if let Some((_, ns_acc)) = &mut saw_ns {
5331 if let Some((n, defs)) = saw_ns {
5332 acc.push(Def::mk_namespace(n, defs));
5336 fn p_program<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Program, Error> {
5337 let nodes = node.syntax_node_to_list_skip_separator();
5338 let mut acc = vec![];
5341 EndOfFile(_) => break,
5342 _ => match p_def(n, env) {
5343 Err(Error::MissingSyntax { .. }) if env.fail_open => {}
5344 Err(e) => return Err(e),
5345 Ok(mut def) => acc.append(&mut def),
5349 let mut program = vec![];
5350 post_process(env, acc, &mut program);
5351 Ok(ast::Program(program))
5354 fn p_script<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<ast::Program, Error> {
5355 match &node.children {
5356 Script(c) => p_program(&c.declarations, env),
5357 _ => missing_syntax("script", node, env),
5361 pub fn lower<'a>(env: &mut Env<'a>, script: S<'a>) -> Result<ast::Program, String> {
5362 p_script(script, env).map_err(|e| match e {
5363 Error::MissingSyntax {
5369 "missing case in {:?}.\n - pos: {:?}\n - unexpected: '{:?}'\n - kind: {:?}\n",
5370 expecting, pos, node_name, kind,
5372 Error::Failwith(msg) => msg,