Add a cfg_attr syntax extension
This extends cfg-gating to attributes. ```rust #[cfg_attr(<cfg pattern>, <attr>)] ``` will expand to ```rust #[<attr>] ``` if the `<cfg pattern>` matches the current cfg environment, and nothing if it does not. The grammar for the cfg pattern has a simple recursive structure: * `value` and `key = "value"` are cfg patterns, * `not(<cfg pattern>)` is a cfg pattern and matches if `<cfg pattern>` does not. * `all(<cfg pattern>, ...)` is a cfg pattern and matches if all of the `<cfg pattern>`s do. * `any(<cfg pattern>, ...)` is a cfg pattern and matches if any of the `<cfg pattern>`s do. Examples: ```rust // only derive Show for assert_eq! in tests #[cfg_attr(test, deriving(Show))] struct Foo { ... } // only derive Show for assert_eq! in tests and debug builds #[cfg_attr(any(test, not(ndebug)), deriving(Show))] struct Foo { ... } // ignore a test in certain cases #[test] #[cfg_attr(all(not(target_os = "linux"), target_endian = "big"), ignore)] fn test_broken_thing() { ... } // Avoid duplication when fixing staging issues in rustc #[cfg_attr(not(stage0), lang="iter")] pub trait Iterator<T> { ... } ```
This commit is contained in:
parent
c8bafe0466
commit
e520bb1b2f
4 changed files with 117 additions and 0 deletions
|
@ -439,6 +439,8 @@ fn initial_syntax_expander_table() -> SyntaxEnv {
|
|||
syntax_expanders.insert(intern("cfg"),
|
||||
builtin_normal_expander(
|
||||
ext::cfg::expand_cfg));
|
||||
syntax_expanders.insert(intern("cfg_attr"),
|
||||
ItemModifier(ext::cfg_attr::expand));
|
||||
syntax_expanders.insert(intern("trace_macros"),
|
||||
builtin_normal_expander(
|
||||
ext::trace_macros::expand_trace_macros));
|
||||
|
|
59
src/libsyntax/ext/cfg_attr.rs
Normal file
59
src/libsyntax/ext/cfg_attr.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
use std::gc::{Gc, GC};
|
||||
|
||||
use ast;
|
||||
use attr;
|
||||
use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
|
||||
pub fn expand(cx: &mut ExtCtxt, sp: Span, mi: Gc<ast::MetaItem>, it: Gc<ast::Item>)
|
||||
-> Gc<ast::Item> {
|
||||
let (cfg, attr) = match mi.node {
|
||||
ast::MetaList(_, ref mis) if mis.len() == 2 => (mis[0], mis[1]),
|
||||
_ => {
|
||||
cx.span_err(sp, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
|
||||
return it;
|
||||
}
|
||||
};
|
||||
|
||||
let mut out = (*it).clone();
|
||||
if cfg_matches(cx, cfg) {
|
||||
out.attrs.push(cx.attribute(attr.span, attr));
|
||||
}
|
||||
|
||||
box(GC) out
|
||||
}
|
||||
|
||||
fn cfg_matches(cx: &mut ExtCtxt, cfg: Gc<ast::MetaItem>) -> bool {
|
||||
match cfg.node {
|
||||
ast::MetaList(ref pred, ref mis) if pred.get() == "any" =>
|
||||
mis.iter().any(|mi| cfg_matches(cx, *mi)),
|
||||
ast::MetaList(ref pred, ref mis) if pred.get() == "all" =>
|
||||
mis.iter().all(|mi| cfg_matches(cx, *mi)),
|
||||
ast::MetaList(ref pred, ref mis) if pred.get() == "not" => {
|
||||
if mis.len() != 1 {
|
||||
cx.span_err(cfg.span, format!("expected 1 value, got {}",
|
||||
mis.len()).as_slice());
|
||||
return false;
|
||||
}
|
||||
!cfg_matches(cx, mis[0])
|
||||
}
|
||||
ast::MetaList(ref pred, _) => {
|
||||
cx.span_err(cfg.span,
|
||||
format!("invalid predicate `{}`", pred).as_slice());
|
||||
false
|
||||
},
|
||||
ast::MetaWord(_) | ast::MetaNameValue(..) =>
|
||||
attr::contains(cx.cfg.as_slice(), cfg),
|
||||
}
|
||||
}
|
|
@ -83,6 +83,7 @@ pub mod ext {
|
|||
pub mod build;
|
||||
pub mod bytes;
|
||||
pub mod cfg;
|
||||
pub mod cfg_attr;
|
||||
pub mod concat;
|
||||
pub mod concat_idents;
|
||||
pub mod deriving;
|
||||
|
|
55
src/test/run-pass/cfg_attr.rs
Normal file
55
src/test/run-pass/cfg_attr.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2014 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:--cfg set1 --cfg set2
|
||||
#![allow(dead_code)]
|
||||
use std::fmt::Show;
|
||||
|
||||
struct NotShowable;
|
||||
|
||||
#[cfg_attr(set1, deriving(Show))]
|
||||
struct Set1;
|
||||
|
||||
#[cfg_attr(notset, deriving(Show))]
|
||||
struct Notset(NotShowable);
|
||||
|
||||
#[cfg_attr(not(notset), deriving(Show))]
|
||||
struct NotNotset;
|
||||
|
||||
#[cfg_attr(not(set1), deriving(Show))]
|
||||
struct NotSet1(NotShowable);
|
||||
|
||||
#[cfg_attr(all(set1, set2), deriving(Show))]
|
||||
struct AllSet1Set2;
|
||||
|
||||
#[cfg_attr(all(set1, notset), deriving(Show))]
|
||||
struct AllSet1Notset(NotShowable);
|
||||
|
||||
#[cfg_attr(any(set1, notset), deriving(Show))]
|
||||
struct AnySet1Notset;
|
||||
|
||||
#[cfg_attr(any(notset, notset2), deriving(Show))]
|
||||
struct AnyNotsetNotset2(NotShowable);
|
||||
|
||||
#[cfg_attr(all(not(notset), any(set1, notset)), deriving(Show))]
|
||||
struct Complex;
|
||||
|
||||
#[cfg_attr(any(notset, not(any(set1, notset))), deriving(Show))]
|
||||
struct ComplexNot(NotShowable);
|
||||
|
||||
fn is_show<T: Show>() {}
|
||||
|
||||
fn main() {
|
||||
is_show::<Set1>();
|
||||
is_show::<NotNotset>();
|
||||
is_show::<AllSet1Set2>();
|
||||
is_show::<AnySet1Notset>();
|
||||
is_show::<Complex>();
|
||||
}
|
Loading…
Add table
Reference in a new issue