//
// baz! should not use this definition unless foo is enabled.
- krate = time(time_passes, "configuration 1", move ||
- syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
+ let mut feature_gated_cfgs = vec![];
+ krate = time(time_passes, "configuration 1", ||
+ syntax::config::strip_unconfigured_items(sess.diagnostic(), krate,
+ &mut feature_gated_cfgs));
*sess.crate_types.borrow_mut() =
collect_crate_types(sess, &krate.attrs);
cfg,
macros,
syntax_exts,
+ &mut feature_gated_cfgs,
krate);
if cfg!(windows) {
env::set_var("PATH", &_old_path);
// strip again, in case expansion added anything with a #[cfg].
krate = time(time_passes, "configuration 2", ||
- syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
+ syntax::config::strip_unconfigured_items(sess.diagnostic(), krate,
+ &mut feature_gated_cfgs));
+
+ time(time_passes, "gated configuration checking", || {
+ let features = sess.features.borrow();
+ feature_gated_cfgs.sort();
+ feature_gated_cfgs.dedup();
+ for cfg in &feature_gated_cfgs {
+ cfg.check_and_emit(sess.diagnostic(), &features);
+ }
+ });
krate = time(time_passes, "maybe building test harness", ||
syntax::test::modify_for_testing(&sess.parse_sess,
use codemap::{Span, Spanned, spanned, dummy_spanned};
use codemap::BytePos;
use diagnostic::SpanHandler;
+use feature_gate::GatedCfg;
use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
use parse::token::{InternedString, intern_and_get_ident};
use parse::token;
}
/// Tests if a cfg-pattern matches the cfg set
-pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem) -> bool {
+pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem,
+ feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
match cfg.node {
ast::MetaList(ref pred, ref mis) if &pred[..] == "any" =>
- mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
+ mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
ast::MetaList(ref pred, ref mis) if &pred[..] == "all" =>
- mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi)),
+ mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => {
if mis.len() != 1 {
diagnostic.span_err(cfg.span, "expected 1 cfg-pattern");
return false;
}
- !cfg_matches(diagnostic, cfgs, &*mis[0])
+ !cfg_matches(diagnostic, cfgs, &*mis[0], feature_gated_cfgs)
}
ast::MetaList(ref pred, _) => {
diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred));
false
},
- ast::MetaWord(_) | ast::MetaNameValue(..) => contains(cfgs, cfg),
+ ast::MetaWord(_) | ast::MetaNameValue(..) => {
+ feature_gated_cfgs.extend(GatedCfg::gate(cfg));
+ contains(cfgs, cfg)
+ }
}
}
use attr::AttrMetaMethods;
use diagnostic::SpanHandler;
+use feature_gate::GatedCfg;
use fold::Folder;
use {ast, fold, attr};
use codemap::{Spanned, respan};
// 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) -> ast::Crate {
- let krate = process_cfg_attr(diagnostic, krate);
+pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate,
+ feature_gated_cfgs: &mut Vec<GatedCfg>)
+ -> ast::Crate
+{
+ let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs);
let config = krate.config.clone();
- strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs))
+ strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs))
}
impl<F> fold::Folder for Context<F> where F: FnMut(&[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<ast::MetaItem>], attrs: &[ast::Attribute]) -> bool {
+fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute],
+ feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
attrs.iter().all(|attr| {
let mis = match attr.node.value.node {
ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis,
return true;
}
- attr::cfg_matches(diagnostic, cfg, &*mis[0])
+ attr::cfg_matches(diagnostic, cfg, &*mis[0],
+ feature_gated_cfgs)
})
}
-struct CfgAttrFolder<'a> {
+struct CfgAttrFolder<'a, 'b> {
diag: &'a SpanHandler,
config: ast::CrateConfig,
+ feature_gated_cfgs: &'b mut Vec<GatedCfg>
}
// Process `#[cfg_attr]`.
-fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate) -> ast::Crate {
+fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate,
+ feature_gated_cfgs: &mut Vec<GatedCfg>) -> ast::Crate {
let mut fld = CfgAttrFolder {
diag: diagnostic,
config: krate.config.clone(),
+ feature_gated_cfgs: feature_gated_cfgs,
};
fld.fold_crate(krate)
}
-impl<'a> fold::Folder for CfgAttrFolder<'a> {
+impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
if !attr.check_name("cfg_attr") {
return fold::noop_fold_attribute(attr, self);
}
};
- if attr::cfg_matches(self.diag, &self.config[..], &cfg) {
+ if attr::cfg_matches(self.diag, &self.config[..], &cfg,
+ self.feature_gated_cfgs) {
Some(respan(mi.span, ast::Attribute_ {
id: attr::mk_attr_id(),
style: attr.node.style,
use ext;
use ext::expand;
use ext::tt::macro_rules;
+use feature_gate::GatedCfg;
use parse;
use parse::parser;
use parse::token;
pub backtrace: ExpnId,
pub ecfg: expand::ExpansionConfig<'a>,
pub crate_root: Option<&'static str>,
+ pub feature_gated_cfgs: &'a mut Vec<GatedCfg>,
pub mod_path: Vec<ast::Ident> ,
pub exported_macros: Vec<ast::MacroDef>,
impl<'a> ExtCtxt<'a> {
pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
- ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> {
+ ecfg: expand::ExpansionConfig<'a>,
+ feature_gated_cfgs: &'a mut Vec<GatedCfg>) -> ExtCtxt<'a> {
let env = initial_syntax_expander_table(&ecfg);
ExtCtxt {
parse_sess: parse_sess,
mod_path: Vec::new(),
ecfg: ecfg,
crate_root: None,
+ feature_gated_cfgs: feature_gated_cfgs,
exported_macros: Vec::new(),
syntax_env: env,
recursion_count: 0,
return DummyResult::expr(sp);
}
- let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &*cfg);
+ let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &*cfg,
+ cx.feature_gated_cfgs);
MacEager::expr(cx.expr_bool(sp, matches_cfg))
}
use codemap;
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute, CompilerExpansion};
use ext::base::*;
-use feature_gate::{self, Features};
+use feature_gate::{self, Features, GatedCfg};
use fold;
use fold::*;
use parse;
// these are the macros being imported to this crate:
imported_macros: Vec<ast::MacroDef>,
user_exts: Vec<NamedSyntaxExtension>,
+ feature_gated_cfgs: &mut Vec<GatedCfg>,
c: Crate) -> Crate {
- let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
+ let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg,
+ feature_gated_cfgs);
if std_inject::no_core(&c) {
cx.crate_root = None;
} else if std_inject::no_std(&c) {
src,
Vec::new(), &sess);
// should fail:
- expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
+ expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
}
// make sure that macros can't escape modules
"<test>".to_string(),
src,
Vec::new(), &sess);
- expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
+ expand_crate(&sess,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast);
}
// macro_use modules should allow macros to escape
"<test>".to_string(),
src,
Vec::new(), &sess);
- expand_crate(&sess, test_ecfg(), vec!(), vec!(), crate_ast);
+ expand_crate(&sess, test_ecfg(), vec!(), vec!(), &mut vec![], crate_ast);
}
fn expand_crate_str(crate_str: String) -> ast::Crate {
let ps = parse::ParseSess::new();
let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
// the cfg argument actually does matter, here...
- expand_crate(&ps,test_ecfg(),vec!(),vec!(),crate_ast)
+ expand_crate(&ps,test_ecfg(),vec!(),vec!(), &mut vec![], crate_ast)
}
// find the pat_ident paths in a crate
use parse::token::{self, InternedString};
use std::ascii::AsciiExt;
+use std::cmp;
// If you change this list without updating src/doc/reference.md, @cmr will be sad
// Don't ever remove anything from this list; set them to 'Removed'.
// allow `repr(simd)`, and importing the various simd intrinsics
("simd_basics", "1.3.0", Active),
+
+ // Allows cfg(target_feature = "...").
+ ("cfg_target_feature", "1.3.0", Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
("recursion_limit", CrateLevel),
];
+macro_rules! cfg_fn {
+ (|$x: ident| $e: expr) => {{
+ fn f($x: &Features) -> bool {
+ $e
+ }
+ f as fn(&Features) -> bool
+ }}
+}
+// cfg(...)'s that are feature gated
+const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] = &[
+ // (name in cfg, feature, function to check if the feature is enabled)
+ ("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
+];
+
+#[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 PartialOrd for GatedCfg {
+ fn partial_cmp(&self, other: &GatedCfg) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl GatedCfg {
+ pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
+ let name = cfg.name();
+ GATED_CFGS.iter()
+ .position(|info| info.0 == name)
+ .map(|idx| {
+ GatedCfg {
+ span: cfg.span,
+ index: idx
+ }
+ })
+ }
+ pub 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);
+ emit_feature_err(diagnostic, feature, self.span, &explain);
+ }
+ }
+}
+
+
#[derive(PartialEq, Copy, Clone, Debug)]
pub enum AttributeType {
/// Normal, builtin attribute that is consumed
pub static_recursion: bool,
pub default_type_parameter_fallback: bool,
pub type_macros: bool,
+ pub cfg_target_feature: bool,
}
impl Features {
static_recursion: false,
default_type_parameter_fallback: false,
type_macros: false,
+ cfg_target_feature: false,
}
}
}
static_recursion: cx.has_feature("static_recursion"),
default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
type_macros: cx.has_feature("type_macros"),
+ cfg_target_feature: cx.has_feature("cfg_target_feature"),
}
}
krate: ast::Crate,
cfg: &ast::CrateConfig,
sd: &diagnostic::SpanHandler) -> ast::Crate {
+ let mut feature_gated_cfgs = vec![];
let mut cx: TestCtxt = TestCtxt {
sess: sess,
span_diagnostic: sd,
ext_cx: ExtCtxt::new(sess, cfg.clone(),
- ExpansionConfig::default("test".to_string())),
+ ExpansionConfig::default("test".to_string()),
+ &mut feature_gated_cfgs),
path: Vec::new(),
testfns: Vec::new(),
reexport_test_harness_main: reexport_test_harness_main,
--- /dev/null
+// 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.
+
+#[cfg(target_feature = "x")] //~ ERROR `cfg(target_feature)` is experimental
+#[cfg_attr(target_feature = "x", x)] //~ ERROR `cfg(target_feature)` is experimental
+struct Foo(u64, u64);
+
+#[cfg(not(any(all(target_feature = "x"))))] //~ ERROR `cfg(target_feature)` is experimental
+fn foo() {}
+
+fn main() {
+ cfg!(target_feature = "x");
+ //~^ ERROR `cfg(target_feature)` is experimental and subject to change
+}
fn main() {
let ps = syntax::parse::ParseSess::new();
+ let mut feature_gated_cfgs = vec![];
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
- syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
+ syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
+ &mut feature_gated_cfgs);
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {
fn main() {
let ps = syntax::parse::ParseSess::new();
+ let mut feature_gated_cfgs = vec![];
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
- syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
+ syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
+ &mut feature_gated_cfgs);
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {