From 296c3613ca7ed1117788f4e56259d2e426e992d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Tue, 24 Nov 2015 14:56:20 +0100 Subject: [PATCH] Added stmt_expr_attribute feature gate --- src/doc/reference.md | 3 + src/libsyntax/attr.rs | 28 +- src/libsyntax/config.rs | 249 +++++++++++++++--- src/libsyntax/ext/base.rs | 6 +- src/libsyntax/ext/cfg.rs | 10 +- src/libsyntax/ext/expand.rs | 6 +- src/libsyntax/feature_gate.rs | 55 +++- .../stmt_expr_attrs_no_feature.rs | 151 +++++++++++ src/test/pretty/stmt_expr_attributes.rs | 1 + src/test/run-pass/cfg_stmt_expr.rs | 1 + .../run-pass/stmt_expr_attr_macro_parse.rs | 22 ++ 11 files changed, 469 insertions(+), 63 deletions(-) create mode 100644 src/test/compile-fail/stmt_expr_attrs_no_feature.rs create mode 100644 src/test/run-pass/stmt_expr_attr_macro_parse.rs diff --git a/src/doc/reference.md b/src/doc/reference.md index 80194ea27bf5d..98074f09441c4 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2368,6 +2368,9 @@ The currently implemented features of the reference compiler are: influence type inference. * - `braced_empty_structs` - Allows use of empty structs and enum variants with braces. +* - `stmt_expr_attributes` - Allows attributes on expressions and + non-item statements. + If a feature is promoted to a language feature, then all existing programs will start to receive compilation warnings about `#![feature]` directives which enabled the new feature (because the directive is no longer necessary). However, if a diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index d020ba8a07b38..c2b2d00689fbe 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -20,8 +20,9 @@ use ast::{Stmt, StmtDecl, StmtExpr, StmtMac, StmtSemi, DeclItem, DeclLocal}; use ast::{Expr, Item, Local, Decl}; use codemap::{Span, Spanned, spanned, dummy_spanned}; use codemap::BytePos; +use config::CfgDiag; use diagnostic::SpanHandler; -use feature_gate::GatedCfg; +use feature_gate::{GatedCfg, GatedCfgAttr}; use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; use parse::token::{InternedString, intern_and_get_ident}; use parse::token; @@ -358,26 +359,35 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool { } /// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P], cfg: &ast::MetaItem, - feature_gated_cfgs: &mut Vec) -> bool { +pub fn cfg_matches(cfgs: &[P], + cfg: &ast::MetaItem, + diag: &mut T) -> bool { match cfg.node { ast::MetaList(ref pred, ref mis) if &pred[..] == "any" => - mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)), + mis.iter().any(|mi| cfg_matches(cfgs, &**mi, diag)), ast::MetaList(ref pred, ref mis) if &pred[..] == "all" => - mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)), + mis.iter().all(|mi| cfg_matches(cfgs, &**mi, diag)), ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => { if mis.len() != 1 { - diagnostic.span_err(cfg.span, "expected 1 cfg-pattern"); + diag.emit_error(|diagnostic| { + diagnostic.span_err(cfg.span, "expected 1 cfg-pattern"); + }); return false; } - !cfg_matches(diagnostic, cfgs, &*mis[0], feature_gated_cfgs) + !cfg_matches(cfgs, &*mis[0], diag) } ast::MetaList(ref pred, _) => { - diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred)); + diag.emit_error(|diagnostic| { + diagnostic.span_err(cfg.span, + &format!("invalid predicate `{}`", pred)); + }); false }, ast::MetaWord(_) | ast::MetaNameValue(..) => { - feature_gated_cfgs.extend(GatedCfg::gate(cfg)); + diag.flag_gated(|feature_gated_cfgs| { + feature_gated_cfgs.extend( + GatedCfg::gate(cfg).map(GatedCfgAttr::GatedCfg)); + }); contains(cfgs, cfg) } } diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 0d59ff0fbabed..1209c58fd5ed1 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -10,9 +10,10 @@ use attr::AttrMetaMethods; use diagnostic::SpanHandler; -use feature_gate::GatedCfg; +use feature_gate::GatedCfgAttr; use fold::Folder; use {ast, fold, attr}; +use visit; use codemap::{Spanned, respan}; use ptr::P; @@ -28,20 +29,26 @@ struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { // Support conditional compilation by transforming the AST, stripping out // any items that do not belong in the current configuration pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate, - feature_gated_cfgs: &mut Vec) + feature_gated_cfgs: &mut Vec) -> ast::Crate { + // Need to do this check here because cfg runs before feature_gates + check_for_gated_stmt_expr_attributes(&krate, feature_gated_cfgs); + let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs); let config = krate.config.clone(); strip_items(diagnostic, krate, - |attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs)) + |attrs| { + let mut diag = CfgDiagReal { + diag: diagnostic, + feature_gated_cfgs: feature_gated_cfgs, + }; + in_cfg(&config, attrs, &mut diag) + }) } 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 { - fold_mod(self, module) - } fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { fold_foreign_mod(self, foreign_mod) } @@ -87,19 +94,6 @@ pub fn strip_items<'a, F>(diagnostic: &'a SpanHandler, ctxt.fold_crate(krate) } -fn fold_mod(cx: &mut Context, - ast::Mod {inner, items}: ast::Mod) - -> ast::Mod where - F: FnMut(&[ast::Attribute]) -> bool -{ - ast::Mod { - inner: inner, - items: items.into_iter().flat_map(|a| { - cx.fold_item(a).into_iter() - }).collect() - } -} - fn filter_foreign_item(cx: &mut Context, item: P) -> Option> where @@ -271,10 +265,9 @@ fn is_cfg(attr: &ast::Attribute) -> bool { // Determine if an item should be translated in the current crate // configuration based on the item's attributes -fn in_cfg(diagnostic: &SpanHandler, - cfg: &[P], - attrs: &[ast::Attribute], - feature_gated_cfgs: &mut Vec) -> bool { +fn in_cfg(cfg: &[P], + attrs: &[ast::Attribute], + diag: &mut T) -> bool { attrs.iter().all(|attr| { let mis = match attr.node.value.node { ast::MetaList(_, ref mis) if is_cfg(&attr) => mis, @@ -282,33 +275,35 @@ fn in_cfg(diagnostic: &SpanHandler, }; if mis.len() != 1 { - diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); + diag.emit_error(|diagnostic| { + diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); + }); return true; } - attr::cfg_matches(diagnostic, cfg, &mis[0], - feature_gated_cfgs) + attr::cfg_matches(cfg, &mis[0], diag) }) } -struct CfgAttrFolder<'a, 'b> { - diag: &'a SpanHandler, - config: ast::CrateConfig, - feature_gated_cfgs: &'b mut Vec +struct CfgAttrFolder<'a, T> { + diag: T, + config: &'a ast::CrateConfig, } // Process `#[cfg_attr]`. fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate, - feature_gated_cfgs: &mut Vec) -> ast::Crate { + feature_gated_cfgs: &mut Vec) -> ast::Crate { let mut fld = CfgAttrFolder { - diag: diagnostic, - config: krate.config.clone(), - feature_gated_cfgs: feature_gated_cfgs, + diag: CfgDiagReal { + diag: diagnostic, + feature_gated_cfgs: feature_gated_cfgs, + }, + config: &krate.config.clone(), }; fld.fold_crate(krate) } -impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> { +impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> { fn fold_attribute(&mut self, attr: ast::Attribute) -> Option { if !attr.check_name("cfg_attr") { return fold::noop_fold_attribute(attr, self); @@ -317,20 +312,25 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> { let attr_list = match attr.meta_item_list() { Some(attr_list) => attr_list, None => { - self.diag.span_err(attr.span, "expected `#[cfg_attr(, )]`"); + self.diag.emit_error(|diag| { + diag.span_err(attr.span, + "expected `#[cfg_attr(, )]`"); + }); return None; } }; let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) { (2, Some(cfg), Some(mi)) => (cfg, mi), _ => { - self.diag.span_err(attr.span, "expected `#[cfg_attr(, )]`"); + self.diag.emit_error(|diag| { + diag.span_err(attr.span, + "expected `#[cfg_attr(, )]`"); + }); return None; } }; - if attr::cfg_matches(self.diag, &self.config[..], &cfg, - self.feature_gated_cfgs) { + if attr::cfg_matches(&self.config[..], &cfg, &mut self.diag) { Some(respan(mi.span, ast::Attribute_ { id: attr::mk_attr_id(), style: attr.node.style, @@ -347,3 +347,174 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> { fold::noop_fold_mac(mac, self) } } + +fn check_for_gated_stmt_expr_attributes(krate: &ast::Crate, + discovered: &mut Vec) { + let mut v = StmtExprAttrFeatureVisitor { + config: &krate.config, + discovered: discovered, + }; + visit::walk_crate(&mut v, krate); +} + +/// To cover this feature, we need to discover all attributes +/// so we need to run before cfg. +struct StmtExprAttrFeatureVisitor<'a, 'b> { + config: &'a ast::CrateConfig, + discovered: &'b mut Vec, +} + +// Runs the cfg_attr and cfg folders locally in "silent" mode +// to discover attribute use on stmts or expressions ahead of time +impl<'v, 'a, 'b> visit::Visitor<'v> for StmtExprAttrFeatureVisitor<'a, 'b> { + fn visit_stmt(&mut self, s: &'v ast::Stmt) { + // check if there even are any attributes on this node + let stmt_attrs = s.node.attrs(); + if stmt_attrs.len() > 0 { + // attributes on items are fine + if let ast::StmtDecl(ref decl, _) = s.node { + if let ast::DeclItem(_) = decl.node { + visit::walk_stmt(self, s); + return; + } + } + + // flag the offending attributes + for attr in stmt_attrs { + self.discovered.push(GatedCfgAttr::GatedAttr(attr.span)); + } + + // if the node does not end up being cfg-d away, walk down + if node_survives_cfg(stmt_attrs, self.config) { + visit::walk_stmt(self, s); + } + } else { + visit::walk_stmt(self, s); + } + } + + fn visit_expr(&mut self, ex: &'v ast::Expr) { + // check if there even are any attributes on this node + let expr_attrs = ex.attrs(); + if expr_attrs.len() > 0 { + + // flag the offending attributes + for attr in expr_attrs { + self.discovered.push(GatedCfgAttr::GatedAttr(attr.span)); + } + + // if the node does not end up being cfg-d away, walk down + if node_survives_cfg(expr_attrs, self.config) { + visit::walk_expr(self, ex); + } + } else { + visit::walk_expr(self, ex); + } + } + + fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) { + if node_survives_cfg(&i.attrs, self.config) { + visit::walk_foreign_item(self, i); + } + } + + fn visit_item(&mut self, i: &'v ast::Item) { + if node_survives_cfg(&i.attrs, self.config) { + visit::walk_item(self, i); + } + } + + fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { + if node_survives_cfg(&ii.attrs, self.config) { + visit::walk_impl_item(self, ii); + } + } + + fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) { + if node_survives_cfg(&ti.attrs, self.config) { + visit::walk_trait_item(self, ti); + } + } + + fn visit_struct_field(&mut self, s: &'v ast::StructField) { + if node_survives_cfg(&s.node.attrs, self.config) { + visit::walk_struct_field(self, s); + } + } + + fn visit_variant(&mut self, v: &'v ast::Variant, + g: &'v ast::Generics, item_id: ast::NodeId) { + if node_survives_cfg(&v.node.attrs, self.config) { + visit::walk_variant(self, v, g, item_id); + } + } + + fn visit_arm(&mut self, a: &'v ast::Arm) { + if node_survives_cfg(&a.attrs, self.config) { + visit::walk_arm(self, a); + } + } + + // This visitor runs pre expansion, so we need to prevent + // the default panic here + fn visit_mac(&mut self, mac: &'v ast::Mac) { + visit::walk_mac(self, mac) + } +} + +pub trait CfgDiag { + fn emit_error(&mut self, f: F) where F: FnMut(&SpanHandler); + fn flag_gated(&mut self, f: F) where F: FnMut(&mut Vec); +} + +pub struct CfgDiagReal<'a, 'b> { + pub diag: &'a SpanHandler, + pub feature_gated_cfgs: &'b mut Vec, +} + +impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> { + fn emit_error(&mut self, mut f: F) where F: FnMut(&SpanHandler) { + f(self.diag) + } + fn flag_gated(&mut self, mut f: F) where F: FnMut(&mut Vec) { + f(self.feature_gated_cfgs) + } +} + +struct CfgDiagSilent { + error: bool, +} + +impl CfgDiag for CfgDiagSilent { + fn emit_error(&mut self, _: F) where F: FnMut(&SpanHandler) { + self.error = true; + } + fn flag_gated(&mut self, _: F) where F: FnMut(&mut Vec) {} +} + +fn node_survives_cfg(attrs: &[ast::Attribute], + config: &ast::CrateConfig) -> bool { + let mut survives_cfg = true; + + for attr in attrs { + let mut fld = CfgAttrFolder { + diag: CfgDiagSilent { error: false }, + config: config, + }; + let attr = fld.fold_attribute(attr.clone()); + + // In case of error we can just return true, + // since the actual cfg folders will end compilation anyway. + + if fld.diag.error { return true; } + + survives_cfg &= attr.map(|attr| { + let mut diag = CfgDiagSilent { error: false }; + let r = in_cfg(config, &[attr], &mut diag); + if diag.error { return true; } + r + }).unwrap_or(true) + } + + survives_cfg +} diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 8c93327c322b2..41a4fc9e1dfa4 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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; @@ -572,7 +572,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, + pub feature_gated_cfgs: &'a mut Vec, pub mod_path: Vec , pub exported_macros: Vec, @@ -584,7 +584,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) -> ExtCtxt<'a> { + feature_gated_cfgs: &'a mut Vec) -> ExtCtxt<'a> { let env = initial_syntax_expander_table(&ecfg); ExtCtxt { parse_sess: parse_sess, diff --git a/src/libsyntax/ext/cfg.rs b/src/libsyntax/ext/cfg.rs index 48199026204c8..e100355e4f868 100644 --- a/src/libsyntax/ext/cfg.rs +++ b/src/libsyntax/ext/cfg.rs @@ -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)) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 908b2bd24293e..1512cab9498f0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -21,7 +21,7 @@ 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; @@ -586,7 +586,7 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE // also, don't forget to expand the init: init: init.map(|e| fld.fold_expr(e)), span: span, - attrs: attrs + attrs: fold::fold_thin_attrs(attrs, fld), } }); SmallVector::one(P(Spanned { @@ -1280,7 +1280,7 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess, // these are the macros being imported to this crate: imported_macros: Vec, user_exts: Vec, - feature_gated_cfgs: &mut Vec, + feature_gated_cfgs: &mut Vec, c: Crate) -> (Crate, HashSet) { let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg, feature_gated_cfgs); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index b450331d44036..2c1dc638fbc1c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -224,6 +224,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // Allows cfg(target_vendor = "..."). ("cfg_target_vendor", "1.5.0", Some(29718), Active), + + // Allow attributes on expressions and non-item statements + ("stmt_expr_attributes", "1.6.0", Some(15701), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -404,25 +407,57 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] ("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)), ]; +#[derive(Debug, Eq, PartialEq)] +pub enum GatedCfgAttr { + GatedCfg(GatedCfg), + GatedAttr(Span), +} + #[derive(Debug, Eq, PartialEq)] pub struct GatedCfg { span: Span, index: usize, } -impl Ord for GatedCfg { - fn cmp(&self, other: &GatedCfg) -> cmp::Ordering { - (self.span.lo.0, self.span.hi.0, self.index) - .cmp(&(other.span.lo.0, other.span.hi.0, other.index)) +impl Ord for GatedCfgAttr { + fn cmp(&self, other: &GatedCfgAttr) -> cmp::Ordering { + let to_tup = |s: &GatedCfgAttr| match *s { + GatedCfgAttr::GatedCfg(ref gated_cfg) => { + (gated_cfg.span.lo.0, gated_cfg.span.hi.0, gated_cfg.index) + } + GatedCfgAttr::GatedAttr(ref span) => { + (span.lo.0, span.hi.0, GATED_CFGS.len()) + } + }; + to_tup(self).cmp(&to_tup(other)) } } -impl PartialOrd for GatedCfg { - fn partial_cmp(&self, other: &GatedCfg) -> Option { +impl PartialOrd for GatedCfgAttr { + fn partial_cmp(&self, other: &GatedCfgAttr) -> Option { Some(self.cmp(other)) } } +impl GatedCfgAttr { + pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { + match *self { + GatedCfgAttr::GatedCfg(ref cfg) => { + cfg.check_and_emit(diagnostic, features); + } + GatedCfgAttr::GatedAttr(span) => { + if !features.stmt_expr_attributes { + emit_feature_err(diagnostic, + "stmt_expr_attributes", + span, + GateIssue::Language, + EXPLAIN_STMT_ATTR_SYNTAX); + } + } + } + } +} + impl GatedCfg { pub fn gate(cfg: &ast::MetaItem) -> Option { let name = cfg.name(); @@ -435,7 +470,7 @@ impl GatedCfg { } }) } - pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { + fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { let (cfg, feature, has_feature) = GATED_CFGS[self.index]; if !has_feature(features) { let explain = format!("`cfg({})` is experimental and subject to change", cfg); @@ -500,6 +535,7 @@ pub struct Features { pub augmented_assignments: bool, pub braced_empty_structs: bool, pub staged_api: bool, + pub stmt_expr_attributes: bool, } impl Features { @@ -532,6 +568,7 @@ impl Features { augmented_assignments: false, braced_empty_structs: false, staged_api: false, + stmt_expr_attributes: false, } } } @@ -545,6 +582,9 @@ const EXPLAIN_PLACEMENT_IN: &'static str = const EXPLAIN_PUSHPOP_UNSAFE: &'static str = "push/pop_unsafe macros are experimental and subject to change."; +const EXPLAIN_STMT_ATTR_SYNTAX: &'static str = + "attributes on non-item statements and expressions are experimental."; + pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) { if let Some(&Features { allow_box: true, .. }) = f { return; @@ -1105,6 +1145,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, augmented_assignments: cx.has_feature("augmented_assignments"), braced_empty_structs: cx.has_feature("braced_empty_structs"), staged_api: cx.has_feature("staged_api"), + stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"), } } diff --git a/src/test/compile-fail/stmt_expr_attrs_no_feature.rs b/src/test/compile-fail/stmt_expr_attrs_no_feature.rs new file mode 100644 index 0000000000000..68338b115950c --- /dev/null +++ b/src/test/compile-fail/stmt_expr_attrs_no_feature.rs @@ -0,0 +1,151 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(custom_attribute)] +#![feature(associated_consts)] + +macro_rules! stmt_mac { + () => { + fn b() {} + } +} + +fn main() { + #[attr] + fn a() {} + + #[attr] //~ ERROR 15701 + { + + } + + #[attr] //~ ERROR 15701 + 5; + + #[attr] //~ ERROR 15701 + stmt_mac!(); +} + +// Check that cfg works right + +#[cfg(unset)] +fn c() { + #[attr] + 5; +} + +#[cfg(not(unset))] +fn j() { + #[attr] //~ ERROR 15701 + 5; +} + +#[cfg_attr(not(unset), cfg(unset))] +fn d() { + #[attr] + 8; +} + +#[cfg_attr(not(unset), cfg(not(unset)))] +fn i() { + #[attr] //~ ERROR 15701 + 8; +} + +// check that macro expansion and cfg works right + +macro_rules! item_mac { + ($e:ident) => { + fn $e() { + #[attr] //~ ERROR 15701 + 42; + + #[cfg(unset)] + fn f() { + #[attr] + 5; + } + + #[cfg(not(unset))] + fn k() { + #[attr] //~ ERROR 15701 + 5; + } + + #[cfg_attr(not(unset), cfg(unset))] + fn g() { + #[attr] + 8; + } + + #[cfg_attr(not(unset), cfg(not(unset)))] + fn h() { + #[attr] //~ ERROR 15701 + 8; + } + + } + } +} + +item_mac!(e); + +// check that the gate visitor works right: + +extern { + #[cfg(unset)] + fn x(a: [u8; #[attr] 5]); + fn y(a: [u8; #[attr] 5]); //~ ERROR 15701 +} + +struct Foo; +impl Foo { + #[cfg(unset)] + const X: u8 = #[attr] 5; + const Y: u8 = #[attr] 5; //~ ERROR 15701 +} + +trait Bar { + #[cfg(unset)] + const X: [u8; #[attr] 5]; + const Y: [u8; #[attr] 5]; //~ ERROR 15701 +} + +struct Joyce { + #[cfg(unset)] + field: [u8; #[attr] 5], + field2: [u8; #[attr] 5] //~ ERROR 15701 +} + +struct Walky( + #[cfg(unset)] [u8; #[attr] 5], + [u8; #[attr] 5] //~ ERROR 15701 +); + +enum Mike { + Happy( + #[cfg(unset)] [u8; #[attr] 5], + [u8; #[attr] 5] //~ ERROR 15701 + ), + Angry { + #[cfg(unset)] + field: [u8; #[attr] 5], + field2: [u8; #[attr] 5] //~ ERROR 15701 + } +} + +fn pat() { + match 5 { + #[cfg(unset)] + 5 => #[attr] (), + 6 => #[attr] (), //~ ERROR 15701 + _ => (), + } +} diff --git a/src/test/pretty/stmt_expr_attributes.rs b/src/test/pretty/stmt_expr_attributes.rs index 48c2a0470e14e..e52932cd7befa 100644 --- a/src/test/pretty/stmt_expr_attributes.rs +++ b/src/test/pretty/stmt_expr_attributes.rs @@ -13,6 +13,7 @@ #![feature(custom_attribute)] #![feature(box_syntax)] #![feature(placement_in_syntax)] +#![feature(stmt_expr_attributes)] fn main() { } diff --git a/src/test/run-pass/cfg_stmt_expr.rs b/src/test/run-pass/cfg_stmt_expr.rs index f2b2a567b044d..fcc93557665bf 100644 --- a/src/test/run-pass/cfg_stmt_expr.rs +++ b/src/test/run-pass/cfg_stmt_expr.rs @@ -9,6 +9,7 @@ // except according to those terms. #![deny(non_snake_case)] +#![feature(stmt_expr_attributes)] fn main() { let a = 413; diff --git a/src/test/run-pass/stmt_expr_attr_macro_parse.rs b/src/test/run-pass/stmt_expr_attr_macro_parse.rs new file mode 100644 index 0000000000000..e2d70fc54b228 --- /dev/null +++ b/src/test/run-pass/stmt_expr_attr_macro_parse.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! m { + ($e:expr) => { + "expr includes attr" + }; + (#[$attr:meta] $e:expr) => { + "expr excludes attr" + } +} + +fn main() { + assert_eq!(m!(#[attr] 1 + 1), "expr includes attr"); +}