1
Fork 0

Add 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.
This commit is contained in:
Marvin Löbel 2015-11-03 17:39:51 +01:00
parent 6ef02eff89
commit 2a8f358de7
34 changed files with 1602 additions and 416 deletions

View file

@ -41,7 +41,7 @@ use syntax::ast_util::{self, IdVisitingOperation};
use syntax::attr::{self, AttrMetaMethods}; use syntax::attr::{self, AttrMetaMethods};
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::parse::token::InternedString; use syntax::parse::token::InternedString;
use syntax::ast; use syntax::ast::{self, ThinAttributesExt};
use rustc_front::hir; use rustc_front::hir;
use rustc_front::util; use rustc_front::util;
use rustc_front::intravisit as hir_visit; use rustc_front::intravisit as hir_visit;
@ -674,11 +674,18 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
} }
fn visit_expr(&mut self, e: &hir::Expr) { fn visit_expr(&mut self, e: &hir::Expr) {
run_lints!(self, check_expr, late_passes, e); self.with_lint_attrs(e.attrs.as_attrs(), |cx| {
hir_visit::walk_expr(self, e); run_lints!(cx, check_expr, late_passes, e);
hir_visit::walk_expr(cx, e);
})
} }
fn visit_stmt(&mut self, s: &hir::Stmt) { fn visit_stmt(&mut self, s: &hir::Stmt) {
// statement attributes are actually just attributes on one of
// - item
// - local
// - expression
// so we keep track of lint levels there
run_lints!(self, check_stmt, late_passes, s); run_lints!(self, check_stmt, late_passes, s);
hir_visit::walk_stmt(self, s); hir_visit::walk_stmt(self, s);
} }
@ -730,8 +737,10 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
} }
fn visit_local(&mut self, l: &hir::Local) { fn visit_local(&mut self, l: &hir::Local) {
run_lints!(self, check_local, late_passes, l); self.with_lint_attrs(l.attrs.as_attrs(), |cx| {
hir_visit::walk_local(self, l); run_lints!(cx, check_local, late_passes, l);
hir_visit::walk_local(cx, l);
})
} }
fn visit_block(&mut self, b: &hir::Block) { fn visit_block(&mut self, b: &hir::Block) {

View file

@ -409,7 +409,8 @@ fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
P(hir::Expr { P(hir::Expr {
id: 0, id: 0,
node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })), node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
span: DUMMY_SP span: DUMMY_SP,
attrs: None,
}) })
} }

View file

@ -654,6 +654,7 @@ impl fold::Folder for ReplaceBodyWithLoop {
node: ast::ExprLoop(empty_block, None), node: ast::ExprLoop(empty_block, None),
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
span: codemap::DUMMY_SP, span: codemap::DUMMY_SP,
attrs: None,
}); });
expr_to_block(b.rules, Some(loop_expr)) expr_to_block(b.rules, Some(loop_expr))

View file

@ -13,7 +13,7 @@
use hir::*; use hir::*;
use syntax::ast::{Ident, Name, NodeId, DUMMY_NODE_ID, Attribute, Attribute_, MetaItem}; use syntax::ast::{Ident, Name, NodeId, DUMMY_NODE_ID, Attribute, Attribute_, MetaItem};
use syntax::ast::{MetaWord, MetaList, MetaNameValue}; use syntax::ast::{MetaWord, MetaList, MetaNameValue, ThinAttributesExt};
use hir; use hir;
use syntax::codemap::{respan, Span, Spanned}; use syntax::codemap::{respan, Span, Spanned};
use syntax::owned_slice::OwnedSlice; use syntax::owned_slice::OwnedSlice;
@ -501,13 +501,14 @@ pub fn noop_fold_parenthesized_parameter_data<T: Folder>(data: ParenthesizedPara
} }
pub fn noop_fold_local<T: Folder>(l: P<Local>, fld: &mut T) -> P<Local> { pub fn noop_fold_local<T: Folder>(l: P<Local>, fld: &mut T) -> P<Local> {
l.map(|Local { id, pat, ty, init, span }| { l.map(|Local { id, pat, ty, init, span, attrs }| {
Local { Local {
id: fld.new_id(id), id: fld.new_id(id),
ty: ty.map(|t| fld.fold_ty(t)), ty: ty.map(|t| fld.fold_ty(t)),
pat: fld.fold_pat(pat), pat: fld.fold_pat(pat),
init: init.map(|e| fld.fold_expr(e)), init: init.map(|e| fld.fold_expr(e)),
span: fld.new_span(span), span: fld.new_span(span),
attrs: attrs.map_opt_attrs(|attrs| fold_attrs(attrs, fld)),
} }
}) })
} }
@ -1048,7 +1049,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
}) })
} }
pub fn noop_fold_expr<T: Folder>(Expr { id, node, span }: Expr, folder: &mut T) -> Expr { pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &mut T) -> Expr {
Expr { Expr {
id: folder.new_id(id), id: folder.new_id(id),
node: match node { node: match node {
@ -1171,6 +1172,7 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span }: Expr, folder: &mut T)
} }
}, },
span: folder.new_span(span), span: folder.new_span(span),
attrs: attrs.map_opt_attrs(|attrs| fold_attrs(attrs, folder)),
} }
} }

View file

@ -41,6 +41,7 @@ use syntax::codemap::{self, Span, Spanned, DUMMY_SP, ExpnId};
use syntax::abi::Abi; use syntax::abi::Abi;
use syntax::ast::{Name, Ident, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect}; use syntax::ast::{Name, Ident, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect};
use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, CrateConfig}; use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, CrateConfig};
use syntax::ast::ThinAttributes;
use syntax::owned_slice::OwnedSlice; use syntax::owned_slice::OwnedSlice;
use syntax::parse::token::InternedString; use syntax::parse::token::InternedString;
use syntax::ptr::P; use syntax::ptr::P;
@ -558,6 +559,7 @@ pub struct Local {
pub init: Option<P<Expr>>, pub init: Option<P<Expr>>,
pub id: NodeId, pub id: NodeId,
pub span: Span, pub span: Span,
pub attrs: ThinAttributes,
} }
pub type Decl = Spanned<Decl_>; pub type Decl = Spanned<Decl_>;
@ -609,6 +611,7 @@ pub struct Expr {
pub id: NodeId, pub id: NodeId,
pub node: Expr_, pub node: Expr_,
pub span: Span, pub span: Span,
pub attrs: ThinAttributes,
} }
impl fmt::Debug for Expr { impl fmt::Debug for Expr {

View file

@ -331,6 +331,7 @@ pub fn lower_local(lctx: &LoweringContext, l: &Local) -> P<hir::Local> {
pat: lower_pat(lctx, &l.pat), pat: lower_pat(lctx, &l.pat),
init: l.init.as_ref().map(|e| lower_expr(lctx, e)), init: l.init.as_ref().map(|e| lower_expr(lctx, e)),
span: l.span, span: l.span,
attrs: l.attrs.clone(),
}) })
} }
@ -1215,7 +1216,14 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
maybe_expr.as_ref().map(|x| lower_expr(lctx, x))) maybe_expr.as_ref().map(|x| lower_expr(lctx, x)))
} }
ExprParen(ref ex) => { ExprParen(ref ex) => {
return lower_expr(lctx, ex); // merge attributes into the inner expression.
return lower_expr(lctx, ex).map(|mut ex| {
ex.attrs.update(|attrs| {
// FIXME: Badly named
attrs.prepend_outer(e.attrs.clone())
});
ex
});
} }
// Desugar ExprIfLet // Desugar ExprIfLet
@ -1454,6 +1462,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
ExprMac(_) => panic!("Shouldn't exist here"), ExprMac(_) => panic!("Shouldn't exist here"),
}, },
span: e.span, span: e.span,
attrs: e.attrs.clone(),
}) })
} }
@ -1552,52 +1561,62 @@ fn arm(pats: Vec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
} }
} }
fn expr_break(lctx: &LoweringContext, span: Span) -> P<hir::Expr> { fn expr_break(lctx: &LoweringContext, span: Span,
expr(lctx, span, hir::ExprBreak(None)) attrs: ThinAttributes) -> P<hir::Expr> {
expr(lctx, span, hir::ExprBreak(None), attrs)
} }
fn expr_call(lctx: &LoweringContext, fn expr_call(lctx: &LoweringContext,
span: Span, span: Span,
e: P<hir::Expr>, e: P<hir::Expr>,
args: Vec<P<hir::Expr>>) args: Vec<P<hir::Expr>>,
attrs: ThinAttributes)
-> P<hir::Expr> { -> P<hir::Expr> {
expr(lctx, span, hir::ExprCall(e, args)) expr(lctx, span, hir::ExprCall(e, args), attrs)
} }
fn expr_ident(lctx: &LoweringContext, span: Span, id: Ident) -> P<hir::Expr> { fn expr_ident(lctx: &LoweringContext, span: Span, id: Ident,
expr_path(lctx, path_ident(span, id)) attrs: ThinAttributes) -> P<hir::Expr> {
expr_path(lctx, path_ident(span, id), attrs)
} }
fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P<hir::Expr>) -> P<hir::Expr> { fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P<hir::Expr>,
expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e)) attrs: ThinAttributes) -> P<hir::Expr> {
expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e), attrs)
} }
fn expr_path(lctx: &LoweringContext, path: hir::Path) -> P<hir::Expr> { fn expr_path(lctx: &LoweringContext, path: hir::Path,
expr(lctx, path.span, hir::ExprPath(None, path)) attrs: ThinAttributes) -> P<hir::Expr> {
expr(lctx, path.span, hir::ExprPath(None, path), attrs)
} }
fn expr_match(lctx: &LoweringContext, fn expr_match(lctx: &LoweringContext,
span: Span, span: Span,
arg: P<hir::Expr>, arg: P<hir::Expr>,
arms: Vec<hir::Arm>, arms: Vec<hir::Arm>,
source: hir::MatchSource) source: hir::MatchSource,
attrs: ThinAttributes)
-> P<hir::Expr> { -> P<hir::Expr> {
expr(lctx, span, hir::ExprMatch(arg, arms, source)) expr(lctx, span, hir::ExprMatch(arg, arms, source), attrs)
} }
fn expr_block(lctx: &LoweringContext, b: P<hir::Block>) -> P<hir::Expr> { fn expr_block(lctx: &LoweringContext, b: P<hir::Block>,
expr(lctx, b.span, hir::ExprBlock(b)) attrs: ThinAttributes) -> P<hir::Expr> {
expr(lctx, b.span, hir::ExprBlock(b), attrs)
} }
fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>) -> P<hir::Expr> { fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>,
expr(lctx, sp, hir::ExprTup(exprs)) attrs: ThinAttributes) -> P<hir::Expr> {
expr(lctx, sp, hir::ExprTup(exprs), attrs)
} }
fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_) -> P<hir::Expr> { fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_,
attrs: ThinAttributes) -> P<hir::Expr> {
P(hir::Expr { P(hir::Expr {
id: lctx.next_id(), id: lctx.next_id(),
node: node, node: node,
span: span, span: span,
attrs: attrs,
}) })
} }
@ -1605,7 +1624,8 @@ fn stmt_let(lctx: &LoweringContext,
sp: Span, sp: Span,
mutbl: bool, mutbl: bool,
ident: Ident, ident: Ident,
ex: P<hir::Expr>) ex: P<hir::Expr>,
attrs: ThinAttributes)
-> P<hir::Stmt> { -> P<hir::Stmt> {
let pat = if mutbl { let pat = if mutbl {
pat_ident_binding_mode(lctx, sp, ident, hir::BindByValue(hir::MutMutable)) pat_ident_binding_mode(lctx, sp, ident, hir::BindByValue(hir::MutMutable))
@ -1618,6 +1638,7 @@ fn stmt_let(lctx: &LoweringContext,
init: Some(ex), init: Some(ex),
id: lctx.next_id(), id: lctx.next_id(),
span: sp, span: sp,
attrs: attrs,
}); });
let decl = respan(sp, hir::DeclLocal(local)); let decl = respan(sp, hir::DeclLocal(local));
P(respan(sp, hir::StmtDecl(P(decl), lctx.next_id()))) P(respan(sp, hir::StmtDecl(P(decl), lctx.next_id())))
@ -1755,7 +1776,8 @@ fn signal_block_expr(lctx: &LoweringContext,
stmts: Vec<P<hir::Stmt>>, stmts: Vec<P<hir::Stmt>>,
expr: P<hir::Expr>, expr: P<hir::Expr>,
span: Span, span: Span,
rule: hir::BlockCheckMode) rule: hir::BlockCheckMode,
attrs: ThinAttributes)
-> P<hir::Expr> { -> P<hir::Expr> {
let id = lctx.next_id(); let id = lctx.next_id();
expr_block(lctx, expr_block(lctx,
@ -1765,7 +1787,8 @@ fn signal_block_expr(lctx: &LoweringContext,
id: id, id: id,
stmts: stmts, stmts: stmts,
expr: Some(expr), expr: Some(expr),
})) }),
attrs)
} }

