https://gcc.gnu.org/g:f77aeb99dde12544ea7c29004e88d46a8d5aa7b2
commit f77aeb99dde12544ea7c29004e88d46a8d5aa7b2 Author: Pierre-Emmanuel Patry <pierre-emmanuel.pa...@embecosm.com> Date: Tue Apr 1 15:57:47 2025 +0200 Make loop label truly optional A loop label error state was in use to represent missing loop label but this may be easily forgotten and the optional nature of the label was misrepresented. gcc/rust/ChangeLog: * ast/rust-ast-builder.cc (Builder::block): Call with a nullopt instead of an error loop label. (WhileLetLoopExpr::as_string): Use getter function and adapt to newtype. * ast/rust-ast.cc (WhileLoopExpr::as_string): Likewise. (LoopExpr::as_string): Likewise. (BreakExpr::as_string): Likewise. (ForLoopExpr::as_string): Likewise. * ast/rust-expr.h (class BlockExpr): Make loop label optional. (class BreakExpr): Likewise. * expand/rust-derive-clone.cc (DeriveClone::clone_fn): Use nullopt. * expand/rust-derive-debug.cc (DeriveDebug::stub_debug_fn): Likewise. * expand/rust-derive-default.cc (DeriveDefault::default_fn): Likewise. * expand/rust-derive-eq.cc: Likewise. * parse/rust-parse-impl.h (Parser::parse_block_expr): Use optional for arguments. (Parser::parse_loop_expr): Likewise. (Parser::parse_while_loop_expr): Likewise. (Parser::parse_while_let_loop_expr): Likewise. (Parser::parse_for_loop_expr): Likewise. (Parser::parse_labelled_loop_expr): Likewise. (Parser::parse_loop_label): Return an optional. * parse/rust-parse.h: Update function prototype and use nullopt for default values. Signed-off-by: Pierre-Emmanuel Patry <pierre-emmanuel.pa...@embecosm.com> Diff: --- gcc/rust/ast/rust-ast-builder.cc | 6 +-- gcc/rust/ast/rust-ast.cc | 10 ++--- gcc/rust/ast/rust-expr.h | 35 ++++++++------- gcc/rust/expand/rust-derive-clone.cc | 3 +- gcc/rust/expand/rust-derive-debug.cc | 3 +- gcc/rust/expand/rust-derive-default.cc | 3 +- gcc/rust/expand/rust-derive-eq.cc | 3 +- gcc/rust/parse/rust-parse-impl.h | 80 +++++++++++++++++----------------- gcc/rust/parse/rust-parse.h | 12 ++--- 9 files changed, 77 insertions(+), 78 deletions(-) diff --git a/gcc/rust/ast/rust-ast-builder.cc b/gcc/rust/ast/rust-ast-builder.cc index 2e3685f2b198..437ba1b56ea0 100644 --- a/gcc/rust/ast/rust-ast-builder.cc +++ b/gcc/rust/ast/rust-ast-builder.cc @@ -335,9 +335,9 @@ std::unique_ptr<BlockExpr> Builder::block (std::vector<std::unique_ptr<Stmt>> &&stmts, std::unique_ptr<Expr> &&tail_expr) const { - return std::unique_ptr<BlockExpr> ( - new BlockExpr (std::move (stmts), std::move (tail_expr), {}, {}, - LoopLabel::error (), loc, loc)); + return std::unique_ptr<BlockExpr> (new BlockExpr (std::move (stmts), + std::move (tail_expr), {}, + {}, tl::nullopt, loc, loc)); } std::unique_ptr<Expr> diff --git a/gcc/rust/ast/rust-ast.cc b/gcc/rust/ast/rust-ast.cc index 43f1ee2f5266..2d286cdf3d70 100644 --- a/gcc/rust/ast/rust-ast.cc +++ b/gcc/rust/ast/rust-ast.cc @@ -2095,7 +2095,7 @@ WhileLoopExpr::as_string () const if (!has_loop_label ()) str += "none"; else - str += loop_label.as_string (); + str += get_loop_label ().as_string (); str += "\n Conditional expr: " + condition->as_string (); @@ -2115,7 +2115,7 @@ WhileLetLoopExpr::as_string () const if (!has_loop_label ()) str += "none"; else - str += loop_label.as_string (); + str += get_loop_label ().as_string (); str += "\n Match arm patterns: "; if (match_arm_patterns.empty ()) @@ -2146,7 +2146,7 @@ LoopExpr::as_string () const if (!has_loop_label ()) str += "none"; else - str += loop_label.as_string (); + str += get_loop_label ().as_string (); str += "\n Loop block: " + loop_block->as_string (); @@ -2183,7 +2183,7 @@ BreakExpr::as_string () const std::string str ("break "); if (has_label ()) - str += label.as_string () + " "; + str += get_label ().as_string () + " "; if (has_break_expr ()) str += break_expr->as_string (); @@ -2545,7 +2545,7 @@ ForLoopExpr::as_string () const if (!has_loop_label ()) str += "none"; else - str += loop_label.as_string (); + str += get_loop_label ().as_string (); str += "\n Pattern: " + pattern->as_string (); diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h index cff09fe17d7b..3ce78c643c73 100644 --- a/gcc/rust/ast/rust-expr.h +++ b/gcc/rust/ast/rust-expr.h @@ -2590,7 +2590,7 @@ class BlockExpr : public ExprWithBlock std::vector<Attribute> inner_attrs; std::vector<std::unique_ptr<Stmt> > statements; std::unique_ptr<Expr> expr; - LoopLabel label; + tl::optional<LoopLabel> label; location_t start_locus; location_t end_locus; bool marked_for_strip = false; @@ -2607,8 +2607,9 @@ public: BlockExpr (std::vector<std::unique_ptr<Stmt> > block_statements, std::unique_ptr<Expr> block_expr, std::vector<Attribute> inner_attribs, - std::vector<Attribute> outer_attribs, LoopLabel label, - location_t start_locus, location_t end_locus) + std::vector<Attribute> outer_attribs, + tl::optional<LoopLabel> label, location_t start_locus, + location_t end_locus) : outer_attrs (std::move (outer_attribs)), inner_attrs (std::move (inner_attribs)), statements (std::move (block_statements)), expr (std::move (block_expr)), @@ -2727,8 +2728,8 @@ public: outer_attrs = std::move (new_attrs); } - bool has_label () { return !label.is_error (); } - LoopLabel &get_label () { return label; } + bool has_label () { return label.has_value (); } + LoopLabel &get_label () { return label.value (); } Expr::Kind get_expr_kind () const override { return Expr::Kind::Block; } @@ -2901,7 +2902,7 @@ protected: class BreakExpr : public ExprWithoutBlock { std::vector<Attribute> outer_attrs; - LoopLabel label; + tl::optional<LoopLabel> label; std::unique_ptr<Expr> break_expr; location_t locus; @@ -2912,7 +2913,7 @@ public: std::string as_string () const override; // Returns whether the break expression has a label or not. - bool has_label () const { return !label.is_error (); } + bool has_label () const { return label.has_value (); } /* Returns whether the break expression has an expression used in the break or * not. */ @@ -2981,7 +2982,8 @@ public: outer_attrs = std::move (new_attrs); } - LoopLabel &get_label () { return label; } + LoopLabel &get_label () { return label.value (); } + const LoopLabel &get_label () const { return label.value (); } Expr::Kind get_expr_kind () const override { return Expr::Kind::Break; } @@ -3657,7 +3659,7 @@ class BaseLoopExpr : public ExprWithBlock protected: // protected to allow subclasses better use of them std::vector<Attribute> outer_attrs; - LoopLabel loop_label; + tl::optional<LoopLabel> loop_label; std::unique_ptr<BlockExpr> loop_block; private: @@ -3666,7 +3668,7 @@ private: protected: // Constructor for BaseLoopExpr BaseLoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus, - LoopLabel loop_label = LoopLabel::error (), + tl::optional<LoopLabel> loop_label = tl::nullopt, std::vector<Attribute> outer_attribs = std::vector<Attribute> ()) : outer_attrs (std::move (outer_attribs)), @@ -3706,9 +3708,10 @@ protected: BaseLoopExpr &operator= (BaseLoopExpr &&other) = default; public: - bool has_loop_label () const { return !loop_label.is_error (); } + bool has_loop_label () const { return loop_label.has_value (); } - LoopLabel &get_loop_label () { return loop_label; } + LoopLabel &get_loop_label () { return loop_label.value (); } + const LoopLabel &get_loop_label () const { return loop_label.value (); } location_t get_locus () const override final { return locus; } @@ -3752,7 +3755,7 @@ public: // Constructor for LoopExpr LoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus, - LoopLabel loop_label = LoopLabel::error (), + tl::optional<LoopLabel> loop_label = tl::nullopt, std::vector<Attribute> outer_attribs = std::vector<Attribute> ()) : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), std::move (outer_attribs)) @@ -3785,7 +3788,7 @@ public: // Constructor for while loop with loop label WhileLoopExpr (std::unique_ptr<Expr> loop_condition, std::unique_ptr<BlockExpr> loop_block, location_t locus, - LoopLabel loop_label = LoopLabel::error (), + tl::optional<LoopLabel> loop_label = tl::nullopt, std::vector<Attribute> outer_attribs = std::vector<Attribute> ()) : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), @@ -3851,7 +3854,7 @@ public: WhileLetLoopExpr (std::vector<std::unique_ptr<Pattern> > match_arm_patterns, std::unique_ptr<Expr> scrutinee, std::unique_ptr<BlockExpr> loop_block, location_t locus, - LoopLabel loop_label = LoopLabel::error (), + tl::optional<LoopLabel> loop_label = tl::nullopt, std::vector<Attribute> outer_attribs = std::vector<Attribute> ()) : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), @@ -3938,7 +3941,7 @@ public: ForLoopExpr (std::unique_ptr<Pattern> loop_pattern, std::unique_ptr<Expr> iterator_expr, std::unique_ptr<BlockExpr> loop_body, location_t locus, - LoopLabel loop_label = LoopLabel::error (), + tl::optional<LoopLabel> loop_label = tl::nullopt, std::vector<Attribute> outer_attribs = std::vector<Attribute> ()) : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label), std::move (outer_attribs)), diff --git a/gcc/rust/expand/rust-derive-clone.cc b/gcc/rust/expand/rust-derive-clone.cc index 00abb593c17e..d8f79d00106d 100644 --- a/gcc/rust/expand/rust-derive-clone.cc +++ b/gcc/rust/expand/rust-derive-clone.cc @@ -61,8 +61,7 @@ std::unique_ptr<AssociatedItem> DeriveClone::clone_fn (std::unique_ptr<Expr> &&clone_expr) { auto block = std::unique_ptr<BlockExpr> ( - new BlockExpr ({}, std::move (clone_expr), {}, {}, AST::LoopLabel::error (), - loc, loc)); + new BlockExpr ({}, std::move (clone_expr), {}, {}, tl::nullopt, loc, loc)); auto big_self_type = builder.single_type_path ("Self"); std::unique_ptr<SelfParam> self (new SelfParam (Lifetime::error (), diff --git a/gcc/rust/expand/rust-derive-debug.cc b/gcc/rust/expand/rust-derive-debug.cc index 7ad3908483ae..a0bf9d869721 100644 --- a/gcc/rust/expand/rust-derive-debug.cc +++ b/gcc/rust/expand/rust-derive-debug.cc @@ -50,8 +50,7 @@ DeriveDebug::stub_debug_fn () // we can't use builder.block() here as it returns a unique_ptr<Expr> and // Function's constructor expects a unique_ptr<BlockExpr> auto block = std::unique_ptr<BlockExpr> ( - new BlockExpr ({}, std::move (stub_return), {}, {}, - AST::LoopLabel::error (), loc, loc)); + new BlockExpr ({}, std::move (stub_return), {}, {}, tl::nullopt, loc, loc)); auto self = builder.self_ref_param (); diff --git a/gcc/rust/expand/rust-derive-default.cc b/gcc/rust/expand/rust-derive-default.cc index c54f8c318dde..2e8b45681994 100644 --- a/gcc/rust/expand/rust-derive-default.cc +++ b/gcc/rust/expand/rust-derive-default.cc @@ -58,8 +58,7 @@ DeriveDefault::default_fn (std::unique_ptr<Expr> &&return_expr) = std::unique_ptr<Type> (new TypePath (builder.type_path ("Self"))); auto block = std::unique_ptr<BlockExpr> ( - new BlockExpr ({}, std::move (return_expr), {}, {}, - AST::LoopLabel::error (), loc, loc)); + new BlockExpr ({}, std::move (return_expr), {}, {}, tl::nullopt, loc, loc)); return builder.function ("default", {}, std::move (self_ty), std::move (block)); diff --git a/gcc/rust/expand/rust-derive-eq.cc b/gcc/rust/expand/rust-derive-eq.cc index dc173defd67e..5e7a8946dfdb 100644 --- a/gcc/rust/expand/rust-derive-eq.cc +++ b/gcc/rust/expand/rust-derive-eq.cc @@ -49,8 +49,7 @@ DeriveEq::assert_receiver_is_total_eq_fn ( stmts.emplace_back (assert_type_is_eq (std::move (type))); auto block = std::unique_ptr<BlockExpr> ( - new BlockExpr (std::move (stmts), nullptr, {}, {}, AST::LoopLabel::error (), - loc, loc)); + new BlockExpr (std::move (stmts), nullptr, {}, {}, tl::nullopt, loc, loc)); auto self = builder.self_ref_param (); diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index bd5011329142..e2a3440efa93 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -7153,9 +7153,9 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs, // Parses a block expression, including the curly braces at start and end. template <typename ManagedTokenSource> std::unique_ptr<AST::BlockExpr> -Parser<ManagedTokenSource>::parse_block_expr (AST::AttrVec outer_attrs, - AST::LoopLabel label, - location_t pratt_parsed_loc) +Parser<ManagedTokenSource>::parse_block_expr ( + AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label, + location_t pratt_parsed_loc) { location_t locus = pratt_parsed_loc; if (locus == UNKNOWN_LOCATION) @@ -7563,14 +7563,14 @@ Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs, // Parses a loop label used in loop expressions. template <typename ManagedTokenSource> -AST::LoopLabel +tl::optional<AST::LoopLabel> Parser<ManagedTokenSource>::parse_loop_label (const_TokenPtr tok) { // parse lifetime - if doesn't exist, assume no label if (tok->get_id () != LIFETIME) { // not necessarily an error - return AST::LoopLabel::error (); + return tl::nullopt; } /* FIXME: check for named lifetime requirement here? or check in semantic * analysis phase? */ @@ -7579,10 +7579,11 @@ Parser<ManagedTokenSource>::parse_loop_label (const_TokenPtr tok) if (!skip_token (COLON)) { // skip somewhere? - return AST::LoopLabel::error (); + return tl::nullopt; } - return AST::LoopLabel (std::move (label), tok->get_locus ()); + return tl::optional<AST::LoopLabel> ( + AST::LoopLabel (std::move (label), tok->get_locus ())); } /* Parses an if expression of any kind, including with else, else if, else if @@ -7935,16 +7936,16 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs, template <typename ManagedTokenSource> std::unique_ptr<AST::LoopExpr> Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs, - AST::LoopLabel label, + tl::optional<AST::LoopLabel> label, location_t pratt_parsed_loc) { location_t locus = pratt_parsed_loc; if (locus == UNKNOWN_LOCATION) { - if (label.is_error ()) - locus = lexer.peek_token ()->get_locus (); + if (label) + locus = label->get_locus (); else - locus = label.get_locus (); + locus = lexer.peek_token ()->get_locus (); if (!skip_token (LOOP)) { @@ -7954,8 +7955,8 @@ Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs, } else { - if (!label.is_error ()) - locus = label.get_locus (); + if (label) + locus = label->get_locus (); } // parse loop body, which is required @@ -7978,17 +7979,17 @@ Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs, * via parse_labelled_loop_expr, which would call this. */ template <typename ManagedTokenSource> std::unique_ptr<AST::WhileLoopExpr> -Parser<ManagedTokenSource>::parse_while_loop_expr (AST::AttrVec outer_attrs, - AST::LoopLabel label, - location_t pratt_parsed_loc) +Parser<ManagedTokenSource>::parse_while_loop_expr ( + AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label, + location_t pratt_parsed_loc) { location_t locus = pratt_parsed_loc; if (locus == UNKNOWN_LOCATION) { - if (label.is_error ()) - locus = lexer.peek_token ()->get_locus (); + if (label) + locus = label->get_locus (); else - locus = label.get_locus (); + locus = lexer.peek_token ()->get_locus (); if (!skip_token (WHILE)) { @@ -7998,8 +7999,8 @@ Parser<ManagedTokenSource>::parse_while_loop_expr (AST::AttrVec outer_attrs, } else { - if (!label.is_error ()) - locus = label.get_locus (); + if (label) + locus = label->get_locus (); } // ensure it isn't a while let loop @@ -8051,14 +8052,14 @@ Parser<ManagedTokenSource>::parse_while_loop_expr (AST::AttrVec outer_attrs, * parsed via parse_labelled_loop_expr, which would call this. */ template <typename ManagedTokenSource> std::unique_ptr<AST::WhileLetLoopExpr> -Parser<ManagedTokenSource>::parse_while_let_loop_expr (AST::AttrVec outer_attrs, - AST::LoopLabel label) +Parser<ManagedTokenSource>::parse_while_let_loop_expr ( + AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label) { location_t locus = UNKNOWN_LOCATION; - if (label.is_error ()) - locus = lexer.peek_token ()->get_locus (); + if (label) + locus = label->get_locus (); else - locus = label.get_locus (); + locus = lexer.peek_token ()->get_locus (); maybe_skip_token (WHILE); /* check for possible accidental recognition of a while loop as a while let @@ -8125,14 +8126,14 @@ Parser<ManagedTokenSource>::parse_while_let_loop_expr (AST::AttrVec outer_attrs, * parse_labelled_loop_expr, which would call this. */ template <typename ManagedTokenSource> std::unique_ptr<AST::ForLoopExpr> -Parser<ManagedTokenSource>::parse_for_loop_expr (AST::AttrVec outer_attrs, - AST::LoopLabel label) +Parser<ManagedTokenSource>::parse_for_loop_expr ( + AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label) { location_t locus = UNKNOWN_LOCATION; - if (label.is_error ()) - locus = lexer.peek_token ()->get_locus (); + if (label) + locus = label->get_locus (); else - locus = label.get_locus (); + locus = lexer.peek_token ()->get_locus (); maybe_skip_token (FOR); // parse pattern, which is required @@ -8210,8 +8211,9 @@ Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok, } // parse loop label (required) - AST::LoopLabel label = parse_loop_label (tok); - if (label.is_error ()) + // TODO: Convert this return type to tl::expected instead of tl::optional + tl::optional<AST::LoopLabel> label = parse_loop_label (tok); + if (!label) { Error error (lexer.peek_token ()->get_locus (), "failed to parse loop label in labelled loop expr"); @@ -12400,8 +12402,8 @@ Parser<ManagedTokenSource>::null_denotation_not_path ( return parse_continue_expr (std::move (outer_attrs), tok->get_locus ()); case LEFT_CURLY: // ok - this is an expression with block for once. - return parse_block_expr (std::move (outer_attrs), - AST::LoopLabel::error (), tok->get_locus ()); + return parse_block_expr (std::move (outer_attrs), tl::nullopt, + tok->get_locus ()); case IF: // if or if let, so more lookahead to find out if (lexer.peek_token ()->get_id () == LET) @@ -12417,7 +12419,7 @@ Parser<ManagedTokenSource>::null_denotation_not_path ( case LIFETIME: return parse_labelled_loop_expr (tok, std::move (outer_attrs)); case LOOP: - return parse_loop_expr (std::move (outer_attrs), AST::LoopLabel::error (), + return parse_loop_expr (std::move (outer_attrs), tl::nullopt, tok->get_locus ()); case WHILE: if (lexer.peek_token ()->get_id () == LET) @@ -12426,13 +12428,11 @@ Parser<ManagedTokenSource>::null_denotation_not_path ( } else { - return parse_while_loop_expr (std::move (outer_attrs), - AST::LoopLabel::error (), + return parse_while_loop_expr (std::move (outer_attrs), tl::nullopt, tok->get_locus ()); } case FOR: - return parse_for_loop_expr (std::move (outer_attrs), - AST::LoopLabel::error ()); + return parse_for_loop_expr (std::move (outer_attrs), tl::nullopt); case MATCH_KW: // also an expression with block return parse_match_expr (std::move (outer_attrs), tok->get_locus ()); diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h index 6c50ba9523d9..fc548141c44f 100644 --- a/gcc/rust/parse/rust-parse.h +++ b/gcc/rust/parse/rust-parse.h @@ -148,7 +148,7 @@ public: std::unique_ptr<AST::BlockExpr> parse_block_expr (AST::AttrVec outer_attrs = AST::AttrVec (), - AST::LoopLabel label = AST::LoopLabel::error (), + tl::optional<AST::LoopLabel> = tl::nullopt, location_t pratt_parsed_loc = UNKNOWN_LOCATION); bool is_macro_rules_def (const_TokenPtr t); @@ -588,18 +588,18 @@ private: location_t pratt_parsed_loc = UNKNOWN_LOCATION); std::unique_ptr<AST::LoopExpr> parse_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (), - AST::LoopLabel label = AST::LoopLabel::error (), + tl::optional<AST::LoopLabel> label = tl::nullopt, location_t pratt_parsed_loc = UNKNOWN_LOCATION); std::unique_ptr<AST::WhileLoopExpr> parse_while_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (), - AST::LoopLabel label = AST::LoopLabel::error (), + tl::optional<AST::LoopLabel> label = tl::nullopt, location_t pratt_parsed_loc = UNKNOWN_LOCATION); std::unique_ptr<AST::WhileLetLoopExpr> parse_while_let_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (), - AST::LoopLabel label = AST::LoopLabel::error ()); + tl::optional<AST::LoopLabel> label = tl::nullopt); std::unique_ptr<AST::ForLoopExpr> parse_for_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (), - AST::LoopLabel label = AST::LoopLabel::error ()); + tl::optional<AST::LoopLabel> label = tl::nullopt); std::unique_ptr<AST::MatchExpr> parse_match_expr (AST::AttrVec outer_attrs = AST::AttrVec (), location_t pratt_parsed_loc = UNKNOWN_LOCATION); @@ -609,7 +609,7 @@ private: std::unique_ptr<AST::Expr> parse_labelled_loop_expr (const_TokenPtr tok, AST::AttrVec outer_attrs = AST::AttrVec ()); - AST::LoopLabel parse_loop_label (const_TokenPtr tok); + tl::optional<AST::LoopLabel> parse_loop_label (const_TokenPtr tok); std::unique_ptr<AST::AsyncBlockExpr> parse_async_block_expr (AST::AttrVec outer_attrs = AST::AttrVec ()); std::unique_ptr<AST::GroupedExpr> parse_grouped_expr (AST::AttrVec outer_attrs