From 74d4eefc1346a8b9242e072df16de4f664b0873c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 23 Oct 2022 09:22:19 +0000 Subject: [PATCH] Workaround unstable stmt_expr_attributes for method receiver expressions. --- compiler/rustc_ast/src/mut_visit.rs | 14 ++++- compiler/rustc_ast/src/visit.rs | 5 ++ compiler/rustc_builtin_macros/src/cfg_eval.rs | 9 ++- compiler/rustc_expand/src/config.rs | 10 ++- compiler/rustc_expand/src/expand.rs | 63 +++++++++++++++++++ compiler/rustc_expand/src/placeholders.rs | 8 +++ src/test/ui/cfg/cfg-method-receiver-ok.rs | 14 +++++ src/test/ui/cfg/cfg-method-receiver.rs | 3 +- src/test/ui/cfg/cfg-method-receiver.stderr | 14 +---- 9 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/cfg/cfg-method-receiver-ok.rs diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 25022a02f4b..b970e57e017 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -152,6 +152,12 @@ fn visit_expr(&mut self, e: &mut P) { noop_visit_expr(e, self); } + /// This method is a hack to workaround unstable of `stmt_expr_attributes`. + /// It can be removed once that feature is stabilized. + fn visit_method_receiver_expr(&mut self, ex: &mut P) { + self.visit_expr(ex) + } + fn filter_map_expr(&mut self, e: P) -> Option> { noop_filter_map_expr(e, self) } @@ -1301,7 +1307,7 @@ pub fn noop_visit_expr( vis.visit_ident(ident); vis.visit_id(id); visit_opt(args, |args| vis.visit_generic_args(args)); - vis.visit_expr(receiver); + vis.visit_method_receiver_expr(receiver); visit_exprs(exprs, vis); vis.visit_span(span); } @@ -1589,3 +1595,9 @@ fn dummy() -> Self { } } } + +impl DummyAstNode for crate::ast_traits::AstNodeWrapper { + fn dummy() -> Self { + crate::ast_traits::AstNodeWrapper::new(N::dummy(), T::dummy()) + } +} diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e752cc7dc2d..6f56c1ef0e8 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -140,6 +140,11 @@ fn visit_anon_const(&mut self, c: &'ast AnonConst) { fn visit_expr(&mut self, ex: &'ast Expr) { walk_expr(self, ex) } + /// This method is a hack to workaround unstable of `stmt_expr_attributes`. + /// It can be removed once that feature is stabilized. + fn visit_method_receiver_expr(&mut self, ex: &'ast Expr) { + self.visit_expr(ex) + } fn visit_expr_post(&mut self, _ex: &'ast Expr) {} fn visit_ty(&mut self, t: &'ast Ty) { walk_ty(self, t) diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 009f3c783d4..750f1fe121f 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -210,8 +210,15 @@ fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option { + #[instrument(level = "trace", skip(self))] fn visit_expr(&mut self, expr: &mut P) { - self.cfg.configure_expr(expr); + self.cfg.configure_expr(expr, false); + mut_visit::noop_visit_expr(expr, self); + } + + #[instrument(level = "trace", skip(self))] + fn visit_method_receiver_expr(&mut self, expr: &mut P) { + self.cfg.configure_expr(expr, true); mut_visit::noop_visit_expr(expr, self); } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 8d4e3640748..1d2b1298a68 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -469,6 +469,7 @@ pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool { } /// If attributes are not allowed on expressions, emit an error for `attr` + #[instrument(level = "trace", skip(self))] pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { if !self.features.map_or(true, |features| features.stmt_expr_attributes) { let mut err = feature_err( @@ -486,9 +487,12 @@ pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { } } - pub fn configure_expr(&self, expr: &mut P) { - for attr in expr.attrs.iter() { - self.maybe_emit_expr_attr_err(attr); + #[instrument(level = "trace", skip(self))] + pub fn configure_expr(&self, expr: &mut P, method_receiver: bool) { + if !method_receiver { + for attr in expr.attrs.iter() { + self.maybe_emit_expr_attr_err(attr); + } } // If an expr is valid to cfg away it will have been removed by the diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 15e9a8db3c6..57713fb3cd6 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -50,6 +50,7 @@ macro_rules! ast_fragments { /// Can also serve as an input and intermediate result for macro expansion operations. pub enum AstFragment { OptExpr(Option>), + MethodReceiverExpr(P), $($Kind($AstTy),)* } @@ -57,6 +58,7 @@ pub enum AstFragment { #[derive(Copy, Clone, PartialEq, Eq)] pub enum AstFragmentKind { OptExpr, + MethodReceiverExpr, $($Kind,)* } @@ -64,6 +66,7 @@ impl AstFragmentKind { pub fn name(self) -> &'static str { match self { AstFragmentKind::OptExpr => "expression", + AstFragmentKind::MethodReceiverExpr => "expression", $(AstFragmentKind::$Kind => $kind_name,)* } } @@ -72,6 +75,8 @@ fn make_from<'a>(self, result: Box) -> Option { match self { AstFragmentKind::OptExpr => result.make_expr().map(Some).map(AstFragment::OptExpr), + AstFragmentKind::MethodReceiverExpr => + result.make_expr().map(AstFragment::MethodReceiverExpr), $(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)* } } @@ -98,6 +103,13 @@ pub fn make_opt_expr(self) -> Option> { } } + pub fn make_method_receiver_expr(self) -> P { + match self { + AstFragment::MethodReceiverExpr(expr) => expr, + _ => panic!("AstFragment::make_* called on the wrong kind of fragment"), + } + } + $(pub fn $make_ast(self) -> $AstTy { match self { AstFragment::$Kind(ast) => ast, @@ -120,6 +132,7 @@ pub fn mut_visit_with(&mut self, vis: &mut F) { } }); } + AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)* $($(AstFragment::$Kind(ast) => ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)?)* @@ -130,6 +143,7 @@ pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { match *self { AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), AstFragment::OptExpr(None) => {} + AstFragment::MethodReceiverExpr(ref expr) => visitor.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)?)* $($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] { visitor.$visit_ast_elt(ast_elt, $($args)*); @@ -222,6 +236,7 @@ pub fn supports_macro_expansion(self) -> SupportsMacroExpansion { match self { AstFragmentKind::OptExpr | AstFragmentKind::Expr + | AstFragmentKind::MethodReceiverExpr | AstFragmentKind::Stmts | AstFragmentKind::Ty | AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false }, @@ -285,6 +300,9 @@ fn expect_from_annotatables>( AstFragmentKind::Expr => AstFragment::Expr( items.next().expect("expected exactly one expression").expect_expr(), ), + AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr( + items.next().expect("expected exactly one expression").expect_expr(), + ), AstFragmentKind::OptExpr => { AstFragment::OptExpr(items.next().map(Annotatable::expect_expr)) } @@ -893,6 +911,7 @@ pub fn parse_ast_fragment<'a>( AstFragment::Stmts(stmts) } AstFragmentKind::Expr => AstFragment::Expr(this.parse_expr()?), + AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(this.parse_expr()?), AstFragmentKind::OptExpr => { if this.token != token::Eof { AstFragment::OptExpr(Some(this.parse_expr()?)) @@ -1477,6 +1496,42 @@ fn pre_flat_map_node_collect_attr(cfg: &StripUnconfigured<'_>, attr: &ast::Attri } } +/// This struct is a hack to workaround unstable of `stmt_expr_attributes`. +/// It can be removed once that feature is stabilized. +struct MethodReceiverTag; +impl DummyAstNode for MethodReceiverTag { + fn dummy() -> MethodReceiverTag { + MethodReceiverTag + } +} +impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> { + type OutputTy = Self; + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr; + fn descr() -> &'static str { + "an expression" + } + fn to_annotatable(self) -> Annotatable { + Annotatable::Expr(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + AstNodeWrapper::new(fragment.make_method_receiver_expr(), MethodReceiverTag) + } + fn noop_visit(&mut self, visitor: &mut V) { + noop_visit_expr(&mut self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { + let node = self.wrapped.into_inner(); + match node.kind { + ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, invocations: Vec<(Invocation, Option>)>, @@ -1840,6 +1895,14 @@ fn visit_expr(&mut self, node: &mut P) { self.visit_node(node) } + fn visit_method_receiver_expr(&mut self, node: &mut P) { + visit_clobber(node, |node| { + let mut wrapper = AstNodeWrapper::new(node, MethodReceiverTag); + self.visit_node(&mut wrapper); + wrapper.wrapped + }) + } + fn filter_map_expr(&mut self, node: P) -> Option> { self.flat_map_node(AstNodeWrapper::new(node, OptExprTag)) } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 3b0d5ddb97b..faaf3b3fea5 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -55,6 +55,7 @@ fn mac_placeholder() -> P { }), AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()), AstFragmentKind::OptExpr => AstFragment::OptExpr(Some(expr_placeholder())), + AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(expr_placeholder()), AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item { id, span, @@ -296,6 +297,13 @@ fn visit_expr(&mut self, expr: &mut P) { } } + fn visit_method_receiver_expr(&mut self, expr: &mut P) { + match expr.kind { + ast::ExprKind::MacCall(_) => *expr = self.remove(expr.id).make_method_receiver_expr(), + _ => noop_visit_expr(expr, self), + } + } + fn filter_map_expr(&mut self, expr: P) -> Option> { match expr.kind { ast::ExprKind::MacCall(_) => self.remove(expr.id).make_opt_expr(), diff --git a/src/test/ui/cfg/cfg-method-receiver-ok.rs b/src/test/ui/cfg/cfg-method-receiver-ok.rs new file mode 100644 index 00000000000..61ad3b8c17a --- /dev/null +++ b/src/test/ui/cfg/cfg-method-receiver-ok.rs @@ -0,0 +1,14 @@ +// check-pass + +macro_rules! foo { + () => { + #[allow(unreachable_patterns)] + { + 123i32 + } + }; +} + +fn main() { + let _ = foo!().abs(); +} diff --git a/src/test/ui/cfg/cfg-method-receiver.rs b/src/test/ui/cfg/cfg-method-receiver.rs index 78a072f503f..71134ff17b5 100644 --- a/src/test/ui/cfg/cfg-method-receiver.rs +++ b/src/test/ui/cfg/cfg-method-receiver.rs @@ -7,6 +7,5 @@ macro_rules! cbor_map { fn main() { cbor_map! { #[cfg(test)] 4}; - //~^ ERROR attributes on expressions are experimental - //~| ERROR removing an expression is not supported in this position + //~^ ERROR removing an expression is not supported in this position } diff --git a/src/test/ui/cfg/cfg-method-receiver.stderr b/src/test/ui/cfg/cfg-method-receiver.stderr index 517fc8168e7..5767a7c1b4b 100644 --- a/src/test/ui/cfg/cfg-method-receiver.stderr +++ b/src/test/ui/cfg/cfg-method-receiver.stderr @@ -1,12 +1,3 @@ -error[E0658]: attributes on expressions are experimental - --> $DIR/cfg-method-receiver.rs:9:17 - | -LL | cbor_map! { #[cfg(test)] 4}; - | ^^^^^^^^^^^^ - | - = note: see issue #15701 for more information - = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable - error: removing an expression is not supported in this position --> $DIR/cfg-method-receiver.rs:9:17 | @@ -28,7 +19,6 @@ help: you must specify a concrete type for this numeric value, like `i32` LL | cbor_map! { #[cfg(test)] 4_i32}; | ~~~~~ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0658, E0689. -For more information about an error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0689`.