View file

@ -138,7 +138,7 @@ impl LateLintPass for NonCamelCaseTypes {
declare_lint! { declare_lint! {
pub NON_SNAKE_CASE, pub NON_SNAKE_CASE,
Warn, Warn,
"methods, functions, lifetime parameters and modules should have snake case names" "variables, methods, functions, lifetime parameters and modules should have snake case names"
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]

View file

@ -692,8 +692,21 @@ pub enum Stmt_ {
/// Expr with trailing semi-colon (may have any type): /// Expr with trailing semi-colon (may have any type):
StmtSemi(P<Expr>, NodeId), StmtSemi(P<Expr>, NodeId),
StmtMac(P<Mac>, MacStmtStyle), StmtMac(P<Mac>, MacStmtStyle, ThinAttributes),
} }
impl Stmt_ {
pub fn attrs(&self) -> &[Attribute] {
match *self {
StmtDecl(ref d, _) => d.attrs(),
StmtExpr(ref e, _) |
StmtSemi(ref e, _) => e.attrs(),
StmtMac(_, _, Some(ref b)) => b,
StmtMac(_, _, None) => &[],
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum MacStmtStyle { pub enum MacStmtStyle {
/// The macro statement had a trailing semicolon, e.g. `foo! { ... };` /// The macro statement had a trailing semicolon, e.g. `foo! { ... };`
@ -718,6 +731,16 @@ pub struct Local {
pub init: Option<P<Expr>>, pub init: Option<P<Expr>>,
pub id: NodeId, pub id: NodeId,
pub span: Span, pub span: Span,
pub attrs: ThinAttributes,
}
impl Local {
pub fn attrs(&self) -> &[Attribute] {
match self.attrs {
Some(ref b) => b,
None => &[],
}
}
} }
pub type Decl = Spanned<Decl_>; pub type Decl = Spanned<Decl_>;
@ -730,6 +753,15 @@ pub enum Decl_ {
DeclItem(P<Item>), DeclItem(P<Item>),
} }
impl Decl {
pub fn attrs(&self) -> &[Attribute] {
match self.node {
DeclLocal(ref l) => l.attrs(),
DeclItem(ref i) => i.attrs(),
}
}
}
/// represents one arm of a 'match' /// represents one arm of a 'match'
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct Arm { pub struct Arm {
@ -766,6 +798,16 @@ pub struct Expr {
pub id: NodeId, pub id: NodeId,
pub node: Expr_, pub node: Expr_,
pub span: Span, pub span: Span,
pub attrs: ThinAttributes
}
impl Expr {
pub fn attrs(&self) -> &[Attribute] {
match self.attrs {
Some(ref b) => b,
None => &[],
}
}
} }
impl fmt::Debug for Expr { impl fmt::Debug for Expr {
@ -1792,6 +1834,12 @@ pub struct Item {
pub span: Span, pub span: Span,
} }
impl Item {
pub fn attrs(&self) -> &[Attribute] {
&self.attrs
}
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum Item_ { pub enum Item_ {
/// An`extern crate` item, with optional original crate name, /// An`extern crate` item, with optional original crate name,
@ -1904,6 +1952,98 @@ pub struct MacroDef {
pub body: Vec<TokenTree>, pub body: Vec<TokenTree>,
} }
/// A list of attributes, behind a optional box as
/// a space optimization.
pub type ThinAttributes = Option<Box<Vec<Attribute>>>;
pub trait ThinAttributesExt {
fn map_opt_attrs<F>(self, f: F) -> Self
where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>;
fn prepend_outer(mut self, attrs: Self) -> Self;
fn append_inner(mut self, attrs: Self) -> Self;
fn update<F>(&mut self, f: F)
where Self: Sized,
F: FnOnce(Self) -> Self;
fn as_attrs(&self) -> &[Attribute];
fn into_attrs(self) -> Vec<Attribute>;
}
// FIXME: Rename inner/outer
// FIXME: Rename opt_attrs
impl ThinAttributesExt for ThinAttributes {
fn map_opt_attrs<F>(self, f: F) -> Self
where F: FnOnce(Vec<Attribute>) -> Vec<Attribute> {
// This is kinda complicated... Ensure the function is
// always called, and that None inputs or results are
// correctly handled.
if let Some(mut b) = self {
use std::mem::replace;
let vec = replace(&mut *b, Vec::new());
let vec = f(vec);
if vec.len() == 0 {
None
} else {
replace(&mut*b, vec);
Some(b)
}
} else {
f(Vec::new()).into_opt_attrs()
}
}
fn prepend_outer(self, attrs: ThinAttributes) -> Self {
attrs.map_opt_attrs(|mut attrs| {
attrs.extend(self.into_attrs());
attrs
})
}
fn append_inner(self, attrs: ThinAttributes) -> Self {
self.map_opt_attrs(|mut self_| {
self_.extend(attrs.into_attrs());
self_
})
}
fn update<F>(&mut self, f: F)
where Self: Sized,
F: FnOnce(ThinAttributes) -> ThinAttributes
{
let self_ = f(self.take());
*self = self_;
}
fn as_attrs(&self) -> &[Attribute] {
match *self {
Some(ref b) => b,
None => &[],
}
}
fn into_attrs(self) -> Vec<Attribute> {
match self {
Some(b) => *b,
None => Vec::new(),
}
}
}
pub trait AttributesExt {
fn into_opt_attrs(self) -> ThinAttributes;
}
impl AttributesExt for Vec<Attribute> {
fn into_opt_attrs(self) -> ThinAttributes {
if self.len() == 0 {
None
} else {
Some(Box::new(self))
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serialize; use serialize;

View file

@ -16,6 +16,8 @@ pub use self::IntType::*;
use ast; use ast;
use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList}; use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
use ast::{Stmt, StmtDecl, StmtExpr, StmtMac, StmtSemi, DeclItem, DeclLocal, ThinAttributes};
use ast::{Expr, ThinAttributesExt, Item, Local, Decl};
use codemap::{Span, Spanned, spanned, dummy_spanned}; use codemap::{Span, Spanned, spanned, dummy_spanned};
use codemap::BytePos; use codemap::BytePos;
use diagnostic::SpanHandler; use diagnostic::SpanHandler;
@ -720,3 +722,83 @@ impl IntType {
} }
} }
} }
/// A cheap way to add Attributes to an AST node.
pub trait WithAttrs {
// FIXME: Could be extended to anything IntoIter<Item=Attribute>
fn with_attrs(self, attrs: ThinAttributes) -> Self;
}
impl WithAttrs for P<Expr> {
fn with_attrs(self, attrs: ThinAttributes) -> Self {
self.map(|mut e| {
e.attrs.update(|a| a.append_inner(attrs));
e
})
}
}
impl WithAttrs for P<Item> {
fn with_attrs(self, attrs: ThinAttributes) -> Self {
self.map(|Item { ident, attrs: mut ats, id, node, vis, span }| {
ats.extend(attrs.into_attrs());
Item {
ident: ident,
attrs: ats,
id: id,
node: node,
vis: vis,
span: span,
}
})
}
}
impl WithAttrs for P<Local> {
fn with_attrs(self, attrs: ThinAttributes) -> Self {
self.map(|Local { pat, ty, init, id, span, attrs: mut ats }| {
ats.update(|a| a.append_inner(attrs));
Local {
pat: pat,
ty: ty,
init: init,
id: id,
span: span,
attrs: ats,
}
})
}
}
impl WithAttrs for P<Decl> {
fn with_attrs(self, attrs: ThinAttributes) -> Self {
self.map(|Spanned { span, node }| {
Spanned {
span: span,
node: match node {
DeclLocal(local) => DeclLocal(local.with_attrs(attrs)),
DeclItem(item) => DeclItem(item.with_attrs(attrs)),
}
}
})
}
}
impl WithAttrs for P<Stmt> {
fn with_attrs(self, attrs: ThinAttributes) -> Self {
self.map(|Spanned { span, node }| {
Spanned {
span: span,
node: match node {
StmtDecl(decl, id) => StmtDecl(decl.with_attrs(attrs), id),
StmtExpr(expr, id) => StmtExpr(expr.with_attrs(attrs), id),
StmtSemi(expr, id) => StmtSemi(expr.with_attrs(attrs), id),
StmtMac(mac, style, mut ats) => {
ats.update(|a| a.append_inner(attrs));
StmtMac(mac, style, ats)
}
},
}
})
}
}

View file

@ -20,8 +20,9 @@ use util::small_vector::SmallVector;
/// A folder that strips out items that do not belong in the current /// A folder that strips out items that do not belong in the current
/// configuration. /// configuration.
struct Context<F> where F: FnMut(&[ast::Attribute]) -> bool { struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
in_cfg: F, in_cfg: F,
diagnostic: &'a SpanHandler,
} }
// Support conditional compilation by transforming the AST, stripping out // Support conditional compilation by transforming the AST, stripping out
@ -32,16 +33,15 @@ pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate,
{ {
let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs); let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs);
let config = krate.config.clone(); let config = krate.config.clone();
strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs)) strip_items(diagnostic,
krate,
|attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs))
} }
impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool { impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod { fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod {
fold_mod(self, module) fold_mod(self, module)
} }
fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
fold_block(self, block)
}
fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
fold_foreign_mod(self, foreign_mod) fold_foreign_mod(self, foreign_mod)
} }
@ -49,8 +49,25 @@ impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
fold_item_underscore(self, item) fold_item_underscore(self, item)
} }
fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> { fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
// If an expr is valid to cfg away it will have been removed by the
// outer stmt or expression folder before descending in here.
// Anything else is always required, and thus has to error out
// in case of a cfg attr.
//
// NB: This intentionally not part of the fold_expr() function
// in order for fold_opt_expr() to be able to avoid this check
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
self.diagnostic.span_err(attr.span,
"removing an expression is not supported in this position");
}
fold_expr(self, expr) fold_expr(self, expr)
} }
fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
fold_opt_expr(self, expr)
}
fn fold_stmt(&mut self, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>> {
fold_stmt(self, stmt)
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self) fold::noop_fold_mac(mac, self)
} }
@ -59,11 +76,13 @@ impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
} }
} }
pub fn strip_items<F>(krate: ast::Crate, in_cfg: F) -> ast::Crate where pub fn strip_items<'a, F>(diagnostic: &'a SpanHandler,
krate: ast::Crate, in_cfg: F) -> ast::Crate where
F: FnMut(&[ast::Attribute]) -> bool, F: FnMut(&[ast::Attribute]) -> bool,
{ {
let mut ctxt = Context { let mut ctxt = Context {
in_cfg: in_cfg, in_cfg: in_cfg,
diagnostic: diagnostic,
}; };
ctxt.fold_crate(krate) ctxt.fold_crate(krate)
} }
@ -182,45 +201,20 @@ fn fold_struct<F>(cx: &mut Context<F>, vdata: ast::VariantData) -> ast::VariantD
} }
} }
fn retain_stmt<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where fn fold_opt_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> Option<P<ast::Expr>>
F: FnMut(&[ast::Attribute]) -> bool where F: FnMut(&[ast::Attribute]) -> bool
{ {
match stmt.node { if expr_in_cfg(cx, &expr) {
ast::StmtDecl(ref decl, _) => { Some(fold_expr(cx, expr))
match decl.node { } else {
ast::DeclItem(ref item) => { None
item_in_cfg(cx, item)
} }
_ => true
}
}
_ => true
}
}
fn fold_block<F>(cx: &mut Context<F>, b: P<ast::Block>) -> P<ast::Block> where
F: FnMut(&[ast::Attribute]) -> bool
{
b.map(|ast::Block {id, stmts, expr, rules, span}| {
let resulting_stmts: Vec<P<ast::Stmt>> =
stmts.into_iter().filter(|a| retain_stmt(cx, a)).collect();
let resulting_stmts = resulting_stmts.into_iter()
.flat_map(|stmt| cx.fold_stmt(stmt).into_iter())
.collect();
ast::Block {
id: id,
stmts: resulting_stmts,
expr: expr.map(|x| cx.fold_expr(x)),
rules: rules,
span: span,
}
})
} }
fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
F: FnMut(&[ast::Attribute]) -> bool F: FnMut(&[ast::Attribute]) -> bool
{ {
expr.map(|ast::Expr {id, span, node}| { expr.map(|ast::Expr {id, span, node, attrs}| {
fold::noop_fold_expr(ast::Expr { fold::noop_fold_expr(ast::Expr {
id: id, id: id,
node: match node { node: match node {
@ -231,11 +225,34 @@ fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
} }
_ => node _ => node
}, },
span: span span: span,
attrs: attrs,
}, cx) }, cx)
}) })
} }
fn fold_stmt<F>(cx: &mut Context<F>, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>>
where F: FnMut(&[ast::Attribute]) -> bool
{
if stmt_in_cfg(cx, &stmt) {
stmt.and_then(|s| fold::noop_fold_stmt(s, cx))
} else {
SmallVector::zero()
}
}
fn stmt_in_cfg<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where
F: FnMut(&[ast::Attribute]) -> bool
{
(cx.in_cfg)(stmt.node.attrs())
}
fn expr_in_cfg<F>(cx: &mut Context<F>, expr: &ast::Expr) -> bool where
F: FnMut(&[ast::Attribute]) -> bool
{
(cx.in_cfg)(expr.attrs())
}
fn item_in_cfg<F>(cx: &mut Context<F>, item: &ast::Item) -> bool where fn item_in_cfg<F>(cx: &mut Context<F>, item: &ast::Item) -> bool where
F: FnMut(&[ast::Attribute]) -> bool F: FnMut(&[ast::Attribute]) -> bool
{ {
@ -248,13 +265,19 @@ fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> bool
return (cx.in_cfg)(&item.attrs); return (cx.in_cfg)(&item.attrs);
} }
fn is_cfg(attr: &ast::Attribute) -> bool {
attr.check_name("cfg")
}
// Determine if an item should be translated in the current crate // Determine if an item should be translated in the current crate
// configuration based on the item's attributes // configuration based on the item's attributes
fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute], fn in_cfg(diagnostic: &SpanHandler,
cfg: &[P<ast::MetaItem>],
attrs: &[ast::Attribute],
feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool { feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
attrs.iter().all(|attr| { attrs.iter().all(|attr| {
let mis = match attr.node.value.node { let mis = match attr.node.value.node {
ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis, ast::MetaList(_, ref mis) if is_cfg(&attr) => mis,
_ => return true _ => return true
}; };

View file

@ -233,6 +233,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
dialect: dialect, dialect: dialect,
expn_id: expn_id, expn_id: expn_id,
}), }),
span: sp span: sp,
attrs: None,
})) }))
} }

