]> git.lizzy.rs Git - rust.git/commitdiff
Stabilize extended_key_value_attributes
authorJoshua Nelson <jyn514@gmail.com>
Mon, 22 Mar 2021 05:10:10 +0000 (01:10 -0400)
committerJoshua Nelson <jyn514@gmail.com>
Tue, 18 May 2021 05:01:36 +0000 (01:01 -0400)
 # Stabilization report

 ## Summary

This stabilizes using macro expansion in key-value attributes, like so:

 ```rust
 #[doc = include_str!("my_doc.md")]
 struct S;

 #[path = concat!(env!("OUT_DIR"), "/generated.rs")]
 mod m;
 ```

See the changes to the reference for details on what macros are allowed;
see Petrochenkov's excellent blog post [on internals](https://internals.rust-lang.org/t/macro-expansion-points-in-attributes/11455)
for alternatives that were considered and rejected ("why accept no more
and no less?")

This has been available on nightly since 1.50 with no major issues.

 ## Notes

 ### Accepted syntax

The parser accepts arbitrary Rust expressions in this position, but any expression other than a macro invocation will ultimately lead to an error because it is not expected by the built-in expression forms (e.g., `#[doc]`).  Note that decorators and the like may be able to observe other expression forms.

 ### Expansion ordering

Expansion of macro expressions in "inert" attributes occurs after decorators have executed, analogously to macro expressions appearing in the function body or other parts of decorator input.

There is currently no way for decorators to accept macros in key-value position if macro expansion must be performed before the decorator executes (if the macro can simply be copied into the output for later expansion, that can work).

 ## Test cases

 - https://github.com/rust-lang/rust/blob/master/src/test/ui/attributes/key-value-expansion-on-mac.rs
 - https://github.com/rust-lang/rust/blob/master/src/test/rustdoc/external-doc.rs

The feature has also been dogfooded extensively in the compiler and
standard library:

- https://github.com/rust-lang/rust/pull/83329
- https://github.com/rust-lang/rust/pull/83230
- https://github.com/rust-lang/rust/pull/82641
- https://github.com/rust-lang/rust/pull/80534

 ## Implementation history

- Initial proposal: https://github.com/rust-lang/rust/issues/55414#issuecomment-554005412
- Experiment to see how much code it would break: https://github.com/rust-lang/rust/pull/67121
- Preliminary work to restrict expansion that would conflict with this
feature: https://github.com/rust-lang/rust/pull/77271
- Initial implementation: https://github.com/rust-lang/rust/pull/78837
- Fix for an ICE: https://github.com/rust-lang/rust/pull/80563

 ## Unresolved Questions

~~https://github.com/rust-lang/rust/pull/83366#issuecomment-805180738 listed some concerns, but they have been resolved as of this final report.~~

 ## Additional Information

 There are two workarounds that have a similar effect for `#[doc]`
attributes on nightly. One is to emulate this behavior by using a limited version of this feature that was stabilized for historical reasons:

```rust
macro_rules! forward_inner_docs {
    ($e:expr => $i:item) => {
        #[doc = $e]
        $i
    };
}

forward_inner_docs!(include_str!("lib.rs") => struct S {});
```

This also works for other attributes (like `#[path = concat!(...)]`).
The other is to use `doc(include)`:

```rust
 #![feature(external_doc)]
 #[doc(include = "lib.rs")]
 struct S {}
```

The first works, but is non-trivial for people to discover, and
difficult to read and maintain. The second is a strange special-case for
a particular use of the macro. This generalizes it to work for any use
case, not just including files.

I plan to remove `doc(include)` when this is stabilized. The
`forward_inner_docs` workaround will still compile without warnings, but
I expect it to be used less once it's no longer necessary.

17 files changed:
compiler/rustc_ast_passes/src/feature_gate.rs
compiler/rustc_codegen_llvm/src/lib.rs
compiler/rustc_errors/src/lib.rs
compiler/rustc_feature/src/accepted.rs
compiler/rustc_feature/src/active.rs
compiler/rustc_hir/src/lib.rs
compiler/rustc_parse/src/parser/mod.rs
library/core/src/lib.rs
library/std/src/lib.rs
src/doc/rustdoc/README.md [new file with mode: 0644]
src/doc/rustdoc/src/the-doc-attribute.md
src/test/rustdoc/external-doc.rs
src/test/ui/attributes/key-value-expansion-on-mac.rs
src/test/ui/attributes/key-value-expansion-on-mac.stderr
src/test/ui/feature-gates/feature-gate-extended_key_value_attributes.rs [deleted file]
src/test/ui/feature-gates/feature-gate-extended_key_value_attributes.stderr [deleted file]
src/test/ui/suffixed-literal-meta.rs

index 4215d5c55a049d9a23620c4b27b769eef1988ed0..4996c2195efdf2402f8cd32c816671c31623f553 100644 (file)
@@ -712,10 +712,6 @@ macro_rules! gate_all {
     gate_all!(const_trait_impl, "const trait impls are experimental");
     gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
     gate_all!(inline_const, "inline-const is experimental");
-    gate_all!(
-        extended_key_value_attributes,
-        "arbitrary expressions in key-value attributes are unstable"
-    );
     gate_all!(
         const_generics_defaults,
         "default values for const generic parameters are experimental"
index 329458773ffaff3fbf0afca8815d19a348a9fef8..8eef06f018f3af91871a09e4462d8af129500c5d 100644 (file)
@@ -8,7 +8,7 @@
 #![feature(bool_to_option)]
 #![feature(const_cstr_unchecked)]
 #![feature(crate_visibility_modifier)]
-#![feature(extended_key_value_attributes)]
+#![cfg_attr(bootstrap, feature(extended_key_value_attributes))]
 #![feature(extern_types)]
 #![feature(in_band_lifetimes)]
 #![feature(iter_zip)]
index f53ce7ceace134a239c6bd3ef5c48210b714ea85..65352f0bc6e7b9f6c9e09a4dc23686779c8ed0f4 100644 (file)
@@ -5,7 +5,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(crate_visibility_modifier)]
 #![feature(backtrace)]
-#![feature(extended_key_value_attributes)]
+#![cfg_attr(bootstrap, feature(extended_key_value_attributes))]
 #![feature(format_args_capture)]
 #![feature(iter_zip)]
 #![feature(nll)]
index e8642a52749c46201ee0e1c7116fd919c33e32e1..eef71e096a52e0ebf579282691f700008d03714e 100644 (file)
@@ -281,6 +281,8 @@ macro_rules! declare_features {
     (accepted, or_patterns, "1.53.0", Some(54883), None),
     /// Allows defining identifiers beyond ASCII.
     (accepted, non_ascii_idents, "1.53.0", Some(55467), None),
+    /// Allows arbitrary expressions in key-value attributes at parse time.
+    (accepted, extended_key_value_attributes, "1.54.0", Some(78835), None),
 
     // -------------------------------------------------------------------------
     // feature-group-end: accepted features
index 80fe5bafc6ee6018dfdc563c6b35800c94345685..763264476b341ca506a41dbd3c567c18cf4c0f7c 100644 (file)
@@ -601,9 +601,6 @@ pub fn set(&self, features: &mut Features, span: Span) {
     /// Allows capturing disjoint fields in a closure/generator (RFC 2229).
     (active, capture_disjoint_fields, "1.49.0", Some(53488), None),
 
-    /// Allows arbitrary expressions in key-value attributes at parse time.
-    (active, extended_key_value_attributes, "1.50.0", Some(78835), None),
-
     /// Allows const generics to have default values (e.g. `struct Foo<const N: usize = 3>(...);`).
     (active, const_generics_defaults, "1.51.0", Some(44580), None),
 
index 65c99535c4e3474e830c6b20f45dddfbea45437b..71e997994deff85d8ad76304a5bdde9f03f49336 100644 (file)
@@ -4,7 +4,7 @@
 
 #![feature(crate_visibility_modifier)]
 #![feature(const_panic)]
-#![feature(extended_key_value_attributes)]
+#![cfg_attr(bootstrap, feature(extended_key_value_attributes))]
 #![feature(in_band_lifetimes)]
 #![feature(once_cell)]
 #![cfg_attr(bootstrap, feature(or_patterns))]
index 35cfaae13a4a0942cc8d35a722993d95f666e0d4..4c2bc6ebf3143071d896df2480049798f4636d4f 100644 (file)
@@ -1065,24 +1065,11 @@ fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, MacArgs
             } else if !delimited_only {
                 if self.eat(&token::Eq) {
                     let eq_span = self.prev_token.span;
-                    let mut is_interpolated_expr = false;
-                    if let token::Interpolated(nt) = &self.token.kind {
-                        if let token::NtExpr(..) = **nt {
-                            is_interpolated_expr = true;
-                        }
-                    }
 
                     // Collect tokens because they are used during lowering to HIR.
                     let expr = self.parse_expr_force_collect()?;
                     let span = expr.span;
 
-                    match &expr.kind {
-                        // Not gated to support things like `doc = $expr` that work on stable.
-                        _ if is_interpolated_expr => {}
-                        ExprKind::Lit(lit) if lit.kind.is_unsuffixed() => {}
-                        _ => self.sess.gated_spans.gate(sym::extended_key_value_attributes, span),
-                    }
-
                     let token_kind = token::Interpolated(Lrc::new(token::NtExpr(expr)));
                     MacArgs::Eq(eq_span, Token::new(token_kind, span))
                 } else {
index 6a4f2d5a544c1607630b4f8bcdd960fd084df1de..f652c52db530160623782f897a303897f9bb075d 100644 (file)
 #![cfg_attr(bootstrap, feature(doc_spotlight))]
 #![cfg_attr(not(bootstrap), feature(doc_notable_trait))]
 #![feature(duration_consts_2)]
-#![feature(extended_key_value_attributes)]
+#![cfg_attr(bootstrap, feature(extended_key_value_attributes))]
 #![feature(extern_types)]
 #![feature(fundamental)]
 #![feature(intra_doc_pointers)]
index 5f89ac059fd2d9194c7a909a2ee3445a08cc7725..6d11698d268223ffd95dc4e86377127a0795fb82 100644 (file)
 #![feature(exact_size_is_empty)]
 #![feature(exhaustive_patterns)]
 #![feature(extend_one)]
-#![feature(extended_key_value_attributes)]
+#![cfg_attr(bootstrap, feature(extended_key_value_attributes))]
 #![feature(fn_traits)]
 #![feature(format_args_nl)]
 #![feature(gen_future)]
diff --git a/src/doc/rustdoc/README.md b/src/doc/rustdoc/README.md
new file mode 100644 (file)
index 0000000..7d97d5e
--- /dev/null
@@ -0,0 +1,5 @@
+# Rustdoc
+
+This is documentation for rustdoc itself, written in mdbook format.
+To build the book, use `x.py doc src/doc/rustdoc`.
+To run doctests, use `x.py test src/doc/rustdoc`.
index 52f2a3728fabb5908784e76c7d79bc588dedf979..d192f7d5ce9e52d6d87fe0defa148e07e176bbc6 100644 (file)
@@ -35,6 +35,13 @@ Which can feel more flexible. Note that this would generate this:
 
 but given that docs are rendered via Markdown, it will remove these newlines.
 
+Another use case is for including external files as documentation:
+
+```rust,no_run
+#[doc = include_str!("../README.md")]
+# fn f() {}
+```
+
 The `doc` attribute has more options though! These don't involve the text of
 the output, but instead, various aspects of the presentation of the output.
 We've split them into two kinds below: attributes that are useful at the
index befd31a5492902ea90bd53648b1a2ce36b77f0fb..0dadca551a9ff14a84f851a7de490a9b046d5187 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(external_doc)]
-#![feature(extended_key_value_attributes)]
 
 // @has external_doc/struct.CanHasDocs.html
 // @has - '//h1' 'External Docs'
index 1247ff2b2307559dded6536f4f04715ad0fd3f0b..95bc1c04961b0b01c49e4a762a6b6e30f1e09f9b 100644 (file)
@@ -1,4 +1,3 @@
-#![feature(extended_key_value_attributes)]
 #![feature(rustc_attrs)]
 
 #[rustc_dummy = stringify!(a)] // OK
index b74f3518a7e97be529db32a54ed0c9e5bfca91fc..fa9ea543765f71f3e95c1fc6e13541f5745fedab 100644 (file)
@@ -1,5 +1,5 @@
 error: unexpected token: `stringify!(b)`
-  --> $DIR/key-value-expansion-on-mac.rs:12:17
+  --> $DIR/key-value-expansion-on-mac.rs:11:17
    |
 LL | #[rustc_dummy = stringify!(b)]
    |                 ^^^^^^^^^^^^^
diff --git a/src/test/ui/feature-gates/feature-gate-extended_key_value_attributes.rs b/src/test/ui/feature-gates/feature-gate-extended_key_value_attributes.rs
deleted file mode 100644 (file)
index f19fdb4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#[cfg(FALSE)]
-#[attr = multi::segment::path] //~ ERROR arbitrary expressions in key-value attributes are unstable
-#[attr = macro_call!()] //~ ERROR arbitrary expressions in key-value attributes are unstable
-#[attr = 1 + 2] //~ ERROR arbitrary expressions in key-value attributes are unstable
-#[attr = what?] //~ ERROR arbitrary expressions in key-value attributes are unstable
-struct S;
-
-fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-extended_key_value_attributes.stderr b/src/test/ui/feature-gates/feature-gate-extended_key_value_attributes.stderr
deleted file mode 100644 (file)
index 9887814..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-error[E0658]: arbitrary expressions in key-value attributes are unstable
-  --> $DIR/feature-gate-extended_key_value_attributes.rs:2:10
-   |
-LL | #[attr = multi::segment::path]
-   |          ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #78835 <https://github.com/rust-lang/rust/issues/78835> for more information
-   = help: add `#![feature(extended_key_value_attributes)]` to the crate attributes to enable
-
-error[E0658]: arbitrary expressions in key-value attributes are unstable
-  --> $DIR/feature-gate-extended_key_value_attributes.rs:3:10
-   |
-LL | #[attr = macro_call!()]
-   |          ^^^^^^^^^^^^^
-   |
-   = note: see issue #78835 <https://github.com/rust-lang/rust/issues/78835> for more information
-   = help: add `#![feature(extended_key_value_attributes)]` to the crate attributes to enable
-
-error[E0658]: arbitrary expressions in key-value attributes are unstable
-  --> $DIR/feature-gate-extended_key_value_attributes.rs:4:10
-   |
-LL | #[attr = 1 + 2]
-   |          ^^^^^
-   |
-   = note: see issue #78835 <https://github.com/rust-lang/rust/issues/78835> for more information
-   = help: add `#![feature(extended_key_value_attributes)]` to the crate attributes to enable
-
-error[E0658]: arbitrary expressions in key-value attributes are unstable
-  --> $DIR/feature-gate-extended_key_value_attributes.rs:5:10
-   |
-LL | #[attr = what?]
-   |          ^^^^^
-   |
-   = note: see issue #78835 <https://github.com/rust-lang/rust/issues/78835> for more information
-   = help: add `#![feature(extended_key_value_attributes)]` to the crate attributes to enable
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
index 319264aec9cd67d441107f1855c5f3037ba3d035..a6531490c015914091f13b6423d65a64611090a5 100644 (file)
@@ -1,4 +1,4 @@
-#![feature(rustc_attrs, extended_key_value_attributes)]
+#![feature(rustc_attrs)]
 
 #[rustc_dummy = 1usize] //~ ERROR: suffixed literals are not allowed in attributes
 #[rustc_dummy = 1u8] //~ ERROR: suffixed literals are not allowed in attributes