diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index cc8af70a050c9..68a96293891a0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1201,50 +1201,62 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_expr(&mut self, expr: P) -> P { - let mut expr = self.cfg.configure_expr(expr).into_inner(); - expr.node = self.cfg.configure_expr_kind(expr.node); - - // ignore derives so they remain unused - let (attr, expr, after_derive) = self.classify_nonitem(expr); - - if attr.is_some() { - // collect the invoc regardless of whether or not attributes are permitted here - // expansion will eat the attribute so it won't error later - attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); - - // AstFragmentKind::Expr requires the macro to emit an expression - return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), - AstFragmentKind::Expr, after_derive).make_expr(); - } + let expr = self.cfg.configure_expr(expr); + expr.map(|mut expr| { + expr.node = self.cfg.configure_expr_kind(expr.node); + + // ignore derives so they remain unused + let (attr, expr, after_derive) = self.classify_nonitem(expr); + + if attr.is_some() { + // Collect the invoc regardless of whether or not attributes are permitted here + // expansion will eat the attribute so it won't error later. + attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); + + // AstFragmentKind::Expr requires the macro to emit an expression. + return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), + AstFragmentKind::Expr, after_derive) + .make_expr() + .into_inner() + } - if let ast::ExprKind::Mac(mac) = expr.node { - self.check_attributes(&expr.attrs); - self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr() - } else { - P(noop_fold_expr(expr, self)) - } + if let ast::ExprKind::Mac(mac) = expr.node { + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, AstFragmentKind::Expr) + .make_expr() + .into_inner() + } else { + noop_fold_expr(expr, self) + } + }) } fn fold_opt_expr(&mut self, expr: P) -> Option> { - let mut expr = configure!(self, expr).into_inner(); - expr.node = self.cfg.configure_expr_kind(expr.node); + let expr = configure!(self, expr); + expr.filter_map(|mut expr| { + expr.node = self.cfg.configure_expr_kind(expr.node); - // ignore derives so they remain unused - let (attr, expr, after_derive) = self.classify_nonitem(expr); + // Ignore derives so they remain unused. + let (attr, expr, after_derive) = self.classify_nonitem(expr); - if attr.is_some() { - attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); + if attr.is_some() { + attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); - return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), - AstFragmentKind::OptExpr, after_derive).make_opt_expr(); - } + return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), + AstFragmentKind::OptExpr, after_derive) + .make_opt_expr() + .map(|expr| expr.into_inner()) + } - if let ast::ExprKind::Mac(mac) = expr.node { - self.check_attributes(&expr.attrs); - self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr).make_opt_expr() - } else { - Some(P(noop_fold_expr(expr, self))) - } + if let ast::ExprKind::Mac(mac) = expr.node { + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr) + .make_opt_expr() + .map(|expr| expr.into_inner()) + } else { + Some(noop_fold_expr(expr, self)) + } + }) } fn fold_pat(&mut self, pat: P) -> P { diff --git a/src/libsyntax/ptr.rs b/src/libsyntax/ptr.rs index bb47d9b535bef..9fbc64758da4d 100644 --- a/src/libsyntax/ptr.rs +++ b/src/libsyntax/ptr.rs @@ -72,7 +72,7 @@ impl P { *self.ptr } - /// Transform the inner value, consuming `self` and producing a new `P`. + /// Produce a new `P` from `self` without reallocating. pub fn map(mut self, f: F) -> P where F: FnOnce(T) -> T, { @@ -88,8 +88,30 @@ impl P { ptr::write(p, f(ptr::read(p))); // Recreate self from the raw pointer. - P { - ptr: Box::from_raw(p) + P { ptr: Box::from_raw(p) } + } + } + + /// Optionally produce a new `P` from `self` without reallocating. + pub fn filter_map(mut self, f: F) -> Option> where + F: FnOnce(T) -> Option, + { + let p: *mut T = &mut *self.ptr; + + // Leak self in case of panic. + // FIXME(eddyb) Use some sort of "free guard" that + // only deallocates, without dropping the pointee, + // in case the call the `f` below ends in a panic. + mem::forget(self); + + unsafe { + if let Some(v) = f(ptr::read(p)) { + ptr::write(p, v); + + // Recreate self from the raw pointer. + Some(P { ptr: Box::from_raw(p) }) + } else { + None } } }