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:
bors
2015-12-04 08:46:29 +00:00
45 changed files with 2283 additions and 586 deletions

View File

@@ -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,
}))
}

View File

@@ -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,

View File

@@ -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,
})
}

View File

@@ -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))
}

View File

@@ -67,6 +67,7 @@ pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
}
),
span: sp,
attrs: None,
});
MacEager::expr(e)
}

View File

@@ -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)))

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)
}
}