View file

@ -349,6 +349,7 @@ impl DummyResult {
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))), node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))),
span: sp, span: sp,
attrs: None,
}) })
} }

View file

@ -525,6 +525,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
init: Some(ex), init: Some(ex),
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
span: sp, span: sp,
attrs: None,
}); });
let decl = respan(sp, ast::DeclLocal(local)); let decl = respan(sp, ast::DeclLocal(local));
P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
@ -548,6 +549,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
init: Some(ex), init: Some(ex),
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
span: sp, span: sp,
attrs: None,
}); });
let decl = respan(sp, ast::DeclLocal(local)); let decl = respan(sp, ast::DeclLocal(local));
P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) 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, id: ast::DUMMY_NODE_ID,
node: node, node: node,
span: span, span: span,
attrs: None,
}) })
} }

View file

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

View file

@ -148,6 +148,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt,
init: Some(expr), init: Some(expr),
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
span: sp, span: sp,
attrs: None,
}); });
let decl = respan(sp, ast::DeclLocal(local)); let decl = respan(sp, ast::DeclLocal(local));
P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))

View file

@ -17,7 +17,7 @@ use ast;
use ext::mtwt; use ext::mtwt;
use ext::build::AstBuilder; use ext::build::AstBuilder;
use attr; use attr;
use attr::AttrMetaMethods; use attr::{AttrMetaMethods, WithAttrs};
use codemap; use codemap;
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
use ext::base::*; use ext::base::*;
@ -37,11 +37,16 @@ use std::collections::HashSet;
pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> { pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
let expr_span = e.span; let expr_span = e.span;
return e.and_then(|ast::Expr {id, node, span}| match node { // FIXME: Drop attrs on the floor for now.
return e.and_then(|ast::Expr {id, node, span, attrs}| match node {
// expr_mac should really be expr_ext or something; it's the // expr_mac should really be expr_ext or something; it's the
// entry-point for all syntax extensions. // entry-point for all syntax extensions.
ast::ExprMac(mac) => { ast::ExprMac(mac) => {
// drop attributes on the macro itself
let _ = attrs;
let expanded_expr = match expand_mac_invoc(mac, span, let expanded_expr = match expand_mac_invoc(mac, span,
|r| r.make_expr(), |r| r.make_expr(),
mark_expr, fld) { 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, id: ast::DUMMY_NODE_ID,
node: e.node, node: e.node,
span: span, 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 placer = fld.fold_expr(placer);
let value_expr = fld.fold_expr(value_expr); let value_expr = fld.fold_expr(value_expr);
fld.cx.expr(span, ast::ExprInPlace(placer, value_expr)) fld.cx.expr(span, ast::ExprInPlace(placer, value_expr))
.with_attrs(attrs)
} }
ast::ExprWhile(cond, body, opt_ident) => { ast::ExprWhile(cond, body, opt_ident) => {
let cond = fld.fold_expr(cond); let cond = fld.fold_expr(cond);
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld); let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident)) fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
.with_attrs(attrs)
} }
ast::ExprWhileLet(pat, expr, body, opt_ident) => { 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); assert!(rewritten_pats.len() == 1);
fld.cx.expr(span, ast::ExprWhileLet(rewritten_pats.remove(0), expr, body, opt_ident)) fld.cx.expr(span, ast::ExprWhileLet(rewritten_pats.remove(0), expr, body, opt_ident))
.with_attrs(attrs)
} }
ast::ExprLoop(loop_block, opt_ident) => { ast::ExprLoop(loop_block, opt_ident) => {
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)) fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
.with_attrs(attrs)
} }
ast::ExprForLoop(pat, head, body, opt_ident) => { 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); let head = fld.fold_expr(head);
fld.cx.expr(span, ast::ExprForLoop(rewritten_pats.remove(0), head, body, opt_ident)) fld.cx.expr(span, ast::ExprForLoop(rewritten_pats.remove(0), head, body, opt_ident))
.with_attrs(attrs)
} }
ast::ExprIfLet(pat, sub_expr, body, else_opt) => { 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 else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
let sub_expr = fld.fold_expr(sub_expr); let sub_expr = fld.fold_expr(sub_expr);
fld.cx.expr(span, ast::ExprIfLet(rewritten_pats.remove(0), sub_expr, body, else_opt)) fld.cx.expr(span, ast::ExprIfLet(rewritten_pats.remove(0), sub_expr, body, else_opt))
.with_attrs(attrs)
} }
ast::ExprClosure(capture_clause, fn_decl, block) => { ast::ExprClosure(capture_clause, fn_decl, block) => {
@ -144,15 +156,18 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
let new_node = ast::ExprClosure(capture_clause, let new_node = ast::ExprClosure(capture_clause,
rewritten_fn_decl, rewritten_fn_decl,
rewritten_block); 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: None})
.with_attrs(attrs)
} }
_ => { _ => {
P(noop_fold_expr(ast::Expr { P(noop_fold_expr(ast::Expr {
id: id, id: id,
node: node, node: node,
span: span span: span,
}, fld)) attrs: None
}, fld)).with_attrs(attrs)
} }
}); });
} }
@ -486,11 +501,14 @@ pub fn expand_item_mac(it: P<ast::Item>,
/// Expand a stmt /// Expand a stmt
fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> { fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
let stmt = stmt.and_then(|stmt| stmt); let stmt = stmt.and_then(|stmt| stmt);
let (mac, style) = match stmt.node { let (mac, style, attrs) = match stmt.node {
StmtMac(mac, style) => (mac, style), StmtMac(mac, style, attrs) => (mac, style, attrs),
_ => return expand_non_macro_stmt(stmt, fld) _ => return expand_non_macro_stmt(stmt, fld)
}; };
// FIXME: drop attrs for macros.
let _ = attrs;
let maybe_new_items = let maybe_new_items =
expand_mac_invoc(mac.and_then(|m| m), stmt.span, expand_mac_invoc(mac.and_then(|m| m), stmt.span,
|r| r.make_stmts(), |r| r.make_stmts(),
@ -538,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 { StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl {
DeclLocal(local) => { DeclLocal(local) => {
// take it apart: // 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 // expand the ty since TyFixedLengthVec contains an Expr
// and thus may have a macro use // and thus may have a macro use
let expanded_ty = ty.map(|t| fld.fold_ty(t)); let expanded_ty = ty.map(|t| fld.fold_ty(t));
@ -568,7 +586,8 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE
pat: rewritten_pat, pat: rewritten_pat,
// also, don't forget to expand the init: // also, don't forget to expand the init:
init: init.map(|e| fld.fold_expr(e)), init: init.map(|e| fld.fold_expr(e)),
span: span span: span,
attrs: attrs
} }
}); });
SmallVector::one(P(Spanned { SmallVector::one(P(Spanned {

View file

@ -242,6 +242,7 @@ pub mod rt {
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
node: ast::ExprLit(P(self.clone())), node: ast::ExprLit(P(self.clone())),
span: DUMMY_SP, span: DUMMY_SP,
attrs: None,
}).to_tokens(cx) }).to_tokens(cx)
} }
} }

View file

@ -134,6 +134,14 @@ pub trait Folder : Sized {
e.map(|e| noop_fold_expr(e, self)) e.map(|e| noop_fold_expr(e, self))
} }
fn fold_opt_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
noop_fold_opt_expr(e, self)
}
fn fold_exprs(&mut self, es: Vec<P<Expr>>) -> Vec<P<Expr>> {
noop_fold_exprs(es, self)
}
fn fold_ty(&mut self, t: P<Ty>) -> P<Ty> { fn fold_ty(&mut self, t: P<Ty>) -> P<Ty> {
noop_fold_ty(t, self) noop_fold_ty(t, self)
} }
@ -508,12 +516,13 @@ pub fn noop_fold_parenthesized_parameter_data<T: Folder>(data: ParenthesizedPara
} }
pub fn noop_fold_local<T: Folder>(l: P<Local>, fld: &mut T) -> P<Local> { pub fn noop_fold_local<T: Folder>(l: P<Local>, fld: &mut T) -> P<Local> {
l.map(|Local {id, pat, ty, init, span}| Local { l.map(|Local {id, pat, ty, init, span, attrs}| Local {
id: fld.new_id(id), id: fld.new_id(id),
ty: ty.map(|t| fld.fold_ty(t)), ty: ty.map(|t| fld.fold_ty(t)),
pat: fld.fold_pat(pat), pat: fld.fold_pat(pat),
init: init.map(|e| fld.fold_expr(e)), init: init.map(|e| fld.fold_expr(e)),
span: fld.new_span(span) span: fld.new_span(span),
attrs: attrs.map_opt_attrs(|v| fold_attrs(v, fld)),
}) })
} }
@ -891,7 +900,7 @@ pub fn noop_fold_block<T: Folder>(b: P<Block>, folder: &mut T) -> P<Block> {
b.map(|Block {id, stmts, expr, rules, span}| Block { b.map(|Block {id, stmts, expr, rules, span}| Block {
id: folder.new_id(id), id: folder.new_id(id),
stmts: stmts.into_iter().flat_map(|s| folder.fold_stmt(s).into_iter()).collect(), stmts: stmts.into_iter().flat_map(|s| folder.fold_stmt(s).into_iter()).collect(),
expr: expr.map(|x| folder.fold_expr(x)), expr: expr.and_then(|x| folder.fold_opt_expr(x)),
rules: rules, rules: rules,
span: folder.new_span(span), span: folder.new_span(span),
}) })
@ -1171,7 +1180,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
}) })
} }
pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) -> Expr { pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mut T) -> Expr {
Expr { Expr {
id: folder.new_id(id), id: folder.new_id(id),
node: match node { node: match node {
@ -1182,21 +1191,21 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
ExprInPlace(folder.fold_expr(p), folder.fold_expr(e)) ExprInPlace(folder.fold_expr(p), folder.fold_expr(e))
} }
ExprVec(exprs) => { ExprVec(exprs) => {
ExprVec(exprs.move_map(|x| folder.fold_expr(x))) ExprVec(folder.fold_exprs(exprs))
} }
ExprRepeat(expr, count) => { ExprRepeat(expr, count) => {
ExprRepeat(folder.fold_expr(expr), folder.fold_expr(count)) ExprRepeat(folder.fold_expr(expr), folder.fold_expr(count))
} }
ExprTup(elts) => ExprTup(elts.move_map(|x| folder.fold_expr(x))), ExprTup(exprs) => ExprTup(folder.fold_exprs(exprs)),
ExprCall(f, args) => { ExprCall(f, args) => {
ExprCall(folder.fold_expr(f), ExprCall(folder.fold_expr(f),
args.move_map(|x| folder.fold_expr(x))) folder.fold_exprs(args))
} }
ExprMethodCall(i, tps, args) => { ExprMethodCall(i, tps, args) => {
ExprMethodCall( ExprMethodCall(
respan(folder.new_span(i.span), folder.fold_ident(i.node)), respan(folder.new_span(i.span), folder.fold_ident(i.node)),
tps.move_map(|x| folder.fold_ty(x)), tps.move_map(|x| folder.fold_ty(x)),
args.move_map(|x| folder.fold_expr(x))) folder.fold_exprs(args))
} }
ExprBinary(binop, lhs, rhs) => { ExprBinary(binop, lhs, rhs) => {
ExprBinary(binop, ExprBinary(binop,
@ -1329,10 +1338,20 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
}, },
ExprParen(ex) => ExprParen(folder.fold_expr(ex)) ExprParen(ex) => ExprParen(folder.fold_expr(ex))
}, },
span: folder.new_span(span) span: folder.new_span(span),
attrs: attrs.map_opt_attrs(|v| fold_attrs(v, folder)),
} }
} }
pub fn noop_fold_opt_expr<T: Folder>(e: P<Expr>, folder: &mut T) -> Option<P<Expr>> {
Some(folder.fold_expr(e))
}
pub fn noop_fold_exprs<T: Folder>(es: Vec<P<Expr>>, folder: &mut T) -> Vec<P<Expr>> {
// FIXME: Needs a efficient in-place flat_map
es.into_iter().flat_map(|e| folder.fold_opt_expr(e)).collect()
}
pub fn noop_fold_stmt<T: Folder>(Spanned {node, span}: Stmt, folder: &mut T) pub fn noop_fold_stmt<T: Folder>(Spanned {node, span}: Stmt, folder: &mut T)
-> SmallVector<P<Stmt>> { -> SmallVector<P<Stmt>> {
let span = folder.new_span(span); let span = folder.new_span(span);
@ -1346,20 +1365,30 @@ pub fn noop_fold_stmt<T: Folder>(Spanned {node, span}: Stmt, folder: &mut T)
} }
StmtExpr(e, id) => { StmtExpr(e, id) => {
let id = folder.new_id(id); let id = folder.new_id(id);
if let Some(e) = folder.fold_opt_expr(e) {
SmallVector::one(P(Spanned { SmallVector::one(P(Spanned {
node: StmtExpr(folder.fold_expr(e), id), node: StmtExpr(e, id),
span: span span: span
})) }))
} else {
SmallVector::zero()
}
} }
StmtSemi(e, id) => { StmtSemi(e, id) => {
let id = folder.new_id(id); let id = folder.new_id(id);
if let Some(e) = folder.fold_opt_expr(e) {
SmallVector::one(P(Spanned { SmallVector::one(P(Spanned {
node: StmtSemi(folder.fold_expr(e), id), node: StmtSemi(e, id),
span: span span: span
})) }))
} else {
SmallVector::zero()
} }
StmtMac(mac, semi) => SmallVector::one(P(Spanned { }
node: StmtMac(mac.map(|m| folder.fold_mac(m)), semi), StmtMac(mac, semi, attrs) => SmallVector::one(P(Spanned {
node: StmtMac(mac.map(|m| folder.fold_mac(m)),
semi,
attrs.map_opt_attrs(|v| fold_attrs(v, folder))),
span: span span: span
})) }))
} }

View file

@ -699,7 +699,8 @@ mod tests {
} }
), ),
}), }),
span: sp(0, 1) span: sp(0, 1),
attrs: None,
})) }))
} }
@ -721,7 +722,8 @@ mod tests {
} }
) )
}), }),
span: sp(0, 6) span: sp(0, 6),
attrs: None,
})) }))
} }
@ -848,9 +850,11 @@ mod tests {
} }
), ),
}), }),
span:sp(7,8) span:sp(7,8),
attrs: None,
}))), }))),
span:sp(0,8) span:sp(0,8),
attrs: None,
})) }))
} }
@ -869,7 +873,8 @@ mod tests {
} }
), ),
}), }),
span: sp(0,1)}), span: sp(0,1),
attrs: None}),
ast::DUMMY_NODE_ID), ast::DUMMY_NODE_ID),
span: sp(0,1)}))) span: sp(0,1)})))
@ -963,7 +968,8 @@ mod tests {
} }
), ),
}), }),
span: sp(17,18)}), span: sp(17,18),
attrs: None,}),
ast::DUMMY_NODE_ID), ast::DUMMY_NODE_ID),
span: sp(17,19)})), span: sp(17,19)})),
expr: None, expr: None,

File diff suppressed because it is too large Load diff

View file

@ -202,6 +202,7 @@ impl Token {
Interpolated(NtIdent(..)) => true, Interpolated(NtIdent(..)) => true,
Interpolated(NtBlock(..)) => true, Interpolated(NtBlock(..)) => true,
Interpolated(NtPath(..)) => true, Interpolated(NtPath(..)) => true,
Pound => true, // for expression attributes
_ => false, _ => false,
} }
} }

View file

