Workaround unstable stmt_expr_attributes for method receiver expressions.
This commit is contained in:
parent
6c9c2d862d
commit
74d4eefc13
9 changed files with 121 additions and 19 deletions
|
@ -152,6 +152,12 @@ pub trait MutVisitor: Sized {
|
||||||
noop_visit_expr(e, self);
|
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<Expr>) {
|
||||||
|
self.visit_expr(ex)
|
||||||
|
}
|
||||||
|
|
||||||
fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
|
fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
|
||||||
noop_filter_map_expr(e, self)
|
noop_filter_map_expr(e, self)
|
||||||
}
|
}
|
||||||
|
@ -1301,7 +1307,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||||
vis.visit_ident(ident);
|
vis.visit_ident(ident);
|
||||||
vis.visit_id(id);
|
vis.visit_id(id);
|
||||||
visit_opt(args, |args| vis.visit_generic_args(args));
|
visit_opt(args, |args| vis.visit_generic_args(args));
|
||||||
vis.visit_expr(receiver);
|
vis.visit_method_receiver_expr(receiver);
|
||||||
visit_exprs(exprs, vis);
|
visit_exprs(exprs, vis);
|
||||||
vis.visit_span(span);
|
vis.visit_span(span);
|
||||||
}
|
}
|
||||||
|
@ -1589,3 +1595,9 @@ impl DummyAstNode for Crate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N: DummyAstNode, T: DummyAstNode> DummyAstNode for crate::ast_traits::AstNodeWrapper<N, T> {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
crate::ast_traits::AstNodeWrapper::new(N::dummy(), T::dummy())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -140,6 +140,11 @@ pub trait Visitor<'ast>: Sized {
|
||||||
fn visit_expr(&mut self, ex: &'ast Expr) {
|
fn visit_expr(&mut self, ex: &'ast Expr) {
|
||||||
walk_expr(self, ex)
|
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_expr_post(&mut self, _ex: &'ast Expr) {}
|
||||||
fn visit_ty(&mut self, t: &'ast Ty) {
|
fn visit_ty(&mut self, t: &'ast Ty) {
|
||||||
walk_ty(self, t)
|
walk_ty(self, t)
|
||||||
|
|
|
@ -210,8 +210,15 @@ impl CfgEval<'_, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MutVisitor for CfgEval<'_, '_> {
|
impl MutVisitor for CfgEval<'_, '_> {
|
||||||
|
#[instrument(level = "trace", skip(self))]
|
||||||
fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
|
fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
|
||||||
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<ast::Expr>) {
|
||||||
|
self.cfg.configure_expr(expr, true);
|
||||||
mut_visit::noop_visit_expr(expr, self);
|
mut_visit::noop_visit_expr(expr, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -469,6 +469,7 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If attributes are not allowed on expressions, emit an error for `attr`
|
/// 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) {
|
pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
|
||||||
if !self.features.map_or(true, |features| features.stmt_expr_attributes) {
|
if !self.features.map_or(true, |features| features.stmt_expr_attributes) {
|
||||||
let mut err = feature_err(
|
let mut err = feature_err(
|
||||||
|
@ -486,10 +487,13 @@ impl<'a> StripUnconfigured<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_expr(&self, expr: &mut P<ast::Expr>) {
|
#[instrument(level = "trace", skip(self))]
|
||||||
|
pub fn configure_expr(&self, expr: &mut P<ast::Expr>, method_receiver: bool) {
|
||||||
|
if !method_receiver {
|
||||||
for attr in expr.attrs.iter() {
|
for attr in expr.attrs.iter() {
|
||||||
self.maybe_emit_expr_attr_err(attr);
|
self.maybe_emit_expr_attr_err(attr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If an expr is valid to cfg away it will have been removed by the
|
// If an expr is valid to cfg away it will have been removed by the
|
||||||
// outer stmt or expression folder before descending in here.
|
// outer stmt or expression folder before descending in here.
|
||||||
|
|
|
@ -50,6 +50,7 @@ macro_rules! ast_fragments {
|
||||||
/// Can also serve as an input and intermediate result for macro expansion operations.
|
/// Can also serve as an input and intermediate result for macro expansion operations.
|
||||||
pub enum AstFragment {
|
pub enum AstFragment {
|
||||||
OptExpr(Option<P<ast::Expr>>),
|
OptExpr(Option<P<ast::Expr>>),
|
||||||
|
MethodReceiverExpr(P<ast::Expr>),
|
||||||
$($Kind($AstTy),)*
|
$($Kind($AstTy),)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ macro_rules! ast_fragments {
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum AstFragmentKind {
|
pub enum AstFragmentKind {
|
||||||
OptExpr,
|
OptExpr,
|
||||||
|
MethodReceiverExpr,
|
||||||
$($Kind,)*
|
$($Kind,)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +66,7 @@ macro_rules! ast_fragments {
|
||||||
pub fn name(self) -> &'static str {
|
pub fn name(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
AstFragmentKind::OptExpr => "expression",
|
AstFragmentKind::OptExpr => "expression",
|
||||||
|
AstFragmentKind::MethodReceiverExpr => "expression",
|
||||||
$(AstFragmentKind::$Kind => $kind_name,)*
|
$(AstFragmentKind::$Kind => $kind_name,)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +75,8 @@ macro_rules! ast_fragments {
|
||||||
match self {
|
match self {
|
||||||
AstFragmentKind::OptExpr =>
|
AstFragmentKind::OptExpr =>
|
||||||
result.make_expr().map(Some).map(AstFragment::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),)*
|
$(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +103,13 @@ macro_rules! ast_fragments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_method_receiver_expr(self) -> P<ast::Expr> {
|
||||||
|
match self {
|
||||||
|
AstFragment::MethodReceiverExpr(expr) => expr,
|
||||||
|
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(pub fn $make_ast(self) -> $AstTy {
|
$(pub fn $make_ast(self) -> $AstTy {
|
||||||
match self {
|
match self {
|
||||||
AstFragment::$Kind(ast) => ast,
|
AstFragment::$Kind(ast) => ast,
|
||||||
|
@ -120,6 +132,7 @@ macro_rules! ast_fragments {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr),
|
||||||
$($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)*
|
$($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)*
|
||||||
$($(AstFragment::$Kind(ast) =>
|
$($(AstFragment::$Kind(ast) =>
|
||||||
ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)?)*
|
ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)?)*
|
||||||
|
@ -130,6 +143,7 @@ macro_rules! ast_fragments {
|
||||||
match *self {
|
match *self {
|
||||||
AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr),
|
AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr),
|
||||||
AstFragment::OptExpr(None) => {}
|
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) => visitor.$visit_ast(ast),)?)*
|
||||||
$($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] {
|
$($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] {
|
||||||
visitor.$visit_ast_elt(ast_elt, $($args)*);
|
visitor.$visit_ast_elt(ast_elt, $($args)*);
|
||||||
|
@ -222,6 +236,7 @@ impl AstFragmentKind {
|
||||||
match self {
|
match self {
|
||||||
AstFragmentKind::OptExpr
|
AstFragmentKind::OptExpr
|
||||||
| AstFragmentKind::Expr
|
| AstFragmentKind::Expr
|
||||||
|
| AstFragmentKind::MethodReceiverExpr
|
||||||
| AstFragmentKind::Stmts
|
| AstFragmentKind::Stmts
|
||||||
| AstFragmentKind::Ty
|
| AstFragmentKind::Ty
|
||||||
| AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false },
|
| AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false },
|
||||||
|
@ -285,6 +300,9 @@ impl AstFragmentKind {
|
||||||
AstFragmentKind::Expr => AstFragment::Expr(
|
AstFragmentKind::Expr => AstFragment::Expr(
|
||||||
items.next().expect("expected exactly one expression").expect_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 => {
|
AstFragmentKind::OptExpr => {
|
||||||
AstFragment::OptExpr(items.next().map(Annotatable::expect_expr))
|
AstFragment::OptExpr(items.next().map(Annotatable::expect_expr))
|
||||||
}
|
}
|
||||||
|
@ -893,6 +911,7 @@ pub fn parse_ast_fragment<'a>(
|
||||||
AstFragment::Stmts(stmts)
|
AstFragment::Stmts(stmts)
|
||||||
}
|
}
|
||||||
AstFragmentKind::Expr => AstFragment::Expr(this.parse_expr()?),
|
AstFragmentKind::Expr => AstFragment::Expr(this.parse_expr()?),
|
||||||
|
AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(this.parse_expr()?),
|
||||||
AstFragmentKind::OptExpr => {
|
AstFragmentKind::OptExpr => {
|
||||||
if this.token != token::Eof {
|
if this.token != token::Eof {
|
||||||
AstFragment::OptExpr(Some(this.parse_expr()?))
|
AstFragment::OptExpr(Some(this.parse_expr()?))
|
||||||
|
@ -1477,6 +1496,42 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, OptExprTag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<P<ast::Expr>, 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<V: MutVisitor>(&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<ast::MacCall>, 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> {
|
struct InvocationCollector<'a, 'b> {
|
||||||
cx: &'a mut ExtCtxt<'b>,
|
cx: &'a mut ExtCtxt<'b>,
|
||||||
invocations: Vec<(Invocation, Option<Lrc<SyntaxExtension>>)>,
|
invocations: Vec<(Invocation, Option<Lrc<SyntaxExtension>>)>,
|
||||||
|
@ -1840,6 +1895,14 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
||||||
self.visit_node(node)
|
self.visit_node(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_method_receiver_expr(&mut self, node: &mut P<ast::Expr>) {
|
||||||
|
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<ast::Expr>) -> Option<P<ast::Expr>> {
|
fn filter_map_expr(&mut self, node: P<ast::Expr>) -> Option<P<ast::Expr>> {
|
||||||
self.flat_map_node(AstNodeWrapper::new(node, OptExprTag))
|
self.flat_map_node(AstNodeWrapper::new(node, OptExprTag))
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ pub fn placeholder(
|
||||||
}),
|
}),
|
||||||
AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()),
|
AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()),
|
||||||
AstFragmentKind::OptExpr => AstFragment::OptExpr(Some(expr_placeholder())),
|
AstFragmentKind::OptExpr => AstFragment::OptExpr(Some(expr_placeholder())),
|
||||||
|
AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(expr_placeholder()),
|
||||||
AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item {
|
AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item {
|
||||||
id,
|
id,
|
||||||
span,
|
span,
|
||||||
|
@ -296,6 +297,13 @@ impl MutVisitor for PlaceholderExpander {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_method_receiver_expr(&mut self, expr: &mut P<ast::Expr>) {
|
||||||
|
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<ast::Expr>) -> Option<P<ast::Expr>> {
|
fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ast::ExprKind::MacCall(_) => self.remove(expr.id).make_opt_expr(),
|
ast::ExprKind::MacCall(_) => self.remove(expr.id).make_opt_expr(),
|
||||||
|
|
14
src/test/ui/cfg/cfg-method-receiver-ok.rs
Normal file
14
src/test/ui/cfg/cfg-method-receiver-ok.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
macro_rules! foo {
|
||||||
|
() => {
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
{
|
||||||
|
123i32
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = foo!().abs();
|
||||||
|
}
|
|
@ -7,6 +7,5 @@ macro_rules! cbor_map {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
cbor_map! { #[cfg(test)] 4};
|
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <https://github.com/rust-lang/rust/issues/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
|
error: removing an expression is not supported in this position
|
||||||
--> $DIR/cfg-method-receiver.rs:9:17
|
--> $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};
|
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 this error, try `rustc --explain E0689`.
|
||||||
For more information about an error, try `rustc --explain E0658`.
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue