Skip to content

Commit e520bb1

Browse files
committed
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> { ... } ```
1 parent c8bafe0 commit e520bb1

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

src/libsyntax/ext/base.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,8 @@ fn initial_syntax_expander_table() -> SyntaxEnv {
439439
syntax_expanders.insert(intern("cfg"),
440440
builtin_normal_expander(
441441
ext::cfg::expand_cfg));
442+
syntax_expanders.insert(intern("cfg_attr"),
443+
ItemModifier(ext::cfg_attr::expand));
442444
syntax_expanders.insert(intern("trace_macros"),
443445
builtin_normal_expander(
444446
ext::trace_macros::expand_trace_macros));

src/libsyntax/ext/cfg_attr.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::gc::{Gc, GC};
12+
13+
use ast;
14+
use attr;
15+
use codemap::Span;
16+
use ext::base::ExtCtxt;
17+
use ext::build::AstBuilder;
18+
19+
pub fn expand(cx: &mut ExtCtxt, sp: Span, mi: Gc<ast::MetaItem>, it: Gc<ast::Item>)
20+
-> Gc<ast::Item> {
21+
let (cfg, attr) = match mi.node {
22+
ast::MetaList(_, ref mis) if mis.len() == 2 => (mis[0], mis[1]),
23+
_ => {
24+
cx.span_err(sp, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
25+
return it;
26+
}
27+
};
28+
29+
let mut out = (*it).clone();
30+
if cfg_matches(cx, cfg) {
31+
out.attrs.push(cx.attribute(attr.span, attr));
32+
}
33+
34+
box(GC) out
35+
}
36+
37+
fn cfg_matches(cx: &mut ExtCtxt, cfg: Gc<ast::MetaItem>) -> bool {
38+
match cfg.node {
39+
ast::MetaList(ref pred, ref mis) if pred.get() == "any" =>
40+
mis.iter().any(|mi| cfg_matches(cx, *mi)),
41+
ast::MetaList(ref pred, ref mis) if pred.get() == "all" =>
42+
mis.iter().all(|mi| cfg_matches(cx, *mi)),
43+
ast::MetaList(ref pred, ref mis) if pred.get() == "not" => {
44+
if mis.len() != 1 {
45+
cx.span_err(cfg.span, format!("expected 1 value, got {}",
46+
mis.len()).as_slice());
47+
return false;
48+
}
49+
!cfg_matches(cx, mis[0])
50+
}
51+
ast::MetaList(ref pred, _) => {
52+
cx.span_err(cfg.span,
53+
format!("invalid predicate `{}`", pred).as_slice());
54+
false
55+
},
56+
ast::MetaWord(_) | ast::MetaNameValue(..) =>
57+
attr::contains(cx.cfg.as_slice(), cfg),
58+
}
59+
}

src/libsyntax/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ pub mod ext {
8383
pub mod build;
8484
pub mod bytes;
8585
pub mod cfg;
86+
pub mod cfg_attr;
8687
pub mod concat;
8788
pub mod concat_idents;
8889
pub mod deriving;

src/test/run-pass/cfg_attr.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags:--cfg set1 --cfg set2
12+
#![allow(dead_code)]
13+
use std::fmt::Show;
14+
15+
struct NotShowable;
16+
17+
#[cfg_attr(set1, deriving(Show))]
18+
struct Set1;
19+
20+
#[cfg_attr(notset, deriving(Show))]
21+
struct Notset(NotShowable);
22+
23+
#[cfg_attr(not(notset), deriving(Show))]
24+
struct NotNotset;
25+
26+
#[cfg_attr(not(set1), deriving(Show))]
27+
struct NotSet1(NotShowable);
28+
29+
#[cfg_attr(all(set1, set2), deriving(Show))]
30+
struct AllSet1Set2;
31+
32+
#[cfg_attr(all(set1, notset), deriving(Show))]
33+
struct AllSet1Notset(NotShowable);
34+
35+
#[cfg_attr(any(set1, notset), deriving(Show))]
36+
struct AnySet1Notset;
37+
38+
#[cfg_attr(any(notset, notset2), deriving(Show))]
39+
struct AnyNotsetNotset2(NotShowable);
40+
41+
#[cfg_attr(all(not(notset), any(set1, notset)), deriving(Show))]
42+
struct Complex;
43+
44+
#[cfg_attr(any(notset, not(any(set1, notset))), deriving(Show))]
45+
struct ComplexNot(NotShowable);
46+
47+
fn is_show<T: Show>() {}
48+
49+
fn main() {
50+
is_show::<Set1>();
51+
is_show::<NotNotset>();
52+
is_show::<AllSet1Set2>();
53+
is_show::<AnySet1Notset>();
54+
is_show::<Complex>();
55+
}

0 commit comments

Comments
 (0)