@ -13,6 +13,7 @@ pub use self::AnnNode::*;
use abi; use abi;
use ast::{self, TokenTree}; use ast::{self, TokenTree};
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
use ast::{ThinAttributesExt, Attribute};
use ast_util; use ast_util;
use util::parser::AssocOp; use util::parser::AssocOp;
use attr; use attr;
@ -77,7 +78,7 @@ pub fn rust_printer<'a>(writer: Box<Write+'a>) -> State<'a> {
pub fn rust_printer_annotated<'a>(writer: Box<Write+'a>, pub fn rust_printer_annotated<'a>(writer: Box<Write+'a>,
ann: &'a PpAnn) -> State<'a> { ann: &'a PpAnn) -> State<'a> {
State { State {
s: pp::mk_printer(writer, default_columns), s: pp::mk_printer(writer, DEFAULT_COLUMNS),
cm: None, cm: None,
comments: None, comments: None,
literals: None, literals: None,
@ -90,11 +91,9 @@ pub fn rust_printer_annotated<'a>(writer: Box<Write+'a>,
} }
} }
#[allow(non_upper_case_globals)] pub const INDENT_UNIT: usize = 4;
pub const indent_unit: usize = 4;
#[allow(non_upper_case_globals)] pub const DEFAULT_COLUMNS: usize = 78;
pub const default_columns: usize = 78;
/// Requires you to pass an input filename and reader so that /// Requires you to pass an input filename and reader so that
/// it can scan the input text for comments and literals to /// it can scan the input text for comments and literals to
@ -170,7 +169,7 @@ impl<'a> State<'a> {
comments: Option<Vec<comments::Comment>>, comments: Option<Vec<comments::Comment>>,
literals: Option<Vec<comments::Literal>>) -> State<'a> { literals: Option<Vec<comments::Literal>>) -> State<'a> {
State { State {
s: pp::mk_printer(out, default_columns), s: pp::mk_printer(out, DEFAULT_COLUMNS),
cm: Some(cm), cm: Some(cm),
comments: comments, comments: comments,
literals: literals, literals: literals,
@ -401,7 +400,7 @@ pub fn fun_to_string(decl: &ast::FnDecl,
pub fn block_to_string(blk: &ast::Block) -> String { pub fn block_to_string(blk: &ast::Block) -> String {
to_string(|s| { to_string(|s| {
// containing cbox, will be closed by print-block at } // containing cbox, will be closed by print-block at }
try!(s.cbox(indent_unit)); try!(s.cbox(INDENT_UNIT));
// head-ibox, will be closed by print-block after { // head-ibox, will be closed by print-block after {
try!(s.ibox(0)); try!(s.ibox(0));
s.print_block(blk) s.print_block(blk)
@ -708,42 +707,60 @@ pub trait PrintState<'a> {
fn print_inner_attributes(&mut self, fn print_inner_attributes(&mut self,
attrs: &[ast::Attribute]) -> io::Result<()> { attrs: &[ast::Attribute]) -> io::Result<()> {
let mut count = 0; self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
for attr in attrs {
match attr.node.style {
ast::AttrStyle::Inner => {
try!(self.print_attribute(attr));
count += 1;
} }
_ => {/* fallthrough */ }
} fn print_inner_attributes_no_trailing_hardbreak(&mut self,
} attrs: &[ast::Attribute])
if count > 0 { -> io::Result<()> {
try!(self.hardbreak_if_not_bol()); self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
}
Ok(())
} }
fn print_outer_attributes(&mut self, fn print_outer_attributes(&mut self,
attrs: &[ast::Attribute]) -> io::Result<()> { attrs: &[ast::Attribute]) -> io::Result<()> {
self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
}
fn print_inner_attributes_inline(&mut self,
attrs: &[ast::Attribute]) -> io::Result<()> {
self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
}
fn print_outer_attributes_inline(&mut self,
attrs: &[ast::Attribute]) -> io::Result<()> {
self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
}
fn print_either_attributes(&mut self,
attrs: &[ast::Attribute],
kind: ast::AttrStyle,
is_inline: bool,
trailing_hardbreak: bool) -> io::Result<()> {
let mut count = 0; let mut count = 0;
for attr in attrs { for attr in attrs {
match attr.node.style { if attr.node.style == kind {
ast::AttrStyle::Outer => { try!(self.print_attribute_inline(attr, is_inline));
try!(self.print_attribute(attr)); if is_inline {
try!(self.nbsp());
}
count += 1; count += 1;
} }
_ => {/* fallthrough */ }
} }
} if count > 0 && trailing_hardbreak && !is_inline {
if count > 0 {
try!(self.hardbreak_if_not_bol()); try!(self.hardbreak_if_not_bol());
} }
Ok(()) Ok(())
} }
fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> { fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> {
self.print_attribute_inline(attr, false)
}
fn print_attribute_inline(&mut self, attr: &ast::Attribute,
is_inline: bool) -> io::Result<()> {
if !is_inline {
try!(self.hardbreak_if_not_bol()); try!(self.hardbreak_if_not_bol());
}
try!(self.maybe_print_comment(attr.span.lo)); try!(self.maybe_print_comment(attr.span.lo));
if attr.node.is_sugared_doc { if attr.node.is_sugared_doc {
word(self.writer(), &attr.value_str().unwrap()) word(self.writer(), &attr.value_str().unwrap())
@ -758,7 +775,7 @@ pub trait PrintState<'a> {
} }
fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> { fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
match item.node { match item.node {
ast::MetaWord(ref name) => { ast::MetaWord(ref name) => {
try!(word(self.writer(), &name)); try!(word(self.writer(), &name));
@ -779,6 +796,13 @@ pub trait PrintState<'a> {
} }
self.end() self.end()
} }
fn space_if_not_bol(&mut self) -> io::Result<()> {
if !self.is_bol() { try!(space(self.writer())); }
Ok(())
}
fn nbsp(&mut self) -> io::Result<()> { word(self.writer(), " ") }
} }
impl<'a> PrintState<'a> for State<'a> { impl<'a> PrintState<'a> for State<'a> {
@ -809,8 +833,6 @@ impl<'a> State<'a> {
pp::cbox(&mut self.s, u) pp::cbox(&mut self.s, u)
} }
pub fn nbsp(&mut self) -> io::Result<()> { word(&mut self.s, " ") }
pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> { pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> {
try!(word(&mut self.s, w)); try!(word(&mut self.s, w));
self.nbsp() self.nbsp()
@ -818,7 +840,7 @@ impl<'a> State<'a> {
pub fn head(&mut self, w: &str) -> io::Result<()> { pub fn head(&mut self, w: &str) -> io::Result<()> {
// outer-box is consistent // outer-box is consistent
try!(self.cbox(indent_unit)); try!(self.cbox(INDENT_UNIT));
// head-box is inconsistent // head-box is inconsistent
try!(self.ibox(w.len() + 1)); try!(self.ibox(w.len() + 1));
// keyword that starts the head // keyword that starts the head
@ -848,7 +870,7 @@ impl<'a> State<'a> {
Ok(()) Ok(())
} }
pub fn bclose(&mut self, span: codemap::Span) -> io::Result<()> { pub fn bclose(&mut self, span: codemap::Span) -> io::Result<()> {
self.bclose_(span, indent_unit) self.bclose_(span, INDENT_UNIT)
} }
pub fn in_cbox(&self) -> bool { pub fn in_cbox(&self) -> bool {
@ -858,10 +880,6 @@ impl<'a> State<'a> {
} }
} }
pub fn space_if_not_bol(&mut self) -> io::Result<()> {
if !self.is_bol() { try!(space(&mut self.s)); }
Ok(())
}
pub fn break_offset_if_not_bol(&mut self, n: usize, pub fn break_offset_if_not_bol(&mut self, n: usize,
off: isize) -> io::Result<()> { off: isize) -> io::Result<()> {
if !self.is_bol() { if !self.is_bol() {
@ -1200,7 +1218,7 @@ impl<'a> State<'a> {
try!(self.bclose(item.span)); try!(self.bclose(item.span));
} }
ast::ItemTy(ref ty, ref params) => { ast::ItemTy(ref ty, ref params) => {
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
try!(self.ibox(0)); try!(self.ibox(0));
try!(self.word_nbsp(&visibility_qualified(item.vis, "type"))); try!(self.word_nbsp(&visibility_qualified(item.vis, "type")));
try!(self.print_ident(item.ident)); try!(self.print_ident(item.ident));
@ -1314,7 +1332,7 @@ impl<'a> State<'a> {
try!(self.print_path(&node.path, false, 0)); try!(self.print_path(&node.path, false, 0));
try!(word(&mut self.s, "! ")); try!(word(&mut self.s, "! "));
try!(self.print_ident(item.ident)); try!(self.print_ident(item.ident));
try!(self.cbox(indent_unit)); try!(self.cbox(INDENT_UNIT));
try!(self.popen()); try!(self.popen());
try!(self.print_tts(&node.tts[..])); try!(self.print_tts(&node.tts[..]));
try!(self.pclose()); try!(self.pclose());
@ -1370,7 +1388,7 @@ impl<'a> State<'a> {
try!(self.space_if_not_bol()); try!(self.space_if_not_bol());
try!(self.maybe_print_comment(v.span.lo)); try!(self.maybe_print_comment(v.span.lo));
try!(self.print_outer_attributes(&v.node.attrs)); try!(self.print_outer_attributes(&v.node.attrs));
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
try!(self.print_variant(&**v)); try!(self.print_variant(&**v));
try!(word(&mut self.s, ",")); try!(word(&mut self.s, ","));
try!(self.end()); try!(self.end());
@ -1592,7 +1610,7 @@ impl<'a> State<'a> {
// code copied from ItemMac: // code copied from ItemMac:
try!(self.print_path(&node.path, false, 0)); try!(self.print_path(&node.path, false, 0));
try!(word(&mut self.s, "! ")); try!(word(&mut self.s, "! "));
try!(self.cbox(indent_unit)); try!(self.cbox(INDENT_UNIT));
try!(self.popen()); try!(self.popen());
try!(self.print_tts(&node.tts[..])); try!(self.print_tts(&node.tts[..]));
try!(self.pclose()); try!(self.pclose());
@ -1611,15 +1629,16 @@ impl<'a> State<'a> {
} }
ast::StmtExpr(ref expr, _) => { ast::StmtExpr(ref expr, _) => {
try!(self.space_if_not_bol()); try!(self.space_if_not_bol());
try!(self.print_expr(&**expr)); try!(self.print_expr_outer_attr_style(&**expr, false));
} }
ast::StmtSemi(ref expr, _) => { ast::StmtSemi(ref expr, _) => {
try!(self.space_if_not_bol()); try!(self.space_if_not_bol());
try!(self.print_expr(&**expr)); try!(self.print_expr_outer_attr_style(&**expr, false));
try!(word(&mut self.s, ";")); try!(word(&mut self.s, ";"));
} }
ast::StmtMac(ref mac, style) => { ast::StmtMac(ref mac, style, ref attrs) => {
try!(self.space_if_not_bol()); try!(self.space_if_not_bol());
try!(self.print_outer_attributes(attrs.as_attrs()));
let delim = match style { let delim = match style {
ast::MacStmtWithBraces => token::Brace, ast::MacStmtWithBraces => token::Brace,
_ => token::Paren _ => token::Paren
@ -1633,6 +1652,8 @@ impl<'a> State<'a> {
} }
if parse::classify::stmt_ends_with_semi(&st.node) { if parse::classify::stmt_ends_with_semi(&st.node) {
try!(word(&mut self.s, ";")); try!(word(&mut self.s, ";"));
} else {
//try!(word(&mut self.s, ""));
} }
self.maybe_print_trailing_comment(st.span, None) self.maybe_print_trailing_comment(st.span, None)
} }
@ -1642,7 +1663,13 @@ impl<'a> State<'a> {
} }
pub fn print_block_unclosed(&mut self, blk: &ast::Block) -> io::Result<()> { pub fn print_block_unclosed(&mut self, blk: &ast::Block) -> io::Result<()> {
self.print_block_unclosed_indent(blk, indent_unit) self.print_block_unclosed_indent(blk, INDENT_UNIT)
}
pub fn print_block_unclosed_with_attrs(&mut self, blk: &ast::Block,
attrs: &[ast::Attribute])
-> io::Result<()> {
self.print_block_maybe_unclosed(blk, INDENT_UNIT, attrs, false)
} }
pub fn print_block_unclosed_indent(&mut self, blk: &ast::Block, pub fn print_block_unclosed_indent(&mut self, blk: &ast::Block,
@ -1653,7 +1680,7 @@ impl<'a> State<'a> {
pub fn print_block_with_attrs(&mut self, pub fn print_block_with_attrs(&mut self,
blk: &ast::Block, blk: &ast::Block,
attrs: &[ast::Attribute]) -> io::Result<()> { attrs: &[ast::Attribute]) -> io::Result<()> {
self.print_block_maybe_unclosed(blk, indent_unit, attrs, true) self.print_block_maybe_unclosed(blk, INDENT_UNIT, attrs, true)
} }
pub fn print_block_maybe_unclosed(&mut self, pub fn print_block_maybe_unclosed(&mut self,
@ -1677,7 +1704,7 @@ impl<'a> State<'a> {
match blk.expr { match blk.expr {
Some(ref expr) => { Some(ref expr) => {
try!(self.space_if_not_bol()); try!(self.space_if_not_bol());
try!(self.print_expr(&**expr)); try!(self.print_expr_outer_attr_style(&**expr, false));
try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi))); try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi)));
} }
_ => () _ => ()
@ -1692,7 +1719,7 @@ impl<'a> State<'a> {
match _else.node { match _else.node {
// "another else-if" // "another else-if"
ast::ExprIf(ref i, ref then, ref e) => { ast::ExprIf(ref i, ref then, ref e) => {
try!(self.cbox(indent_unit - 1)); try!(self.cbox(INDENT_UNIT - 1));
try!(self.ibox(0)); try!(self.ibox(0));
try!(word(&mut self.s, " else if ")); try!(word(&mut self.s, " else if "));
try!(self.print_expr(&**i)); try!(self.print_expr(&**i));
@ -1702,7 +1729,7 @@ impl<'a> State<'a> {
} }
// "another else-if-let" // "another else-if-let"
ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => { ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => {
try!(self.cbox(indent_unit - 1)); try!(self.cbox(INDENT_UNIT - 1));
try!(self.ibox(0)); try!(self.ibox(0));
try!(word(&mut self.s, " else if let ")); try!(word(&mut self.s, " else if let "));
try!(self.print_pat(&**pat)); try!(self.print_pat(&**pat));
@ -1715,7 +1742,7 @@ impl<'a> State<'a> {
} }
// "final else" // "final else"
ast::ExprBlock(ref b) => { ast::ExprBlock(ref b) => {
try!(self.cbox(indent_unit - 1)); try!(self.cbox(INDENT_UNIT - 1));
try!(self.ibox(0)); try!(self.ibox(0));
try!(word(&mut self.s, " else ")); try!(word(&mut self.s, " else "));
self.print_block(&**b) self.print_block(&**b)
@ -1758,7 +1785,13 @@ impl<'a> State<'a> {
match delim { match delim {
token::Paren => try!(self.popen()), token::Paren => try!(self.popen()),
token::Bracket => try!(word(&mut self.s, "[")), token::Bracket => try!(word(&mut self.s, "[")),
token::Brace => try!(self.bopen()), token::Brace => {
// head-ibox, will be closed by bopen()
try!(self.ibox(0));
// Don't ask me why the regular bopen() does
// more then just opening a brace...
try!(self.bopen())
}
} }
try!(self.print_tts(&m.node.tts)); try!(self.print_tts(&m.node.tts));
match delim { match delim {
@ -1811,9 +1844,11 @@ impl<'a> State<'a> {
self.print_expr_maybe_paren(expr) self.print_expr_maybe_paren(expr)
} }
fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> { fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>],
try!(self.ibox(indent_unit)); attrs: &[Attribute]) -> io::Result<()> {
try!(self.ibox(INDENT_UNIT));
try!(word(&mut self.s, "[")); try!(word(&mut self.s, "["));
try!(self.print_inner_attributes_inline(attrs));
try!(self.commasep_exprs(Inconsistent, &exprs[..])); try!(self.commasep_exprs(Inconsistent, &exprs[..]));
try!(word(&mut self.s, "]")); try!(word(&mut self.s, "]"));
self.end() self.end()
@ -1821,9 +1856,11 @@ impl<'a> State<'a> {
fn print_expr_repeat(&mut self, fn print_expr_repeat(&mut self,
element: &ast::Expr, element: &ast::Expr,
count: &ast::Expr) -> io::Result<()> { count: &ast::Expr,
try!(self.ibox(indent_unit)); attrs: &[Attribute]) -> io::Result<()> {
try!(self.ibox(INDENT_UNIT));
try!(word(&mut self.s, "[")); try!(word(&mut self.s, "["));
try!(self.print_inner_attributes_inline(attrs));
try!(self.print_expr(element)); try!(self.print_expr(element));
try!(self.word_space(";")); try!(self.word_space(";"));
try!(self.print_expr(count)); try!(self.print_expr(count));
@ -1834,14 +1871,16 @@ impl<'a> State<'a> {
fn print_expr_struct(&mut self, fn print_expr_struct(&mut self,
path: &ast::Path, path: &ast::Path,
fields: &[ast::Field], fields: &[ast::Field],
wth: &Option<P<ast::Expr>>) -> io::Result<()> { wth: &Option<P<ast::Expr>>,
attrs: &[Attribute]) -> io::Result<()> {
try!(self.print_path(path, true, 0)); try!(self.print_path(path, true, 0));
try!(word(&mut self.s, "{")); try!(word(&mut self.s, "{"));
try!(self.print_inner_attributes_inline(attrs));
try!(self.commasep_cmnt( try!(self.commasep_cmnt(
Consistent, Consistent,
&fields[..], &fields[..],
|s, field| { |s, field| {
try!(s.ibox(indent_unit)); try!(s.ibox(INDENT_UNIT));
try!(s.print_ident(field.ident.node)); try!(s.print_ident(field.ident.node));
try!(s.word_space(":")); try!(s.word_space(":"));
try!(s.print_expr(&*field.expr)); try!(s.print_expr(&*field.expr));
@ -1850,7 +1889,7 @@ impl<'a> State<'a> {
|f| f.span)); |f| f.span));
match *wth { match *wth {
Some(ref expr) => { Some(ref expr) => {
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
if !fields.is_empty() { if !fields.is_empty() {
try!(word(&mut self.s, ",")); try!(word(&mut self.s, ","));
try!(space(&mut self.s)); try!(space(&mut self.s));
@ -1867,8 +1906,10 @@ impl<'a> State<'a> {
Ok(()) Ok(())
} }
fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> { fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>],
attrs: &[Attribute]) -> io::Result<()> {
try!(self.popen()); try!(self.popen());
try!(self.print_inner_attributes_inline(attrs));
try!(self.commasep_exprs(Inconsistent, &exprs[..])); try!(self.commasep_exprs(Inconsistent, &exprs[..]));
if exprs.len() == 1 { if exprs.len() == 1 {
try!(word(&mut self.s, ",")); try!(word(&mut self.s, ","));
@ -1934,8 +1975,22 @@ impl<'a> State<'a> {
} }
pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> { pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
self.print_expr_outer_attr_style(expr, true)
}
fn print_expr_outer_attr_style(&mut self,
expr: &ast::Expr,
is_inline: bool) -> io::Result<()> {
try!(self.maybe_print_comment(expr.span.lo)); try!(self.maybe_print_comment(expr.span.lo));
try!(self.ibox(indent_unit));
let attrs = expr.attrs.as_attrs();
if is_inline {
try!(self.print_outer_attributes_inline(attrs));
} else {
try!(self.print_outer_attributes(attrs));
}
try!(self.ibox(INDENT_UNIT));
try!(self.ann.pre(self, NodeExpr(expr))); try!(self.ann.pre(self, NodeExpr(expr)));
match expr.node { match expr.node {
ast::ExprBox(ref expr) => { ast::ExprBox(ref expr) => {
@ -1946,16 +2001,16 @@ impl<'a> State<'a> {
try!(self.print_expr_in_place(place, expr)); try!(self.print_expr_in_place(place, expr));
} }
ast::ExprVec(ref exprs) => { ast::ExprVec(ref exprs) => {
try!(self.print_expr_vec(&exprs[..])); try!(self.print_expr_vec(&exprs[..], attrs));
} }
ast::ExprRepeat(ref element, ref count) => { ast::ExprRepeat(ref element, ref count) => {
try!(self.print_expr_repeat(&**element, &**count)); try!(self.print_expr_repeat(&**element, &**count, attrs));
} }
ast::ExprStruct(ref path, ref fields, ref wth) => { ast::ExprStruct(ref path, ref fields, ref wth) => {
try!(self.print_expr_struct(path, &fields[..], wth)); try!(self.print_expr_struct(path, &fields[..], wth, attrs));
} }
ast::ExprTup(ref exprs) => { ast::ExprTup(ref exprs) => {
try!(self.print_expr_tup(&exprs[..])); try!(self.print_expr_tup(&exprs[..], attrs));
} }
ast::ExprCall(ref func, ref args) => { ast::ExprCall(ref func, ref args) => {
try!(self.print_expr_call(&**func, &args[..])); try!(self.print_expr_call(&**func, &args[..]));
@ -1999,7 +2054,7 @@ impl<'a> State<'a> {
try!(self.head("while")); try!(self.head("while"));
try!(self.print_expr(&**test)); try!(self.print_expr(&**test));
try!(space(&mut self.s)); try!(space(&mut self.s));
try!(self.print_block(&**blk)); try!(self.print_block_with_attrs(&**blk, attrs));
} }
ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => { ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => {
if let Some(ident) = opt_ident { if let Some(ident) = opt_ident {
@ -2012,7 +2067,7 @@ impl<'a> State<'a> {
try!(self.word_space("=")); try!(self.word_space("="));
try!(self.print_expr(&**expr)); try!(self.print_expr(&**expr));
try!(space(&mut self.s)); try!(space(&mut self.s));
try!(self.print_block(&**blk)); try!(self.print_block_with_attrs(&**blk, attrs));
} }
ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => { ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => {
if let Some(ident) = opt_ident { if let Some(ident) = opt_ident {
@ -2025,7 +2080,7 @@ impl<'a> State<'a> {
try!(self.word_space("in")); try!(self.word_space("in"));
try!(self.print_expr(&**iter)); try!(self.print_expr(&**iter));
try!(space(&mut self.s)); try!(space(&mut self.s));
try!(self.print_block(&**blk)); try!(self.print_block_with_attrs(&**blk, attrs));
} }
ast::ExprLoop(ref blk, opt_ident) => { ast::ExprLoop(ref blk, opt_ident) => {
if let Some(ident) = opt_ident { if let Some(ident) = opt_ident {
@ -2034,19 +2089,20 @@ impl<'a> State<'a> {
} }
try!(self.head("loop")); try!(self.head("loop"));
try!(space(&mut self.s)); try!(space(&mut self.s));
try!(self.print_block(&**blk)); try!(self.print_block_with_attrs(&**blk, attrs));
} }
ast::ExprMatch(ref expr, ref arms) => { ast::ExprMatch(ref expr, ref arms) => {
try!(self.cbox(indent_unit)); try!(self.cbox(INDENT_UNIT));
try!(self.ibox(4)); try!(self.ibox(4));
try!(self.word_nbsp("match")); try!(self.word_nbsp("match"));
try!(self.print_expr(&**expr)); try!(self.print_expr(&**expr));
try!(space(&mut self.s)); try!(space(&mut self.s));
try!(self.bopen()); try!(self.bopen());
try!(self.print_inner_attributes_no_trailing_hardbreak(attrs));
for arm in arms { for arm in arms {
try!(self.print_arm(arm)); try!(self.print_arm(arm));
} }
try!(self.bclose_(expr.span, indent_unit)); try!(self.bclose_(expr.span, INDENT_UNIT));
} }
ast::ExprClosure(capture_clause, ref decl, ref body) => { ast::ExprClosure(capture_clause, ref decl, ref body) => {
try!(self.print_capture_clause(capture_clause)); try!(self.print_capture_clause(capture_clause));
@ -2063,13 +2119,16 @@ impl<'a> State<'a> {
try!(self.print_block_unclosed(&**body)); try!(self.print_block_unclosed(&**body));
} else { } else {
// we extract the block, so as not to create another set of boxes // we extract the block, so as not to create another set of boxes
match body.expr.as_ref().unwrap().node { let i_expr = body.expr.as_ref().unwrap();
match i_expr.node {
ast::ExprBlock(ref blk) => { ast::ExprBlock(ref blk) => {
try!(self.print_block_unclosed(&**blk)); try!(self.print_block_unclosed_with_attrs(
&**blk,
i_expr.attrs.as_attrs()));
} }
_ => { _ => {
// this is a bare expression // this is a bare expression
try!(self.print_expr(body.expr.as_ref().map(|e| &**e).unwrap())); try!(self.print_expr(&**i_expr));
try!(self.end()); // need to close a box try!(self.end()); // need to close a box
} }
} }
@ -2081,10 +2140,10 @@ impl<'a> State<'a> {
} }
ast::ExprBlock(ref blk) => { ast::ExprBlock(ref blk) => {
// containing cbox, will be closed by print-block at } // containing cbox, will be closed by print-block at }
try!(self.cbox(indent_unit)); try!(self.cbox(INDENT_UNIT));
// head-box, will be closed by print-block after { // head-box, will be closed by print-block after {
try!(self.ibox(0)); try!(self.ibox(0));
try!(self.print_block(&**blk)); try!(self.print_block_with_attrs(&**blk, attrs));
} }
ast::ExprAssign(ref lhs, ref rhs) => { ast::ExprAssign(ref lhs, ref rhs) => {
try!(self.print_expr(&**lhs)); try!(self.print_expr(&**lhs));
@ -2222,6 +2281,7 @@ impl<'a> State<'a> {
ast::ExprMac(ref m) => try!(self.print_mac(m, token::Paren)), ast::ExprMac(ref m) => try!(self.print_mac(m, token::Paren)),
ast::ExprParen(ref e) => { ast::ExprParen(ref e) => {
try!(self.popen()); try!(self.popen());
try!(self.print_inner_attributes_inline(attrs));
try!(self.print_expr(&**e)); try!(self.print_expr(&**e));
try!(self.pclose()); try!(self.pclose());
} }
@ -2243,11 +2303,12 @@ impl<'a> State<'a> {
try!(self.maybe_print_comment(decl.span.lo)); try!(self.maybe_print_comment(decl.span.lo));
match decl.node { match decl.node {
ast::DeclLocal(ref loc) => { ast::DeclLocal(ref loc) => {
try!(self.print_outer_attributes(loc.attrs.as_attrs()));
try!(self.space_if_not_bol()); try!(self.space_if_not_bol());
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
try!(self.word_nbsp("let")); try!(self.word_nbsp("let"));
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
try!(self.print_local_decl(&**loc)); try!(self.print_local_decl(&**loc));
try!(self.end()); try!(self.end());
if let Some(ref init) = loc.init { if let Some(ref init) = loc.init {
@ -2452,7 +2513,7 @@ impl<'a> State<'a> {
try!(self.commasep_cmnt( try!(self.commasep_cmnt(
Consistent, &fields[..], Consistent, &fields[..],
|s, f| { |s, f| {
try!(s.cbox(indent_unit)); try!(s.cbox(INDENT_UNIT));
if !f.node.is_shorthand { if !f.node.is_shorthand {
try!(s.print_ident(f.node.ident)); try!(s.print_ident(f.node.ident));
try!(s.word_nbsp(":")); try!(s.word_nbsp(":"));
@ -2525,7 +2586,7 @@ impl<'a> State<'a> {
if arm.attrs.is_empty() { if arm.attrs.is_empty() {
try!(space(&mut self.s)); try!(space(&mut self.s));
} }
try!(self.cbox(indent_unit)); try!(self.cbox(INDENT_UNIT));
try!(self.ibox(0)); try!(self.ibox(0));
try!(self.print_outer_attributes(&arm.attrs)); try!(self.print_outer_attributes(&arm.attrs));
let mut first = true; let mut first = true;
@ -2549,7 +2610,7 @@ impl<'a> State<'a> {
match arm.body.node { match arm.body.node {
ast::ExprBlock(ref blk) => { ast::ExprBlock(ref blk) => {
// the block will close the pattern's ibox // the block will close the pattern's ibox
try!(self.print_block_unclosed_indent(&**blk, indent_unit)); try!(self.print_block_unclosed_indent(&**blk, INDENT_UNIT));
// If it is a user-provided unsafe block, print a comma after it // If it is a user-provided unsafe block, print a comma after it
if let ast::UnsafeBlock(ast::UserProvided) = blk.rules { if let ast::UnsafeBlock(ast::UserProvided) = blk.rules {
@ -2907,7 +2968,7 @@ impl<'a> State<'a> {
} }
pub fn print_arg(&mut self, input: &ast::Arg) -> io::Result<()> { pub fn print_arg(&mut self, input: &ast::Arg) -> io::Result<()> {
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
match input.ty.node { match input.ty.node {
ast::TyInfer => try!(self.print_pat(&*input.pat)), ast::TyInfer => try!(self.print_pat(&*input.pat)),
_ => { _ => {
@ -2935,7 +2996,7 @@ impl<'a> State<'a> {
} }
try!(self.space_if_not_bol()); try!(self.space_if_not_bol());
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
try!(self.word_space("->")); try!(self.word_space("->"));
match decl.output { match decl.output {
ast::NoReturn(_) => ast::NoReturn(_) =>
@ -2960,7 +3021,7 @@ impl<'a> State<'a> {
generics: &ast::Generics, generics: &ast::Generics,
opt_explicit_self: Option<&ast::ExplicitSelf_>) opt_explicit_self: Option<&ast::ExplicitSelf_>)
-> io::Result<()> { -> io::Result<()> {
try!(self.ibox(indent_unit)); try!(self.ibox(INDENT_UNIT));
if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
try!(word(&mut self.s, "for")); try!(word(&mut self.s, "for"));
try!(self.print_generics(generics)); try!(self.print_generics(generics));

View file

@ -88,7 +88,7 @@ pub fn modify_for_testing(sess: &ParseSess,
if should_test { if should_test {
generate_test_harness(sess, reexport_test_harness_main, krate, cfg, span_diagnostic) generate_test_harness(sess, reexport_test_harness_main, krate, cfg, span_diagnostic)
} else { } else {
strip_test_functions(krate) strip_test_functions(span_diagnostic, krate)
} }
} }
@ -314,10 +314,11 @@ fn generate_test_harness(sess: &ParseSess,
return res; return res;
} }
fn strip_test_functions(krate: ast::Crate) -> ast::Crate { fn strip_test_functions(diagnostic: &diagnostic::SpanHandler, krate: ast::Crate)
-> ast::Crate {
// When not compiling with --test we should not compile the // When not compiling with --test we should not compile the
// #[test] functions // #[test] functions
config::strip_items(krate, |attrs| { config::strip_items(diagnostic, krate, |attrs| {
!attr::contains_name(&attrs[..], "test") && !attr::contains_name(&attrs[..], "test") &&
!attr::contains_name(&attrs[..], "bench") !attr::contains_name(&attrs[..], "bench")
}) })
@ -619,8 +620,10 @@ fn mk_test_descs(cx: &TestCtxt) -> P<ast::Expr> {
mk_test_desc_and_fn_rec(cx, test) mk_test_desc_and_fn_rec(cx, test)
}).collect()), }).collect()),
span: DUMMY_SP, span: DUMMY_SP,
attrs: None,
})), })),
span: DUMMY_SP, span: DUMMY_SP,
attrs: None,
}) })
} }

View file

@ -628,7 +628,12 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt) {
StmtExpr(ref expression, _) | StmtSemi(ref expression, _) => { StmtExpr(ref expression, _) | StmtSemi(ref expression, _) => {
visitor.visit_expr(expression) visitor.visit_expr(expression)
} }
StmtMac(ref mac, _) => visitor.visit_mac(mac), StmtMac(ref mac, _, ref attrs) => {
visitor.visit_mac(mac);
for attr in attrs.as_attrs() {
visitor.visit_attribute(attr);
}
}
} }
} }

View file

@ -1,16 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
fn main() {
#[attr] //~ ERROR expected item after attributes
println!("hi");
}

View file

@ -1,16 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
fn main() {
#[attr] //~ ERROR expected item
let __isize = 0;
}

View file

@ -1,21 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
// error-pattern:expected item
fn f() {
#[foo = "bar"]
let x = 10;
}
fn main() {
}

View file

@ -10,7 +10,7 @@
// compile-flags: -Z parse-only // compile-flags: -Z parse-only
// error-pattern:expected item // error-pattern:expected statement
fn f() { fn f() {
#[foo = "bar"] #[foo = "bar"]

View file

@ -1,17 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
fn main() {
/// hi
println!("hi");
//~^^ ERROR expected item after doc comment
}

View file

@ -12,5 +12,5 @@
fn main() { fn main() {
println!("Hi"); /// hi println!("Hi"); /// hi
//~^ ERROR expected item after doc comment
} }
//~^ ERROR expected statement

View file

@ -13,5 +13,5 @@
fn main() { fn main() {
/// hi /// hi
; ;
//~^^ ERROR expected item after doc comment //~^ ERROR expected statement
} }

View file

@ -0,0 +1,281 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// pp-exact
#![feature(custom_attribute)]
#![feature(box_syntax)]
#![feature(placement_in_syntax)]
fn main() { }
fn _0() {
#[attr]
foo();
}
fn _1() {
#[attr]
unsafe {
// code
}
}
fn _2() {
#[attr]
{ foo(); }
{
#![attr]
foo()
}
}
fn _3() {
#[attr]
match () { _ => { } }
}
fn _4() {
#[attr]
match () {
#![attr]
_ => (),
}
let _ =
#[attr] match () {
#![attr]
() => (),
};
}
fn _5() {
#[attr]
let x = 1;
let x = #[attr] 1;
let y = ();
let z = ();
foo3(x, #[attr] y, z);
qux(3 + #[attr] 2);
}
fn _6() {
#[attr]
[#![attr] 1, 2, 3];
let _ = #[attr] [#![attr] 1, 2, 3];
#[attr]
[#![attr] 1; 4];
let _ = #[attr] [#![attr] 1; 4];
}
struct Foo {
data: (),
}
struct Bar(());
fn _7() {
#[attr]
Foo{#![attr] data: (),};
let _ = #[attr] Foo{#![attr] data: (),};
}
fn _8() {
#[attr]
(#![attr] );
#[attr]
(#![attr] 0);
#[attr]
(#![attr] 0,);
#[attr]
(#![attr] 0, 1);
}
fn _9() {
macro_rules! stmt_mac(( ) => { let _ = ( ) ; });
#[attr]
stmt_mac!();
/*
// pre existing pp bug: delimiter styles gets lost:
#[attr]
stmt_mac!{ };
#[attr]
stmt_mac![];
#[attr]
stmt_mac!{ } // pre-existing pp bug: compiler ICEs with a None unwrap
*/
let _ = ();
}
macro_rules! expr_mac(( ) => { ( ) });
fn _10() {
let _ = #[attr] expr_mac!();
/*
// pre existing pp bug: delimiter styles gets lost:
let _ = #[attr] expr_mac![];
let _ = #[attr] expr_mac!{};
*/
}
fn _11() {
let _ = #[attr] box 0;
let _: [(); 0] = #[attr] [#![attr] ];
let _ = #[attr] [#![attr] 0, 0];
let _ = #[attr] [#![attr] 0; 0];
let _ = #[attr] foo();
let _ = #[attr] 1i32.clone();
let _ = #[attr] (#![attr] );
let _ = #[attr] (#![attr] 0);
let _ = #[attr] (#![attr] 0,);
let _ = #[attr] (#![attr] 0, 0);
let _ = #[attr] 0 + #[attr] 0;
let _ = #[attr] !0;
let _ = #[attr] -0i32;
let _ = #[attr] false;
let _ = #[attr] 'c';
let _ = #[attr] 0;
let _ = #[attr] 0 as usize;
let _ =
#[attr] while false {
#![attr]
};
let _ =
#[attr] while let None = Some(()) {
#![attr]
};
let _ =
#[attr] for _ in 0..0 {
#![attr]
};
// FIXME: pp bug, two spaces after the loop
let _ =
#[attr] loop {
#![attr]
};
let _ =
#[attr] match false {
#![attr]
_ => (),
};
let _ = #[attr] || #[attr] ();
let _ = #[attr] move || #[attr] ();
let _ = #[attr] || {
#![attr]
#[attr]
() };
let _ = #[attr] move || {
#![attr]
#[attr]
() };
let _ =
#[attr] {
#![attr]
};
let _ =
#[attr] {
#![attr]
let _ = ();
};
let _ =
#[attr] {
#![attr]
let _ = ();
()
};
let mut x = 0;
let _ = #[attr] x = 15;
let _ = #[attr] x += 15;
let s = Foo{data: (),};
let _ = #[attr] s.data;
let _ = (#[attr] s).data;
let t = Bar(());
let _ = #[attr] t.0;
let _ = (#[attr] t).0;
let v = vec!(0);
let _ = #[attr] v[0];
let _ = (#[attr] v)[0];
let _ = #[attr] 0..#[attr] 0;
let _ = #[attr] 0..;
let _ = #[attr] (0..0);
let _ = #[attr] (0..);
let _ = #[attr] (..0);
let _ = #[attr] (..);
let _: fn(&u32) -> u32 = #[attr] std::clone::Clone::clone;
let _ = #[attr] &0;
let _ = #[attr] &mut 0;
let _ = #[attr] &#[attr] 0;
let _ = #[attr] &mut #[attr] 0;
// FIXME: pp bug, extra space after keyword?
while false { let _ = #[attr] continue ; }
while true { let _ = #[attr] break ; }
|| #[attr] return;
let _ = #[attr] expr_mac!();
/* FIXME: pp bug, loosing delimiter styles
let _ = #[attr] expr_mac![];
let _ = #[attr] expr_mac!{};
*/
let _ = #[attr] Foo{#![attr] data: (),};
let _ = #[attr] Foo{#![attr] ..s};
let _ = #[attr] Foo{#![attr] data: (), ..s};
let _ = #[attr] (#![attr] 0);
}
fn _12() {
#[attr]
let _ = 0;
#[attr]
0;
#[attr]
expr_mac!();
#[attr]
{
#![attr]
}
}
/////////////////
fn foo() { }
fn foo3(_: i32, _: (), _: ()) { }
fn qux(_: i32) { }

View file

@ -0,0 +1,306 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_private)]
extern crate syntax;
use syntax::ast::*;
use syntax::ast;
use syntax::parse;
use syntax::parse::{ParseSess,filemap_to_tts, PResult};
use syntax::parse::new_parser_from_source_str;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::str::char_at;
use syntax::parse::attr::*;
use syntax::print::pprust;
use std::fmt;
// Copied out of syntax::util::parser_testing
pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> {
new_parser_from_source_str(ps,
Vec::new(),
"bogofile".to_string(),
source_str)
}
fn with_error_checking_parse<T, F>(s: String, f: F) -> PResult<T> where
F: FnOnce(&mut Parser) -> PResult<T>,
{
let ps = ParseSess::new();
let mut p = string_to_parser(&ps, s);
let x = f(&mut p);
if ps.span_diagnostic.handler().has_errors() || p.token != token::Eof {
return Err(p.fatal("parse error"));
}
x
}
fn expr(s: &str) -> PResult<P<ast::Expr>> {
with_error_checking_parse(s.to_string(), |p| {
p.parse_expr_nopanic()
})
}
fn stmt(s: &str) -> PResult<P<ast::Stmt>> {
with_error_checking_parse(s.to_string(), |p| {
p.parse_stmt_nopanic().map(|s| s.unwrap())
})
}
fn attr(s: &str) -> PResult<ast::Attribute> {
with_error_checking_parse(s.to_string(), |p| {
p.parse_attribute(true)
})
}
fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f: F) {
let expected: Vec<_> = expected.iter().map(|e| f(e)).collect();
let actual: Vec<_> = actual.iter().map(|e| f(e)).collect();
if expected != actual {
panic!("parsed `{}` as {:?}, expected {:?}", e, actual, expected);
}
}
fn check_expr_attrs(es: &str, expected: &[&str]) {
let e = expr(es).expect("parse error");
let actual = &e.attrs;
str_compare(es,
&expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
actual.as_attrs(),
pprust::attribute_to_string);
}
fn check_stmt_attrs(es: &str, expected: &[&str]) {
let e = stmt(es).expect("parse error");
let actual = e.node.attrs();
str_compare(es,
&expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
actual,
pprust::attribute_to_string);
}
fn reject_expr_parse(es: &str) {
assert!(expr(es).is_err(), "parser did not reject `{}`", es);
}
fn reject_stmt_parse(es: &str) {
assert!(stmt(es).is_err(), "parser did not reject `{}`", es);
}
fn main() {
let both = &["#[attr]", "#![attr]"];
let outer = &["#[attr]"];
let none = &[];
check_expr_attrs("#[attr] box 0", outer);
reject_expr_parse("box #![attr] 0");
check_expr_attrs("#[attr] 0 <- #[attr] 0", none);
check_expr_attrs("#[attr] (0 <- 0)", outer);
reject_expr_parse("0 #[attr] <- 0");
reject_expr_parse("0 <- #![attr] 0");
check_expr_attrs("in #[attr] 0 {#[attr] 0}", none);
check_expr_attrs("#[attr] (in 0 {0})", outer);
reject_expr_parse("in 0 #[attr] {0}");
reject_expr_parse("in 0 {#![attr] 0}");
check_expr_attrs("#[attr] [#![attr]]", both);
check_expr_attrs("#[attr] [#![attr] 0]", both);
check_expr_attrs("#[attr] [#![attr] 0; 0]", both);
check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both);
reject_expr_parse("[#[attr]]");
check_expr_attrs("#[attr] foo()", outer);
check_expr_attrs("#[attr] x.foo()", outer);
reject_expr_parse("foo#[attr]()");
reject_expr_parse("foo(#![attr])");
reject_expr_parse("x.foo(#![attr])");
reject_expr_parse("x.#[attr]foo()");
reject_expr_parse("x.#![attr]foo()");
check_expr_attrs("#[attr] (#![attr])", both);
check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both);
check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both);
check_expr_attrs("#[attr] 0 + #[attr] 0", none);
check_expr_attrs("#[attr] 0 / #[attr] 0", none);
check_expr_attrs("#[attr] 0 & #[attr] 0", none);
check_expr_attrs("#[attr] 0 % #[attr] 0", none);
check_expr_attrs("#[attr] (0 + 0)", outer);
reject_expr_parse("0 + #![attr] 0");
check_expr_attrs("#[attr] !0", outer);
check_expr_attrs("#[attr] -0", outer);
reject_expr_parse("!#![attr] 0");
reject_expr_parse("-#![attr] 0");
check_expr_attrs("#[attr] false", outer);
check_expr_attrs("#[attr] 0", outer);
check_expr_attrs("#[attr] 'c'", outer);
check_expr_attrs("#[attr] x as Y", none);
check_expr_attrs("#[attr] (x as Y)", outer);
reject_expr_parse("x #![attr] as Y");
reject_expr_parse("#[attr] if false {}");
reject_expr_parse("if false #[attr] {}");
reject_expr_parse("if false {#![attr]}");
reject_expr_parse("if false {} #[attr] else {}");
reject_expr_parse("if false {} else #[attr] {}");
reject_expr_parse("if false {} else {#![attr]}");
reject_expr_parse("if false {} else #[attr] if true {}");
reject_expr_parse("if false {} else if true #[attr] {}");
reject_expr_parse("if false {} else if true {#![attr]}");
reject_expr_parse("#[attr] if let Some(false) = false {}");
reject_expr_parse("if let Some(false) = false #[attr] {}");
reject_expr_parse("if let Some(false) = false {#![attr]}");
reject_expr_parse("if let Some(false) = false {} #[attr] else {}");
reject_expr_parse("if let Some(false) = false {} else #[attr] {}");
reject_expr_parse("if let Some(false) = false {} else {#![attr]}");
reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}");
reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}");
reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}");
check_expr_attrs("#[attr] while true {#![attr]}", both);
check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both);
check_expr_attrs("#[attr] for x in y {#![attr]}", both);
check_expr_attrs("#[attr] loop {#![attr]}", both);
check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both);
check_expr_attrs("#[attr] || #[attr] foo", outer);
check_expr_attrs("#[attr] move || #[attr] foo", outer);
check_expr_attrs("#[attr] || #[attr] { #![attr] foo }", outer);
check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer);
check_expr_attrs("#[attr] || { #![attr] foo }", outer);
check_expr_attrs("#[attr] move || { #![attr] foo }", outer);
reject_expr_parse("|| #![attr] foo");
reject_expr_parse("move || #![attr] foo");
reject_expr_parse("|| #![attr] {foo}");
reject_expr_parse("move || #![attr] {foo}");
check_expr_attrs("#[attr] { #![attr] }", both);
check_expr_attrs("#[attr] { #![attr] let _ = (); }", both);
check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both);
check_expr_attrs("#[attr] x = y", none);
check_expr_attrs("#[attr] (x = y)", outer);
check_expr_attrs("#[attr] x += y", none);
check_expr_attrs("#[attr] (x += y)", outer);
check_expr_attrs("#[attr] foo.bar", outer);
check_expr_attrs("(#[attr] foo).bar", none);
check_expr_attrs("#[attr] foo.0", outer);
check_expr_attrs("(#[attr] foo).0", none);
check_expr_attrs("#[attr] foo[bar]", outer);
check_expr_attrs("(#[attr] foo)[bar]", none);
check_expr_attrs("#[attr] 0..#[attr] 0", none);
check_expr_attrs("#[attr] 0..", none);
reject_expr_parse("#[attr] ..#[attr] 0");
reject_expr_parse("#[attr] ..");
check_expr_attrs("#[attr] (0..0)", outer);
check_expr_attrs("#[attr] (0..)", outer);
check_expr_attrs("#[attr] (..0)", outer);
check_expr_attrs("#[attr] (..)", outer);
check_expr_attrs("#[attr] foo::bar::baz", outer);
check_expr_attrs("#[attr] &0", outer);
check_expr_attrs("#[attr] &mut 0", outer);
check_expr_attrs("#[attr] & #[attr] 0", outer);
check_expr_attrs("#[attr] &mut #[attr] 0", outer);
reject_expr_parse("#[attr] &#![attr] 0");
reject_expr_parse("#[attr] &mut #![attr] 0");
check_expr_attrs("#[attr] break", outer);
check_expr_attrs("#[attr] continue", outer);
check_expr_attrs("#[attr] return", outer);
check_expr_attrs("#[attr] foo!()", outer);
check_expr_attrs("#[attr] foo!(#![attr])", outer);
check_expr_attrs("#[attr] foo![]", outer);
check_expr_attrs("#[attr] foo![#![attr]]", outer);
check_expr_attrs("#[attr] foo!{}", outer);
check_expr_attrs("#[attr] foo!{#![attr]}", outer);
check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both);
check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both);
check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both);
check_expr_attrs("#[attr] (#![attr] 0)", both);
// Look at statements in their natural habitat...
check_expr_attrs("{
#[attr] let _ = 0;
#[attr] 0;
#[attr] foo!();
#[attr] foo!{}
#[attr] foo![];
}", none);
check_stmt_attrs("#[attr] let _ = 0", outer);
check_stmt_attrs("#[attr] 0", outer);
check_stmt_attrs("#[attr] {#![attr]}", both);
check_stmt_attrs("#[attr] foo!()", outer);
check_stmt_attrs("#[attr] foo![]", outer);
check_stmt_attrs("#[attr] foo!{}", outer);
reject_stmt_parse("#[attr] #![attr] let _ = 0");
reject_stmt_parse("#[attr] #![attr] 0");
reject_stmt_parse("#[attr] #![attr] foo!()");
reject_stmt_parse("#[attr] #![attr] foo![]");
reject_stmt_parse("#[attr] #![attr] foo!{}");
// FIXME: Allow attributes in pattern constexprs?
// would require parens in patterns to allow disambiguation...
reject_expr_parse("match 0 {
0...#[attr] 10 => ()
}");
reject_expr_parse("match 0 {
0...#[attr] -10 => ()
}");
reject_expr_parse("match 0 {
0...-#[attr] 10 => ()
}");
reject_expr_parse("match 0 {
0...#[attr] FOO => ()
}");
// make sure we don't catch this bug again...
reject_expr_parse("{
fn foo() {
#[attr];
}
}");
reject_expr_parse("{
fn foo() {
#[attr]
}
}");
}

View file

@ -0,0 +1,97 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(non_snake_case)]
fn main() {
let a = 413;
#[cfg(unset)]
let a = ();
assert_eq!(a, 413);
let mut b = 612;
#[cfg(unset)]
{
b = 1111;
}
assert_eq!(b, 612);
#[cfg(unset)]
undefined_fn();
#[cfg(unset)]
undefined_macro!();
#[cfg(unset)]
undefined_macro![];
#[cfg(unset)]
undefined_macro!{};
// pretty printer bug...
// #[cfg(unset)]
// undefined_macro!{}
let () = (#[cfg(unset)] 341,); // Should this also work on parens?
let t = (1, #[cfg(unset)] 3, 4);
assert_eq!(t, (1, 4));
let f = |_: u32, _: u32| ();
f(2, 1, #[cfg(unset)] 6);
let _: u32 = a.clone(#[cfg(unset)] undefined);
let _: [(); 0] = [#[cfg(unset)] 126];
let t = [#[cfg(unset)] 1, 2, 6];
assert_eq!(t, [2, 6]);
{
let r;
#[cfg(unset)]
(r = 5);
#[cfg(not(unset))]
(r = 10);
assert_eq!(r, 10);
}
// check that macro expanded code works
macro_rules! if_cfg {
($cfg:meta $ib:block else $eb:block) => {
{
let r;
#[cfg($cfg)]
(r = $ib);
#[cfg(not($cfg))]
(r = $eb);
r
}
}
}
let n = if_cfg!(unset {
413
} else {
612
});
assert_eq!((#[cfg(unset)] 1, #[cfg(not(unset))] 2), (2,));
assert_eq!(n, 612);
// check that lints work
#[allow(non_snake_case)]
let FOOBAR = {
fn SYLADEX() {}
};
#[allow(non_snake_case)]
{
fn CRUXTRUDER() {}
}
}