Auto merge of #29850 - Kimundi:attributes_that_make_a_statement, r=pnkfelix
See https://github.com/rust-lang/rfcs/pull/16 and https://github.com/rust-lang/rust/issues/15701 - Added syntax support for attributes on expressions and all syntax nodes in statement position. - Extended `#[cfg]` folder to allow removal of statements, and of expressions in optional positions like expression lists and trailing block expressions. - Extended lint checker to recognize lint levels on expressions and locals. - As per RFC, attributes are not yet accepted on `if` expressions. Examples: ```rust let x = y; { ... } assert_eq!((1, #[cfg(unset)] 2, 3), (1, 3)); let FOO = 0; ``` Implementation wise, there are a few rough corners and open questions: - The parser work ended up a bit ugly. - The pretty printer change was based mostly on guessing. - Similar to the `if` case, there are some places in the grammar where a new `Expr` node starts, but where it seemed weird to accept attributes and hence the parser doesn't. This includes: - const expressions in patterns - in the middle of an postfix operator chain (that is, after `.`, before indexing, before calls) - on range expressions, since `#[attr] x .. y` parses as `(#[attr] x) .. y`, which is inconsistent with `#[attr] .. y` which would parse as `#[attr] (.. y)` - Attributes are added as additional `Option<Box<Vec<Attribute>>>` fields in expressions and locals. - Memory impact has not been measured yet. - A cfg-away trailing expression in a block does not currently promote the previous `StmtExpr` in a block to a new trailing expr. That is to say, this won't work: ```rust let x = { #[cfg(foo)] Foo { data: x } #[cfg(not(foo))] Foo { data: y } }; ``` - One-element tuples can have their inner expression removed to become Unit, but just Parenthesis can't. Eg, `(#[cfg(unset)] x,) == ()` but `(#[cfg(unset)] x) == error`. This seemed reasonable to me since tuples and unit are type constructors, but could probably be argued either way. - Attributes on macro nodes are currently unconditionally dropped during macro expansion, which seemed fine since macro disappear at that point? - Attributes on `ast::ExprParens` will be prepend-ed to the inner expression in the hir folder. - The work on pretty printer tests for this did trigger, but not fix errors regarding macros: - expression `foo![]` prints as `foo!()` - expression `foo!{}` prints as `foo!()` - statement `foo![];` prints as `foo!();` - statement `foo!{};` prints as `foo!();` - statement `foo!{}` triggers a `None` unwrap ICE.
This commit is contained in:
@@ -233,6 +233,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
dialect: dialect,
|
||||
expn_id: expn_id,
|
||||
}),
|
||||
span: sp
|
||||
span: sp,
|
||||
attrs: None,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
|
||||
use ext;
|
||||
use ext::expand;
|
||||
use ext::tt::macro_rules;
|
||||
use feature_gate::GatedCfg;
|
||||
use feature_gate::GatedCfgAttr;
|
||||
use parse;
|
||||
use parse::parser;
|
||||
use parse::token;
|
||||
@@ -350,6 +350,7 @@ impl DummyResult {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))),
|
||||
span: sp,
|
||||
attrs: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -572,7 +573,7 @@ pub struct ExtCtxt<'a> {
|
||||
pub backtrace: ExpnId,
|
||||
pub ecfg: expand::ExpansionConfig<'a>,
|
||||
pub crate_root: Option<&'static str>,
|
||||
pub feature_gated_cfgs: &'a mut Vec<GatedCfg>,
|
||||
pub feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>,
|
||||
|
||||
pub mod_path: Vec<ast::Ident> ,
|
||||
pub exported_macros: Vec<ast::MacroDef>,
|
||||
@@ -584,7 +585,7 @@ pub struct ExtCtxt<'a> {
|
||||
impl<'a> ExtCtxt<'a> {
|
||||
pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
|
||||
ecfg: expand::ExpansionConfig<'a>,
|
||||
feature_gated_cfgs: &'a mut Vec<GatedCfg>) -> ExtCtxt<'a> {
|
||||
feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>) -> ExtCtxt<'a> {
|
||||
let env = initial_syntax_expander_table(&ecfg);
|
||||
ExtCtxt {
|
||||
parse_sess: parse_sess,
|
||||
|
||||
@@ -525,6 +525,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
init: Some(ex),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: sp,
|
||||
attrs: None,
|
||||
});
|
||||
let decl = respan(sp, ast::DeclLocal(local));
|
||||
P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
|
||||
@@ -548,6 +549,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
init: Some(ex),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: sp,
|
||||
attrs: None,
|
||||
});
|
||||
let decl = respan(sp, ast::DeclLocal(local));
|
||||
P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
|
||||
@@ -584,6 +586,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: node,
|
||||
span: span,
|
||||
attrs: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ use ext::build::AstBuilder;
|
||||
use attr;
|
||||
use attr::*;
|
||||
use parse::token;
|
||||
use config::CfgDiagReal;
|
||||
|
||||
pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
|
||||
sp: Span,
|
||||
@@ -33,7 +34,12 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
|
||||
return DummyResult::expr(sp);
|
||||
}
|
||||
|
||||
let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &cfg,
|
||||
cx.feature_gated_cfgs);
|
||||
let matches_cfg = {
|
||||
let mut diag = CfgDiagReal {
|
||||
diag: &cx.parse_sess.span_diagnostic,
|
||||
feature_gated_cfgs: cx.feature_gated_cfgs,
|
||||
};
|
||||
attr::cfg_matches(&cx.cfg, &cfg, &mut diag)
|
||||
};
|
||||
MacEager::expr(cx.expr_bool(sp, matches_cfg))
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
|
||||
}
|
||||
),
|
||||
span: sp,
|
||||
attrs: None,
|
||||
});
|
||||
MacEager::expr(e)
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt,
|
||||
init: Some(expr),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: sp,
|
||||
attrs: None,
|
||||
});
|
||||
let decl = respan(sp, ast::DeclLocal(local));
|
||||
P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
|
||||
|
||||
@@ -204,7 +204,7 @@ use ext::build::AstBuilder;
|
||||
use codemap::{self, DUMMY_SP};
|
||||
use codemap::Span;
|
||||
use diagnostic::SpanHandler;
|
||||
use fold::MoveMap;
|
||||
use util::move_map::MoveMap;
|
||||
use owned_slice::OwnedSlice;
|
||||
use parse::token::{intern, InternedString};
|
||||
use parse::token::special_idents;
|
||||
|
||||
@@ -17,13 +17,14 @@ use ast;
|
||||
use ext::mtwt;
|
||||
use ext::build::AstBuilder;
|
||||
use attr;
|
||||
use attr::AttrMetaMethods;
|
||||
use attr::{AttrMetaMethods, WithAttrs};
|
||||
use codemap;
|
||||
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
||||
use ext::base::*;
|
||||
use feature_gate::{self, Features, GatedCfg};
|
||||
use feature_gate::{self, Features, GatedCfgAttr};
|
||||
use fold;
|
||||
use fold::*;
|
||||
use util::move_map::MoveMap;
|
||||
use parse;
|
||||
use parse::token::{fresh_mark, fresh_name, intern};
|
||||
use ptr::P;
|
||||
@@ -37,11 +38,15 @@ use std::collections::HashSet;
|
||||
|
||||
pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
let expr_span = e.span;
|
||||
return e.and_then(|ast::Expr {id, node, span}| match node {
|
||||
return e.and_then(|ast::Expr {id, node, span, attrs}| match node {
|
||||
|
||||
// expr_mac should really be expr_ext or something; it's the
|
||||
// entry-point for all syntax extensions.
|
||||
ast::ExprMac(mac) => {
|
||||
|
||||
// Assert that we drop any macro attributes on the floor here
|
||||
drop(attrs);
|
||||
|
||||
let expanded_expr = match expand_mac_invoc(mac, span,
|
||||
|r| r.make_expr(),
|
||||
mark_expr, fld) {
|
||||
@@ -60,6 +65,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: e.node,
|
||||
span: span,
|
||||
attrs: e.attrs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,12 +79,14 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
let placer = fld.fold_expr(placer);
|
||||
let value_expr = fld.fold_expr(value_expr);
|
||||
fld.cx.expr(span, ast::ExprInPlace(placer, value_expr))
|
||||
.with_attrs(fold_thin_attrs(attrs, fld))
|
||||
}
|
||||
|
||||
ast::ExprWhile(cond, body, opt_ident) => {
|
||||
let cond = fld.fold_expr(cond);
|
||||
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
|
||||
fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
|
||||
.with_attrs(fold_thin_attrs(attrs, fld))
|
||||
}
|
||||
|
||||
ast::ExprWhileLet(pat, expr, body, opt_ident) => {
|
||||
@@ -96,11 +104,13 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
assert!(rewritten_pats.len() == 1);
|
||||
|
||||
fld.cx.expr(span, ast::ExprWhileLet(rewritten_pats.remove(0), expr, body, opt_ident))
|
||||
.with_attrs(fold_thin_attrs(attrs, fld))
|
||||
}
|
||||
|
||||
ast::ExprLoop(loop_block, opt_ident) => {
|
||||
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
|
||||
fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
|
||||
.with_attrs(fold_thin_attrs(attrs, fld))
|
||||
}
|
||||
|
||||
ast::ExprForLoop(pat, head, body, opt_ident) => {
|
||||
@@ -118,6 +128,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
|
||||
let head = fld.fold_expr(head);
|
||||
fld.cx.expr(span, ast::ExprForLoop(rewritten_pats.remove(0), head, body, opt_ident))
|
||||
.with_attrs(fold_thin_attrs(attrs, fld))
|
||||
}
|
||||
|
||||
ast::ExprIfLet(pat, sub_expr, body, else_opt) => {
|
||||
@@ -136,6 +147,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
let else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
|
||||
let sub_expr = fld.fold_expr(sub_expr);
|
||||
fld.cx.expr(span, ast::ExprIfLet(rewritten_pats.remove(0), sub_expr, body, else_opt))
|
||||
.with_attrs(fold_thin_attrs(attrs, fld))
|
||||
}
|
||||
|
||||
ast::ExprClosure(capture_clause, fn_decl, block) => {
|
||||
@@ -144,14 +156,16 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
let new_node = ast::ExprClosure(capture_clause,
|
||||
rewritten_fn_decl,
|
||||
rewritten_block);
|
||||
P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)})
|
||||
P(ast::Expr{id:id, node: new_node, span: fld.new_span(span),
|
||||
attrs: fold_thin_attrs(attrs, fld)})
|
||||
}
|
||||
|
||||
_ => {
|
||||
P(noop_fold_expr(ast::Expr {
|
||||
id: id,
|
||||
node: node,
|
||||
span: span
|
||||
span: span,
|
||||
attrs: attrs
|
||||
}, fld))
|
||||
}
|
||||
});
|
||||
@@ -487,11 +501,14 @@ pub fn expand_item_mac(it: P<ast::Item>,
|
||||
/// Expand a stmt
|
||||
fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
|
||||
let stmt = stmt.and_then(|stmt| stmt);
|
||||
let (mac, style) = match stmt.node {
|
||||
StmtMac(mac, style) => (mac, style),
|
||||
let (mac, style, attrs) = match stmt.node {
|
||||
StmtMac(mac, style, attrs) => (mac, style, attrs),
|
||||
_ => return expand_non_macro_stmt(stmt, fld)
|
||||
};
|
||||
|
||||
// Assert that we drop any macro attributes on the floor here
|
||||
drop(attrs);
|
||||
|
||||
let maybe_new_items =
|
||||
expand_mac_invoc(mac.and_then(|m| m), stmt.span,
|
||||
|r| r.make_stmts(),
|
||||
@@ -539,7 +556,7 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE
|
||||
StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl {
|
||||
DeclLocal(local) => {
|
||||
// take it apart:
|
||||
let rewritten_local = local.map(|Local {id, pat, ty, init, span}| {
|
||||
let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
|
||||
// expand the ty since TyFixedLengthVec contains an Expr
|
||||
// and thus may have a macro use
|
||||
let expanded_ty = ty.map(|t| fld.fold_ty(t));
|
||||
@@ -569,7 +586,8 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE
|
||||
pat: rewritten_pat,
|
||||
// also, don't forget to expand the init:
|
||||
init: init.map(|e| fld.fold_expr(e)),
|
||||
span: span
|
||||
span: span,
|
||||
attrs: fold::fold_thin_attrs(attrs, fld),
|
||||
}
|
||||
});
|
||||
SmallVector::one(P(Spanned {
|
||||
@@ -1263,7 +1281,7 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
|
||||
// these are the macros being imported to this crate:
|
||||
imported_macros: Vec<ast::MacroDef>,
|
||||
user_exts: Vec<NamedSyntaxExtension>,
|
||||
feature_gated_cfgs: &mut Vec<GatedCfg>,
|
||||
feature_gated_cfgs: &mut Vec<GatedCfgAttr>,
|
||||
c: Crate) -> (Crate, HashSet<Name>) {
|
||||
let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg,
|
||||
feature_gated_cfgs);
|
||||
|
||||
@@ -242,6 +242,7 @@ pub mod rt {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ExprLit(P(self.clone())),
|
||||
span: DUMMY_SP,
|
||||
attrs: None,
|
||||
}).to_tokens(cx)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user