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

Conversation

sfackler
Copy link
Member

@sfackler sfackler commented Aug 4, 2014

This extends cfg-gating to attributes.

#[cfg_attr(<cfg pattern>, <attr>)]

will expand to

#[<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:

// 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> { ... }

I also deprecated the #[ignore(cfg(...))] syntax in favor of #[cfg_attr(..., ignore)].

I think this cfg pattern syntax is a good system to move #[cfg(...)] to as well, but that should probably go through an RFC first.

@sfackler
Copy link
Member Author

sfackler commented Aug 4, 2014

cc #12479

out.attrs.push(cx.attribute(attr.span, attr));
}

box(GC) out
Copy link
Contributor

Choose a reason for hiding this comment

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

Must you always clone this? Seems like the clone is unnecessary if the config doesn't match.

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

@lilyball
Copy link
Contributor

lilyball commented Aug 4, 2014

👍 I like this grammar.

@sfackler
Copy link
Member Author

sfackler commented Aug 4, 2014

@kballard updated.

@@ -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?

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> { ... }
```
Replace `#[ignore(cfg(a, b))]` with `#[cfg_attr(all(a, b), ignore)]`
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.

@sfackler
Copy link
Member Author

sfackler commented Aug 7, 2014

This is on hold pending an RFC to move #[cfg] and cfg! over to this syntax.

@sfackler
Copy link
Member Author

sfackler commented Aug 9, 2014

rust-lang/rfcs#194

@alexcrichton
Copy link
Member

For now (bors's queue is quite full), I'm going to close this while we wait on the RFC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants