fn expression_errors()

in hphp/hack/src/parser/rust_parser_errors.rs [2977:3443]


    fn expression_errors(&mut self, node: S<'a>) {
        if let Some(sl) = self.env.stack_limit.as_ref() {
            sl.panic_if_exceeded();
        }
        let check_is_as_expression = |self_: &mut Self, hint: S<'a>| {
            let n = match &node.children {
                IsExpression(_) => "is",
                _ => "as",
            };
            match &hint.children {
                ClosureTypeSpecifier(_) if self_.env.is_hhvm_compat() => {
                    self_.errors.push(make_error_from_node(
                        hint,
                        errors::invalid_is_as_expression_hint(n, "__Callable"),
                    ));
                }
                SoftTypeSpecifier(_) => {
                    self_.errors.push(make_error_from_node(
                        hint,
                        errors::invalid_is_as_expression_hint(n, "__Soft"),
                    ));
                }
                AttributizedSpecifier(x)
                    if self_.attribute_specification_contains(&x.attribute_spec, "__Soft") =>
                {
                    self_.errors.push(make_error_from_node(
                        hint,
                        errors::invalid_is_as_expression_hint(n, "__Soft"),
                    ));
                }
                _ => {}
            }
        };
        match &node.children {
            // We parse the right hand side of `new` as a generic expression, but PHP
            // (and therefore Hack) only allow a certain subset of expressions, so we
            // should verify here that the expression we parsed is in that subset.
            // Refer: https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#instanceof-operator*)
            ConstructorCall(ctr_call) => {
                for p in syntax_to_list_no_separators(&ctr_call.argument_list) {
                    self.function_call_argument_errors(true, p);
                }
                self.class_type_designator_errors(&ctr_call.type_);
                if self.env.is_typechecker() {
                    // attr or list item -> syntax list -> attribute
                    match self.parents.iter().rev().nth(2) {
                        Some(a)
                            if a.is_attribute_specification()
                                || a.is_old_attribute_specification()
                                || a.is_file_attribute_specification() => {}
                        _ => {
                            if ctr_call.left_paren.is_missing() || ctr_call.right_paren.is_missing()
                            {
                                let node = &ctr_call.type_;
                                let constructor_name = self.text(&ctr_call.type_);
                                self.errors.push(make_error_from_node(
                                    node,
                                    errors::error2038(constructor_name),
                                ));
                            }
                        }
                    }
                };
            }
            LiteralExpression(x) => {
                if let Token(token) = &x.expression.children {
                    if token.kind() == TokenKind::DecimalLiteral
                        || token.kind() == TokenKind::DecimalLiteral
                    {
                        let text = self.text(&x.expression);
                        if text.parse::<i64>().is_err() {
                            let error_text = if token.kind() == TokenKind::DecimalLiteral {
                                errors::error2071(text)
                            } else {
                                errors::error2072(text)
                            };
                            self.errors.push(make_error_from_node(node, error_text))
                        }
                    }
                }
            }

            SubscriptExpression(x)
                if self.env.is_typechecker() && x.left_bracket.is_left_brace() =>
            {
                self.errors
                    .push(make_error_from_node(node, errors::error2020))
            }

            FunctionCallExpression(x) => {
                let arg_list = &x.argument_list;
                if let Some(h) = misplaced_variadic_arg(arg_list) {
                    self.errors.push(make_error_from_node(h, errors::error2033))
                }

                for p in syntax_to_list_no_separators(arg_list) {
                    self.function_call_argument_errors(false, p)
                }

                let recv = &x.receiver;

                self.function_call_on_xhp_name_errors(recv);

                let fun_and_clsmeth_disabled = self
                    .env
                    .parser_options
                    .po_disallow_fun_and_cls_meth_pseudo_funcs;

                if strip_ns(self.text(recv)) == strip_ns(sn::readonly::AS_MUT) {
                    self.mark_uses_readonly()
                }

                if self.text(recv) == strip_hh_ns(sn::autoimported_functions::FUN_)
                    && fun_and_clsmeth_disabled
                {
                    let mut arg_node_list = syntax_to_list_no_separators(arg_list);
                    match arg_node_list.next() {
                        Some(name) if arg_node_list.count() == 0 => self.errors.push(
                            make_error_from_node(recv, errors::fun_disabled(self.text(name))),
                        ),
                        _ => self.errors.push(make_error_from_node(
                            recv,
                            errors::fun_requires_const_string,
                        )),
                    }
                }

                if self.text(recv) == strip_hh_ns(sn::autoimported_functions::CLASS_METH)
                    && fun_and_clsmeth_disabled
                {
                    self.errors
                        .push(make_error_from_node(recv, errors::class_meth_disabled))
                }

                if self.text(recv) == strip_hh_ns(sn::autoimported_functions::INST_METH)
                    && self.env.parser_options.po_disallow_inst_meth
                {
                    self.errors
                        .push(make_error_from_node(recv, errors::inst_meth_disabled))
                }
            }

            ETSpliceExpression(_) => {
                if !self.env.context.active_expression_tree {
                    self.errors
                        .push(make_error_from_node(node, errors::splice_outside_et))
                }
            }

            ListExpression(x) if x.members.is_missing() && self.env.is_hhvm_compat() => {
                if let Some(Syntax {
                    children: ForeachStatement(x),
                    ..
                }) = self.parents.last()
                {
                    if std::ptr::eq(node, &x.value) {
                        self.errors.push(make_error_from_node_with_type(
                            node,
                            errors::error2077,
                            ErrorType::RuntimeError,
                        ))
                    }
                }
            }

            ListExpression(_) => {
                if self
                    .parents
                    .last()
                    .map_or(false, |e| e.is_return_statement())
                {
                    self.errors
                        .push(make_error_from_node(node, errors::list_must_be_lvar))
                }
            }
            ShapeExpression(x) => {
                for f in syntax_to_list_no_separators(&x.fields).rev() {
                    self.invalid_shape_field_check(f)
                }
            }
            DecoratedExpression(x) => {
                let decorator = &x.decorator;
                if token_kind(decorator) == Some(TokenKind::Await) {
                    self.await_as_an_expression_errors(node)
                }
            }
            YieldExpression(_) => {
                if self.is_in_unyieldable_magic_method() {
                    self.errors
                        .push(make_error_from_node(node, errors::yield_in_magic_methods))
                }
                if self.env.context.active_callable.is_none() {
                    self.errors
                        .push(make_error_from_node(node, errors::yield_outside_function))
                }

                if self.has_inout_params() {
                    let e = if self.is_inside_async_method() {
                        errors::inout_param_in_async_generator
                    } else {
                        errors::inout_param_in_generator
                    };
                    self.errors.push(make_error_from_node_with_type(
                        node,
                        e,
                        ErrorType::RuntimeError,
                    ))
                }
            }
            ScopeResolutionExpression(x) => {
                let qualifier = &x.qualifier;
                let name = &x.name;

                let (is_dynamic_name, is_self_or_parent, is_valid) =
                    // PHP langspec allows string literals, variables
                    // qualified names, static, self and parent as valid qualifiers
                    // We do not allow string literals in hack
                    match (&qualifier.children, token_kind(qualifier)) {
                            (LiteralExpression(_), _) => (false, false, false),
                            (QualifiedName(_), _) => (false, false, true),
                            (_, Some(TokenKind::Name))
                            | (_, Some(TokenKind::XHPClassName))
                            | (_, Some(TokenKind::Static)) => (false, false, true),
                            (_, Some(TokenKind::SelfToken)) | (_, Some(TokenKind::Parent)) => {
                                (false, true, true)
                            }
                            // ${}::class
                            (PrefixUnaryExpression(x), _)
                                if token_kind(&x.operator) == Some(TokenKind::Dollar) =>
                            {
                                (true, false, true)
                            }
                            (PipeVariableExpression(_), _)
                            | (VariableExpression(_), _)
                            | (SimpleTypeSpecifier(_), _)
                            | (GenericTypeSpecifier(_), _) => (true, false, true),
                            _ => (true, false, false),
                        };
                if !is_valid {
                    self.errors.push(make_error_from_node(
                        node,
                        errors::invalid_scope_resolution_qualifier,
                    ))
                }
                let is_name_class = self.text(name).eq_ignore_ascii_case("class");
                if (is_dynamic_name || !is_valid) && is_name_class {
                    self.errors.push(make_error_from_node(
                        node,
                        errors::coloncolonclass_on_dynamic,
                    ))
                }
                let text_name = self.text(qualifier);
                let is_name_namespace = text_name.eq_ignore_ascii_case("namespace");
                if is_name_namespace {
                    self.errors.push(make_error_from_node(
                        node,
                        errors::namespace_not_a_classname,
                    ))
                }
                if is_self_or_parent && is_name_class && !self.is_in_active_class_scope() {
                    self.errors.push(make_error_from_node_with_type(
                        node,
                        errors::self_or_parent_colon_colon_class_outside_of_class(text_name),
                        ErrorType::RuntimeError,
                    ))
                }
            }

            PrefixUnaryExpression(x) if token_kind(&x.operator) == Some(TokenKind::Dollar) => {
                if check_prefix_unary_dollar(node) {
                    self.errors
                        .push(make_error_from_node(node, errors::dollar_unary))
                }
            }

            // TODO(T21285960): Remove this bug-port, stemming from T22184312
            LambdaExpression(x)
                if self.env.is_hhvm_compat()
                    && !x.async_.is_missing()
                    && x.async_.trailing_width() == 0
                    && x.signature.leading_width() == 0 =>
            {
                self.errors
                    .push(make_error_from_node(node, errors::error1057("==>")))
            }
            // End of bug-port
            IsExpression(x) => check_is_as_expression(self, &x.right_operand),
            AsExpression(x) => check_is_as_expression(self, &x.right_operand),

            ConditionalExpression(x) => {
                if x.consequence.is_missing() && self.env.is_typechecker() {
                    self.errors
                        .push(make_error_from_node(node, errors::elvis_operator_space))
                }
                if x.test.is_conditional_expression() && self.env.is_typechecker() {
                    self.errors
                        .push(make_error_from_node(node, errors::nested_ternary))
                }
                match &x.alternative.children {
                    LambdaExpression(x)
                        if x.body.is_conditional_expression() && self.env.is_typechecker() =>
                    {
                        self.errors
                            .push(make_error_from_node(node, errors::nested_ternary))
                    }
                    _ => {}
                }
            }
            LambdaExpression(x) => {
                self.no_memoize_attribute_on_lambda(&x.attribute_spec);
                self.no_async_before_lambda_body(&x.body);
            }
            AnonymousFunction(x) => self.no_memoize_attribute_on_lambda(&x.attribute_spec),
            AwaitableCreationExpression(x) => {
                self.no_memoize_attribute_on_lambda(&x.attribute_spec)
            }

            CollectionLiteralExpression(x) => {
                enum Status {
                    ValidClass(String),
                    InvalidClass,
                    InvalidBraceKind,
                }
                use Status::*;

                let n = &x.name;
                let initializers = &x.initializers;

                let is_standard_collection = |lc_name: &str| {
                    lc_name.eq_ignore_ascii_case("pair")
                        || lc_name.eq_ignore_ascii_case("vector")
                        || lc_name.eq_ignore_ascii_case("map")
                        || lc_name.eq_ignore_ascii_case("set")
                        || lc_name.eq_ignore_ascii_case("immvector")
                        || lc_name.eq_ignore_ascii_case("immmap")
                        || lc_name.eq_ignore_ascii_case("immset")
                };
                let use_key_value_initializers = |lc_name: &str| {
                    lc_name.eq_ignore_ascii_case("map") || lc_name.eq_ignore_ascii_case("immmap")
                };
                let is_qualified_std_collection = |l, r| {
                    token_kind(l) == Some(TokenKind::Name)
                        && token_kind(r) == Some(TokenKind::Name)
                        && self.text(l).eq_ignore_ascii_case("hh")
                        && is_standard_collection(self.text(r))
                };

                let check_type_specifier = |n, t: &PositionedToken<'a>| {
                    if t.kind() == TokenKind::Name {
                        match self.text(n).to_ascii_lowercase().as_ref() {
                            "dict" | "vec" | "keyset" => InvalidBraceKind,
                            n => {
                                if is_standard_collection(n) {
                                    ValidClass(n.to_string())
                                } else {
                                    InvalidClass
                                }
                            }
                        }
                    } else {
                        InvalidClass
                    }
                };

                let check_qualified_name = |parts| {
                    let mut parts = syntax_to_list(false, parts);
                    let p1 = parts.next();
                    let p2 = parts.next();
                    let p3 = parts.next();
                    let p4 = parts.next();
                    match (p1, p2, p3, p4) {
                        (Some(l), Some(r), None, None)
                            if self.namespace_name == GLOBAL_NAMESPACE_NAME
                                && is_qualified_std_collection(l, r) =>
                        {
                            // HH\Vector in global namespace
                            ValidClass(self.text(r).to_ascii_lowercase())
                        }
                        (Some(missing), Some(l), Some(r), None)
                            if missing.is_missing() && is_qualified_std_collection(l, r) =>
                        {
                            // \HH\Vector
                            ValidClass(self.text(r).to_ascii_lowercase())
                        }
                        _ => InvalidClass,
                    }
                };
                let status = match &n.children {
                    // non-qualified name
                    SimpleTypeSpecifier(x) => match &x.specifier.children {
                        Token(t) => check_type_specifier(&x.specifier, t),
                        QualifiedName(x) => check_qualified_name(&x.parts),
                        _ => InvalidClass,
                    },
                    GenericTypeSpecifier(x) => match &x.class_type.children {
                        Token(t) => check_type_specifier(&x.class_type, t),
                        QualifiedName(x) => check_qualified_name(&x.parts),
                        _ => InvalidClass,
                    },
                    _ => InvalidClass,
                };

                let is_key_value = |s: S<'a>| {
                    if let ElementInitializer(_) = s.children {
                        true
                    } else {
                        false
                    }
                };
                let initializer_list = || syntax_to_list_no_separators(initializers);
                let num_initializers = initializer_list().count();
                match &status {
                    ValidClass(name)
                        if use_key_value_initializers(name)
                            && initializer_list().any(|i| !is_key_value(i)) =>
                    {
                        self.errors.push(make_error_from_node(
                            node,
                            errors::invalid_value_initializer(self.text(n)),
                        ));
                    }

                    ValidClass(name)
                        if !use_key_value_initializers(name)
                            && initializer_list().any(|i| is_key_value(i)) =>
                    {
                        self.errors.push(make_error_from_node(
                            node,
                            errors::invalid_key_value_initializer(self.text(n)),
                        ));
                    }

                    ValidClass(pair) if pair == "pair" && num_initializers != 2 => {
                        let msg = if num_initializers == 0 {
                            errors::pair_initializer_needed
                        } else {
                            errors::pair_initializer_arity
                        };
                        self.errors.push(make_error_from_node_with_type(
                            node,
                            msg,
                            ErrorType::RuntimeError,
                        ));
                    }

                    ValidClass(_) => {}
                    InvalidBraceKind => self.errors.push(make_error_from_node(
                        node,
                        errors::invalid_brace_kind_in_collection_initializer,
                    )),
                    InvalidClass => self.errors.push(make_error_from_node(
                        node,
                        errors::invalid_class_in_collection_initializer,
                    )),
                }
            }
            PrefixUnaryExpression(x) if token_kind(&x.operator) == Some(TokenKind::Await) => {
                self.await_as_an_expression_errors(node)
            }
            PrefixUnaryExpression(x) if token_kind(&x.operator) == Some(TokenKind::Readonly) => {
                self.mark_uses_readonly()
            }

            // Other kinds of expressions currently produce no expr errors.
            _ => {}
        }
    }