Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a cfg_attr syntax extension #16230

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/doc/guide-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ is not used.
Tests that should not be run can be annotated with the `ignore`
attribute. The existence of these tests will be noted in the test
runner output, but the test will not be run. Tests can also be ignored
by configuration so, for example, to ignore a test on windows you can
write `#[ignore(cfg(target_os = "win32"))]`.
by configuration using the `cfg_attr` attribute so, for example, to ignore a
test on windows you can write `#[cfg_attr(windows, ignore)]`.

Tests that are intended to fail can be annotated with the
`should_fail` attribute. The test will be run, and if it causes its
Expand Down
2 changes: 1 addition & 1 deletion src/libnative/io/file_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ mod tests {
use std::os;
use std::rt::rtio::{RtioFileStream, SeekSet};

#[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer
#[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer
#[test]
fn test_file_desc() {
// Run this test with some pipes so we don't have to mess around with
Expand Down
2 changes: 1 addition & 1 deletion src/libnum/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ mod test {
}

#[test]
#[ignore(cfg(target_arch = "x86"))]
#[cfg_attr(target_arch = "x86", ignore)]
// FIXME #7158: (maybe?) currently failing on x86.
fn test_norm() {
fn test(c: Complex64, ns: f64) {
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/front/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,12 @@ fn is_ignored(cx: &TestCtxt, i: Gc<ast::Item>) -> bool {
// check ignore(cfg(foo, bar))
attr.check_name("ignore") && match attr.meta_item_list() {
Some(ref cfgs) => {
if cfgs.iter().any(|cfg| cfg.check_name("cfg")) {
cx.sess.span_warn(attr.span,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use cfg.span here?

"The use of cfg filters in #[ignore] is \
deprecated. Use #[cfg_attr(<cfg pattern>, \
ignore)] instead.");
}
attr::test_cfg(cx.config.as_slice(), cfgs.iter().map(|x| *x))
}
None => true
Expand Down
2 changes: 1 addition & 1 deletion src/librustuv/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,7 @@ mod test {
}

#[test]
#[ignore(cfg(windows))] // FIXME(#10102) server never sees second packet
#[cfg_attr(windows, ignore)] // FIXME(#10102) server never sees second packet
fn test_udp_twice() {
let server_addr = ::next_test_ip4();
let client_addr = ::next_test_ip4();
Expand Down
6 changes: 3 additions & 3 deletions src/libserialize/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3005,7 +3005,7 @@ mod tests {
}
}
#[test]
#[ignore(cfg(target_word_size = "32"))] // FIXME(#14064)
#[cfg_attr(target_word_size = "32", ignore)] // FIXME(#14064)
fn test_streaming_parser() {
assert_stream_equal(
r#"{ "foo":"bar", "array" : [0, 1, 2,3 ,4,5], "idents":[null,true,false]}"#,
Expand Down Expand Up @@ -3040,7 +3040,7 @@ mod tests {
}
}
#[test]
#[ignore(cfg(target_word_size = "32"))] // FIXME(#14064)
#[cfg_attr(target_word_size = "32", ignore)] // FIXME(#14064)
fn test_read_object_streaming() {
assert_eq!(last_event("{ "), Error(SyntaxError(EOFWhileParsingObject, 1, 3)));
assert_eq!(last_event("{1"), Error(SyntaxError(KeyMustBeAString, 1, 2)));
Expand Down Expand Up @@ -3112,7 +3112,7 @@ mod tests {
);
}
#[test]
#[ignore(cfg(target_word_size = "32"))] // FIXME(#14064)
#[cfg_attr(target_word_size = "32", ignore)] // FIXME(#14064)
fn test_read_list_streaming() {
assert_stream_equal(
"[]",
Expand Down
3 changes: 1 addition & 2 deletions src/libstd/dynamic_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ mod test {
use mem;

#[test]
#[ignore(cfg(windows))] // FIXME #8818
#[ignore(cfg(target_os="android"))] // FIXME(#10379)
#[cfg_attr(any(windows, target_os = "android"), ignore)] // FIXME #8818, #10379
fn test_loading_cosine() {
// The math library does not need to be loaded since it is already
// statically linked in
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/io/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ mod test {
Ok(..) => fail!(),
Err(e) => assert_eq!(e.kind, PermissionDenied),
}
} #[ignore(cfg(windows))] #[ignore(cfg(target_os = "android"))])
} #[cfg_attr(any(windows, target_os = "android"), ignore)])

iotest!(fn connect_error() {
match TcpStream::connect("0.0.0.0", 1) {
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/io/net/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ mod test {
Ok(..) => fail!(),
Err(e) => assert_eq!(e.kind, PermissionDenied),
}
} #[ignore(cfg(windows))] #[ignore(cfg(target_os = "android"))])
} #[cfg_attr(any(windows, target_os = "android"), ignore)])

iotest!(fn socket_smoke_test_ip4() {
let server_ip = next_test_ip4();
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/io/net/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ mod tests {
}, proc(_client) {
// drop the client
})
} #[ignore(cfg(windows))]) // FIXME(#12516)
} #[cfg_attr(windows, ignore)]) // FIXME(#12516)

iotest!(fn write_begone() {
smalltest(proc(mut server) {
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ mod tests {
assert_eq!((-0f32).frexp(), (-0f32, 0));
}

#[test] #[ignore(cfg(windows))] // FIXME #8755
#[test] #[cfg_attr(windows, ignore)] // FIXME #8755
fn test_frexp_nowin() {
let inf: f32 = Float::infinity();
let neg_inf: f32 = Float::neg_infinity();
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/num/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ mod tests {
assert_eq!((-0f64).frexp(), (-0f64, 0));
}

#[test] #[ignore(cfg(windows))] // FIXME #8755
#[test] #[cfg_attr(windows, ignore)] // FIXME #8755
fn test_frexp_nowin() {
let inf: f64 = Float::infinity();
let neg_inf: f64 = Float::neg_infinity();
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ mod tests {
}

#[test]
#[ignore(cfg(windows))] // apparently windows scheduling is weird?
#[cfg_attr(windows, ignore)] // apparently windows scheduling is weird?
fn no_starvation() {
static AMT: int = 10000;
static NTHREADS: int = 4;
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,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));
Expand Down
58 changes: 58 additions & 0 deletions src/libsyntax/ext/cfg_attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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;
}
};

if cfg_matches(cx, cfg) {
let mut out = (*it).clone();
out.attrs.push(cx.attribute(attr.span, attr));
box(GC) out
} else {
it
}
}

fn cfg_matches(cx: &mut ExtCtxt, cfg: Gc<ast::MetaItem>) -> bool {
match cfg.node {
ast::MetaList(ref pred, ref mis) => {
match pred.get() {
"any" => mis.iter().any(|mi| cfg_matches(cx, *mi)),
"all" => mis.iter().all(|mi| cfg_matches(cx, *mi)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a little odd to introduce a new sub-language for cfg_attr that isn't present in cfg. Couldn't any be emulated by multiple #[cfg_attr] and could many clauses be allowed to emulate all?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. For one it's error prone, as I can see code easily ending up out of sync like #[cfg_attr(a, deriving(Show, Clone, Eq))] #[cfg_attr(b, deriving(Show, Clone))]. It will also double-define the attribute if both a and b are set.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc #2119

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcrichton I like this proposal because the new sub-language makes a lot more sense than our current #[cfg] behavior, and it provides a model that we can use to modify #[cfg] to match in the future.

"not" if mis.len() == 1 => !cfg_matches(cx, mis[0]),
"not" => {
cx.span_err(cfg.span,
format!("expected 1 value, got {}", mis.len()).as_slice());
false
}
_ => {
cx.span_err(cfg.span, format!("invalid predicate `{}`", pred).as_slice());
false
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the not/catch-all cases be passed through to attr::test_cfg?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not can't, since this is valid: not(any(a, b)). It seems kind of weird to call into test_cfg for a case you know is an error.

}
}
ast::MetaWord(_) | ast::MetaNameValue(..) => attr::contains(cx.cfg.as_slice(), cfg),
}
}
1 change: 1 addition & 0 deletions src/libsyntax/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,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;
Expand Down
3 changes: 1 addition & 2 deletions src/libtest/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,7 @@ Test Attributes:
#[ignore] - When applied to a function which is already attributed as a
test, then the test runner will ignore these tests during
normal test runs. Running with --ignored will run these
tests. This may also be written as #[ignore(cfg(...))] to
ignore the test on certain configurations.",
tests.",
usage = getopts::usage(message.as_slice(),
optgroups().as_slice()));
}
Expand Down
2 changes: 1 addition & 1 deletion src/libtime/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1490,7 +1490,7 @@ mod tests {
}

#[test]
#[ignore(cfg(target_os = "android"))] // FIXME #10958
#[cfg_attr(target_os = "android", ignore)] // FIXME #10958
fn run_tests() {
// The tests race on tzset. So instead of having many independent
// tests, we will just call the functions now.
Expand Down
59 changes: 59 additions & 0 deletions src/test/run-pass/cfg_attr.rs
Original file line number Diff line number Diff line change
@@ -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.

// 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);

#[cfg_attr(any(target_endian = "little", target_endian = "big"), deriving(Show))]
struct KeyValue;

fn is_show<T: Show>() {}

fn main() {
is_show::<Set1>();
is_show::<NotNotset>();
is_show::<AllSet1Set2>();
is_show::<AnySet1Notset>();
is_show::<Complex>();
is_show::<KeyValue>();
}
2 changes: 1 addition & 1 deletion src/test/run-pass/tcp-connect-timeouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ iotest!(fn eventual_timeout() {
}
}
fail!("never timed out!");
} #[ignore(cfg(target_os = "freebsd"))])
} #[cfg_attr(target_os = "freebsd", ignore)])

iotest!(fn timeout_success() {
let addr = next_test_ip4();
Expand Down