fn p_def, env: &mut Env) -> Result>()

in hphp/hack/src/parser/lowerer/lowerer.rs [5119:5611]


fn p_def<'a>(node: S<'a>, env: &mut Env<'a>) -> Result<Vec<ast::Def>> {
    let doc_comment_opt = extract_docblock(node, env);
    match &node.children {
        FunctionDeclaration(FunctionDeclarationChildren {
            attribute_spec,
            declaration_header,
            body,
        }) => {
            let mut env = Env::clone_and_unset_toplevel_if_toplevel(env);
            let env = env.as_mut();
            env.clear_generics();
            let hdr = p_fun_hdr(declaration_header, env)?;
            let is_external = body.is_external();
            let (block, yield_) = if is_external {
                (vec![], false)
            } else {
                map_yielding(body, env, p_function_body)?
            };
            let user_attributes = p_user_attributes(attribute_spec, env)?;
            check_effect_memoized(hdr.contexts.as_ref(), &user_attributes, "function", env);
            check_context_has_this(hdr.contexts.as_ref(), env);
            let ret = ast::TypeHint((), hdr.return_type);

            let fun = ast::Fun_ {
                span: p_fun_pos(node, env),
                readonly_this: hdr.readonly_this,
                annotation: (),
                ret,
                readonly_ret: hdr.readonly_return,
                name: hdr.name,
                tparams: hdr.type_parameters,
                where_constraints: hdr.constrs,
                params: hdr.parameters,
                ctxs: hdr.contexts,
                unsafe_ctxs: hdr.unsafe_contexts,
                body: ast::FuncBody { fb_ast: block },
                fun_kind: mk_fun_kind(hdr.suspension_kind, yield_),
                user_attributes,
                external: is_external,
                doc_comment: doc_comment_opt,
            };

            Ok(vec![ast::Def::mk_fun(ast::FunDef {
                namespace: mk_empty_ns_env(env),
                file_attributes: vec![],
                mode: env.file_mode(),
                fun,
            })])
        }
        ClassishDeclaration(c) if contains_class_body(c) => {
            let mut env = Env::clone_and_unset_toplevel_if_toplevel(env);
            let env = env.as_mut();
            let mode = env.file_mode();
            let user_attributes = p_user_attributes(&c.attribute, env)?;
            let kinds = p_kinds(&c.modifiers, env)?;
            let final_ = kinds.has(modifier::FINAL);
            let is_xhp = matches!(
                token_kind(&c.name),
                Some(TK::XHPElementName) | Some(TK::XHPClassName)
            );
            let has_xhp_keyword = matches!(token_kind(&c.xhp), Some(TK::XHP));
            let name = pos_name(&c.name, env)?;
            env.clear_generics();
            let tparams = p_tparam_l(true, &c.type_parameters, env)?;
            let class_kind = match token_kind(&c.keyword) {
                Some(TK::Class) if kinds.has(modifier::ABSTRACT) => {
                    ast::ClassishKind::Cclass(ast::Abstraction::Abstract)
                }
                Some(TK::Class) => ast::ClassishKind::Cclass(ast::Abstraction::Concrete),
                Some(TK::Interface) => ast::ClassishKind::Cinterface,
                Some(TK::Trait) => ast::ClassishKind::Ctrait,
                Some(TK::Enum) => ast::ClassishKind::Cenum,
                _ => missing_syntax("class kind", &c.keyword, env)?,
            };
            let extends = could_map(&c.extends_list, env, p_hint)?;
            *env.parent_maybe_reified() = match extends.first().map(|h| h.1.as_ref()) {
                Some(ast::Hint_::Happly(_, hl)) => !hl.is_empty(),
                _ => false,
            };
            let implements = could_map(&c.implements_list, env, p_hint)?;
            let where_constraints = p_where_constraint(true, node, &c.where_clause, env)?;
            let namespace = mk_empty_ns_env(env);
            let span = p_pos(node, env);
            let mut class_ = ast::Class_ {
                span,
                annotation: (),
                mode,
                final_,
                is_xhp,
                has_xhp_keyword,
                kind: class_kind,
                name,
                tparams,
                extends,
                uses: vec![],
                use_as_alias: vec![],
                insteadof_alias: vec![],
                xhp_attr_uses: vec![],
                xhp_category: None,
                reqs: vec![],
                implements,
                where_constraints,
                consts: vec![],
                typeconsts: vec![],
                vars: vec![],
                methods: vec![],
                // TODO: what is this attbiute? check ast_to_aast
                attributes: vec![],
                xhp_children: vec![],
                xhp_attrs: vec![],
                namespace,
                user_attributes,
                file_attributes: vec![],
                enum_: None,
                doc_comment: doc_comment_opt,
                emit_id: None,
            };
            match &c.body.children {
                ClassishBody(c1) => {
                    for elt in c1.elements.syntax_node_to_list_skip_separator() {
                        p_class_elt(&mut class_, elt, env)?;
                    }
                }
                _ => missing_syntax("classish body", &c.body, env)?,
            }
            Ok(vec![ast::Def::mk_class(class_)])
        }
        ConstDeclaration(c) => {
            let ty = &c.type_specifier;
            let decls = c.declarators.syntax_node_to_list_skip_separator();
            let mut defs = vec![];
            for decl in decls {
                let def = match &decl.children {
                    ConstantDeclarator(c) => {
                        let name = &c.name;
                        let init = &c.initializer;
                        let gconst = ast::Gconst {
                            annotation: (),
                            mode: env.file_mode(),
                            name: pos_name(name, env)?,
                            type_: map_optional(ty, env, p_hint)?,
                            value: p_simple_initializer(init, env)?,
                            namespace: mk_empty_ns_env(env),
                            span: p_pos(node, env),
                            emit_id: None,
                        };
                        ast::Def::mk_constant(gconst)
                    }
                    _ => missing_syntax("constant declaration", decl, env)?,
                };
                defs.push(def);
            }
            Ok(defs)
        }
        AliasDeclaration(c) => {
            let tparams = p_tparam_l(false, &c.generic_parameter, env)?;
            for tparam in tparams.iter() {
                if tparam.reified != ast::ReifyKind::Erased {
                    raise_parsing_error(node, env, &syntax_error::invalid_reified)
                }
            }
            Ok(vec![ast::Def::mk_typedef(ast::Typedef {
                annotation: (),
                name: pos_name(&c.name, env)?,
                tparams,
                constraint: map_optional(&c.constraint, env, p_tconstraint)?.map(|x| x.1),
                user_attributes: itertools::concat(
                    c.attribute_spec
                        .syntax_node_to_list_skip_separator()
                        .map(|attr| p_user_attribute(attr, env))
                        .collect::<Result<Vec<Vec<_>>, _>>()?,
                ),
                file_attributes: vec![],
                namespace: mk_empty_ns_env(env),
                mode: env.file_mode(),
                vis: match token_kind(&c.keyword) {
                    Some(TK::Type) => ast::TypedefVisibility::Transparent,
                    Some(TK::Newtype) => ast::TypedefVisibility::Opaque,
                    _ => missing_syntax("kind", &c.keyword, env)?,
                },
                kind: p_hint(&c.type_, env)?,
                span: p_pos(node, env),
                emit_id: None,
                is_ctx: false,
            })])
        }
        ContextAliasDeclaration(c) => {
            let (_super_constraint, as_constraint) = p_ctx_constraints(&c.as_constraint, env)?;

            let pos_name = pos_name(&c.name, env)?;
            if let Some(first_char) = pos_name.1.chars().next() {
                if first_char.is_lowercase() {
                    raise_parsing_error(
                        &c.name,
                        env,
                        &syntax_error::user_ctx_should_be_caps(&pos_name.1),
                    )
                }
            }
            if as_constraint.is_none() {
                raise_parsing_error(
                    &c.name,
                    env,
                    &syntax_error::user_ctx_require_as(&pos_name.1),
                )
            }
            let kind = match p_context_list_to_intersection(
                &c.context,
                env,
                "Context aliases cannot alias polymorphic contexts",
            )? {
                Some(h) => h,
                None => {
                    let pos = pos_name.0.clone();
                    let hint_ =
                        ast::Hint_::Happly(ast::Id(pos.clone(), String::from("defaults")), vec![]);
                    ast::Hint::new(pos, hint_)
                }
            };
            Ok(vec![ast::Def::mk_typedef(ast::Typedef {
                annotation: (),
                name: pos_name,
                tparams: vec![],
                constraint: as_constraint,
                user_attributes: itertools::concat(
                    c.attribute_spec
                        .syntax_node_to_list_skip_separator()
                        .map(|attr| p_user_attribute(attr, env))
                        .collect::<Result<Vec<Vec<_>>, _>>()?,
                ),
                namespace: mk_empty_ns_env(env),
                mode: env.file_mode(),
                file_attributes: vec![],
                vis: ast::TypedefVisibility::Opaque,
                kind,
                span: p_pos(node, env),
                emit_id: None,
                is_ctx: true,
            })])
        }
        EnumDeclaration(c) => {
            let p_enumerator = |n: S<'a>, e: &mut Env<'a>| -> Result<ast::ClassConst> {
                match &n.children {
                    Enumerator(c) => Ok(ast::ClassConst {
                        user_attributes: vec![],
                        type_: None,
                        id: pos_name(&c.name, e)?,
                        kind: ast::ClassConstKind::CCConcrete(p_expr(&c.value, e)?),
                        doc_comment: None,
                    }),
                    _ => missing_syntax("enumerator", n, e),
                }
            };

            let mut includes = vec![];

            let mut p_enum_use = |n: S<'a>, e: &mut Env<'a>| -> Result<()> {
                match &n.children {
                    EnumUse(c) => {
                        let mut uses = could_map(&c.names, e, p_hint)?;
                        Ok(includes.append(&mut uses))
                    }
                    _ => missing_syntax("enum_use", node, e),
                }
            };

            for elt in c.use_clauses.syntax_node_to_list_skip_separator() {
                p_enum_use(elt, env)?;
            }

            Ok(vec![ast::Def::mk_class(ast::Class_ {
                annotation: (),
                mode: env.file_mode(),
                user_attributes: p_user_attributes(&c.attribute_spec, env)?,
                file_attributes: vec![],
                final_: false,
                kind: ast::ClassishKind::Cenum,
                is_xhp: false,
                has_xhp_keyword: false,
                name: pos_name(&c.name, env)?,
                tparams: vec![],
                extends: vec![],
                implements: vec![],
                where_constraints: vec![],
                consts: could_map(&c.enumerators, env, p_enumerator)?,
                namespace: mk_empty_ns_env(env),
                span: p_pos(node, env),
                enum_: Some(ast::Enum_ {
                    base: p_hint(&c.base, env)?,
                    constraint: map_optional(&c.type_, env, p_tconstraint_ty)?,
                    includes,
                }),
                doc_comment: doc_comment_opt,
                uses: vec![],
                use_as_alias: vec![],
                insteadof_alias: vec![],
                xhp_attr_uses: vec![],
                xhp_category: None,
                reqs: vec![],
                vars: vec![],
                typeconsts: vec![],
                methods: vec![],
                attributes: vec![],
                xhp_children: vec![],
                xhp_attrs: vec![],
                emit_id: None,
            })])
        }

        EnumClassDeclaration(c) => {
            let name = pos_name(&c.name, env)?;
            // Adding __EnumClass
            let mut user_attributes = p_user_attributes(&c.attribute_spec, env)?;
            let enum_class_attribute = ast::UserAttribute {
                name: ast::Id(name.0.clone(), special_attrs::ENUM_CLASS.to_string()),
                params: vec![],
            };
            user_attributes.push(enum_class_attribute);
            // During lowering we store the base type as is. It will be updated during
            // the naming phase
            let base_type = p_hint(&c.base, env)?;

            let name_s = name.1.clone(); // TODO: can I avoid this clone ?

            let kinds = p_kinds(&c.modifiers, env)?;

            let class_kind = if kinds.has(modifier::ABSTRACT) {
                ast::ClassishKind::CenumClass(ast::Abstraction::Abstract)
            } else {
                ast::ClassishKind::CenumClass(ast::Abstraction::Concrete)
            };

            // Helper to build X -> HH\MemberOf<enum_name, X>
            let build_elt = |p: Pos, ty: ast::Hint| -> ast::Hint {
                let enum_name = ast::Id(p.clone(), name_s.clone());
                let enum_class = ast::Hint_::mk_happly(enum_name, vec![]);
                let enum_class = ast::Hint::new(p.clone(), enum_class);
                let elt_id = ast::Id(p.clone(), special_classes::MEMBER_OF.to_string());
                let full_type = ast::Hint_::mk_happly(elt_id, vec![enum_class, ty]);
                ast::Hint::new(p, full_type)
            };

            let extends = could_map(&c.extends_list, env, p_hint)?;

            let mut enum_class = ast::Class_ {
                annotation: (),
                mode: env.file_mode(),
                user_attributes,
                file_attributes: vec![],
                final_: false, // TODO(T77095784): support final EDTs
                kind: class_kind,
                is_xhp: false,
                has_xhp_keyword: false,
                name,
                tparams: vec![],
                extends: extends.clone(),
                implements: vec![],
                where_constraints: vec![],
                consts: vec![],
                namespace: mk_empty_ns_env(env),
                span: p_pos(node, env),
                enum_: Some(ast::Enum_ {
                    base: base_type,
                    constraint: None,
                    includes: extends,
                }),
                doc_comment: doc_comment_opt,
                uses: vec![],
                use_as_alias: vec![],
                insteadof_alias: vec![],
                xhp_attr_uses: vec![],
                xhp_category: None,
                reqs: vec![],
                vars: vec![],
                typeconsts: vec![],
                methods: vec![],
                attributes: vec![],
                xhp_children: vec![],
                xhp_attrs: vec![],
                emit_id: None,
            };

            for n in c.elements.syntax_node_to_list_skip_separator() {
                match &n.children {
                    // TODO(T77095784): check pos and span usage
                    EnumClassEnumerator(c) => {
                        // we turn:
                        // - type name = expression;
                        // into
                        // - const MemberOf<enum_name, type> name = expression
                        let name = pos_name(&c.name, env)?;
                        let pos = &name.0;
                        let kinds = p_kinds(&c.modifiers, env)?;
                        let has_abstract = kinds.has(modifier::ABSTRACT);
                        let elt_type = p_hint(&c.type_, env)?;
                        let full_type = build_elt(pos.clone(), elt_type);
                        let kind = if has_abstract {
                            ast::ClassConstKind::CCAbstract(None)
                        } else {
                            ast::ClassConstKind::CCConcrete(p_simple_initializer(
                                &c.initializer,
                                env,
                            )?)
                        };
                        let class_const = ast::ClassConst {
                            user_attributes: vec![],
                            type_: Some(full_type),
                            id: name,
                            kind,
                            doc_comment: None,
                        };
                        enum_class.consts.push(class_const)
                    }
                    _ => {
                        let pos = p_pos(n, env);
                        raise_parsing_error_pos(
                            &pos,
                            env,
                            &syntax_error::invalid_enum_class_enumerator,
                        )
                    }
                }
            }
            Ok(vec![ast::Def::mk_class(enum_class)])
        }
        InclusionDirective(c) if env.file_mode() != file_info::Mode::Mhhi || env.codegen() => {
            let expr = p_expr(&c.expression, env)?;
            Ok(vec![ast::Def::mk_stmt(ast::Stmt::new(
                p_pos(node, env),
                ast::Stmt_::mk_expr(expr),
            ))])
        }
        NamespaceDeclaration(c) => {
            let name = if let NamespaceDeclarationHeader(h) = &c.header.children {
                &h.name
            } else {
                return missing_syntax("namespace_declaration_header", node, env);
            };
            let defs = match &c.body.children {
                NamespaceBody(c) => {
                    let mut env1 = Env::clone_and_unset_toplevel_if_toplevel(env);
                    let env1 = env1.as_mut();
                    itertools::concat(
                        c.declarations
                            .syntax_node_to_list_skip_separator()
                            .map(|n| p_def(n, env1))
                            .collect::<Result<Vec<Vec<_>>, _>>()?,
                    )
                }
                _ => vec![],
            };
            Ok(vec![ast::Def::mk_namespace(pos_name(name, env)?, defs)])
        }
        NamespaceGroupUseDeclaration(c) => {
            let uses: Result<Vec<_>, _> = c
                .clauses
                .syntax_node_to_list_skip_separator()
                .map(|n| {
                    p_namespace_use_clause(
                        Some(&c.prefix),
                        p_namespace_use_kind(&c.kind, env),
                        n,
                        env,
                    )
                })
                .collect();
            Ok(vec![ast::Def::mk_namespace_use(uses?)])
        }
        NamespaceUseDeclaration(c) => {
            let uses: Result<Vec<_>, _> = c
                .clauses
                .syntax_node_to_list_skip_separator()
                .map(|n| p_namespace_use_clause(None, p_namespace_use_kind(&c.kind, env), n, env))
                .collect();
            Ok(vec![ast::Def::mk_namespace_use(uses?)])
        }
        FileAttributeSpecification(_) => {
            Ok(vec![ast::Def::mk_file_attributes(ast::FileAttribute {
                user_attributes: p_user_attribute(node, env)?,
                namespace: mk_empty_ns_env(env),
            })])
        }
        ModuleDeclaration(md) => Ok(vec![ast::Def::mk_module(ast::ModuleDef {
            annotation: (),
            name: pos_name(&md.name, env)?,
            user_attributes: p_user_attributes(&md.attribute_spec, env)?,
            span: p_pos(node, env),
            mode: env.file_mode(),
        })]),
        _ if env.file_mode() == file_info::Mode::Mhhi => Ok(vec![]),
        _ => Ok(vec![ast::Def::mk_stmt(p_stmt(node, env)?)]),
    }
}