Allow #![doc(test(attr(..)))] at every level

This commit is contained in:
Urgau
2025-05-05 22:50:40 +02:00
parent 041d95d4dc
commit 316e62a058
9 changed files with 147 additions and 116 deletions

View File

@@ -46,11 +46,6 @@ passes_attr_crate_level =
.suggestion = to apply to the crate, use an inner attribute
.note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
passes_attr_mod_level =
this attribute can only be applied at module level
.suggestion = to apply to the crate, use an inner attribute at the crate level
.note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-module-level> for more information
passes_attr_only_in_functions =
`{$attr}` attribute can only be used on functions

View File

@@ -1252,7 +1252,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
let bang_span = attr.span().lo() + BytePos(1);
let sugg = (attr.style() == AttrStyle::Outer
&& self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID)
.then_some(errors::AttrCrateLevelSugg {
.then_some(errors::AttrCrateLevelOnlySugg {
attr: attr.span().with_lo(bang_span).with_hi(bang_span),
});
self.tcx.emit_node_span_lint(
@@ -1266,46 +1266,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
true
}
/// Checks that an attribute is used at module level. Returns `true` if valid.
fn check_attr_mod_level(
&self,
attr: &Attribute,
meta: &MetaItemInner,
hir_id: HirId,
target: Target,
) -> bool {
if target != Target::Mod {
// insert a bang between `#` and `[...`
let bang_span = attr.span().lo() + BytePos(1);
let sugg = (attr.style() == AttrStyle::Outer
&& self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID)
.then_some(errors::AttrCrateLevelSugg {
attr: attr.span().with_lo(bang_span).with_hi(bang_span),
});
self.tcx.emit_node_span_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
meta.span(),
errors::AttrModLevelOnly { sugg },
);
return false;
}
true
}
/// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place.
fn check_test_attr(
&self,
attr: &Attribute,
meta: &MetaItemInner,
hir_id: HirId,
target: Target,
) {
fn check_test_attr(&self, attr: &Attribute, meta: &MetaItemInner, hir_id: HirId) {
if let Some(metas) = meta.meta_item_list() {
for i_meta in metas {
match (i_meta.name(), i_meta.meta_item()) {
(Some(sym::attr), _) => {
self.check_attr_mod_level(attr, meta, hir_id, target);
// Allowed everywhere like `#[doc]`
}
(Some(sym::no_crate_inject), _) => {
self.check_attr_crate_level(attr, meta, hir_id);
@@ -1396,7 +1363,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
Some(sym::test) => {
self.check_test_attr(attr, meta, hir_id, target);
self.check_test_attr(attr, meta, hir_id);
}
Some(

View File

@@ -1890,20 +1890,12 @@ pub(crate) struct UnusedVarTryIgnoreSugg {
#[note]
pub(crate) struct AttrCrateLevelOnly {
#[subdiagnostic]
pub sugg: Option<AttrCrateLevelSugg>,
}
#[derive(LintDiagnostic)]
#[diag(passes_attr_mod_level)]
#[note]
pub(crate) struct AttrModLevelOnly {
#[subdiagnostic]
pub sugg: Option<AttrCrateLevelSugg>,
pub sugg: Option<AttrCrateLevelOnlySugg>,
}
#[derive(Subdiagnostic)]
#[suggestion(passes_suggestion, applicability = "maybe-incorrect", code = "!", style = "verbose")]
pub(crate) struct AttrCrateLevelSugg {
pub(crate) struct AttrCrateLevelOnlySugg {
#[primary_span]
pub attr: Span,
}

View File

@@ -141,34 +141,6 @@ But if you include this:
it will not.
## At the module level
These forms of the `#[doc]` attribute are used on individual modules, to control how
they are documented.
### `test(attr(...))`
This form of the `doc` attribute allows you to add arbitrary attributes to all your doctests. For
example, if you want your doctests to fail if they have dead code, you could add this:
```rust,no_run
#![doc(test(attr(deny(dead_code))))]
mod my_mod {
#![doc(test(attr(allow(dead_code))))] // but allow `dead_code` for this module
}
```
`test(attr(..))` attributes are appended to the parent module's, they do not replace the current
list of attributes. In the previous example, both attributes would be present:
```rust,no_run
// For every doctest in `my_mod`
#![deny(dead_code)] // from the crate-root
#![allow(dead_code)] // from `my_mod`
```
## At the item level
These forms of the `#[doc]` attribute are used on individual items, to control how
@@ -300,3 +272,26 @@ To get around this limitation, we just add `#[doc(alias = "lib_name_do_something
on the `do_something` method and then it's all good!
Users can now look for `lib_name_do_something` in our crate directly and find
`Obj::do_something`.
### `test(attr(...))`
This form of the `doc` attribute allows you to add arbitrary attributes to all your doctests. For
example, if you want your doctests to fail if they have dead code, you could add this:
```rust,no_run
#![doc(test(attr(deny(dead_code))))]
mod my_mod {
#![doc(test(attr(allow(dead_code))))] // but allow `dead_code` for this module
}
```
`test(attr(..))` attributes are appended to the parent module's, they do not replace the current
list of attributes. In the previous example, both attributes would be present:
```rust,no_run
// For every doctest in `my_mod`
#![deny(dead_code)] // from the crate-root
#![allow(dead_code)] // from `my_mod`
```

View File

@@ -4,10 +4,6 @@
#![doc(masked)]
//~^ ERROR this attribute can only be applied to an `extern crate` item
#[doc(test(attr(allow(warnings))))]
//~^ ERROR can only be applied at module level
//~| HELP to apply to the crate, use an inner attribute
//~| SUGGESTION !
#[doc(test(no_crate_inject))]
//~^ ERROR can only be applied at the crate level
//~| HELP to apply to the crate, use an inner attribute

View File

@@ -1,30 +1,18 @@
error: this attribute can only be applied at module level
--> $DIR/invalid-doc-attr.rs:7:7
|
LL | #[doc(test(attr(allow(warnings))))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-module-level> for more information
= note: `#[deny(invalid_doc_attributes)]` on by default
help: to apply to the crate, use an inner attribute at the crate level
|
LL | #![doc(test(attr(allow(warnings))))]
| +
error: this attribute can only be applied at the crate level
--> $DIR/invalid-doc-attr.rs:11:7
--> $DIR/invalid-doc-attr.rs:7:7
|
LL | #[doc(test(no_crate_inject))]
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
= note: `#[deny(invalid_doc_attributes)]` on by default
help: to apply to the crate, use an inner attribute
|
LL | #![doc(test(no_crate_inject))]
| +
error: this attribute can only be applied to a `use` item
--> $DIR/invalid-doc-attr.rs:15:7
--> $DIR/invalid-doc-attr.rs:11:7
|
LL | #[doc(inline)]
| ^^^^^^ only applicable on `use` items
@@ -35,7 +23,7 @@ LL | pub fn foo() {}
= note: read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
error: this attribute can only be applied at the crate level
--> $DIR/invalid-doc-attr.rs:20:12
--> $DIR/invalid-doc-attr.rs:16:12
|
LL | #![doc(test(no_crate_inject))]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -43,7 +31,7 @@ LL | #![doc(test(no_crate_inject))]
= note: read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
error: conflicting doc inlining attributes
--> $DIR/invalid-doc-attr.rs:30:7
--> $DIR/invalid-doc-attr.rs:26:7
|
LL | #[doc(inline)]
| ^^^^^^ this attribute...
@@ -53,7 +41,7 @@ LL | #[doc(no_inline)]
= help: remove one of the conflicting attributes
error: this attribute can only be applied to an `extern crate` item
--> $DIR/invalid-doc-attr.rs:36:7
--> $DIR/invalid-doc-attr.rs:32:7
|
LL | #[doc(masked)]
| ^^^^^^ only applicable on `extern crate` items
@@ -64,7 +52,7 @@ LL | pub struct Masked;
= note: read <https://doc.rust-lang.org/unstable-book/language-features/doc-masked.html> for more information
error: this attribute cannot be applied to an `extern crate self` item
--> $DIR/invalid-doc-attr.rs:40:7
--> $DIR/invalid-doc-attr.rs:36:7
|
LL | #[doc(masked)]
| ^^^^^^ not applicable on `extern crate self` items
@@ -81,7 +69,7 @@ LL | #![doc(masked)]
= note: read <https://doc.rust-lang.org/unstable-book/language-features/doc-masked.html> for more information
error: this attribute can only be applied at the crate level
--> $DIR/invalid-doc-attr.rs:23:11
--> $DIR/invalid-doc-attr.rs:19:11
|
LL | #[doc(test(no_crate_inject))]
| ^^^^^^^^^^^^^^^^^^^^^
@@ -89,7 +77,7 @@ LL | #[doc(test(no_crate_inject))]
= note: read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
error: this attribute can only be applied to a `use` item
--> $DIR/invalid-doc-attr.rs:25:11
--> $DIR/invalid-doc-attr.rs:21:11
|
LL | #[doc(inline)]
| ^^^^^^ only applicable on `use` items
@@ -99,5 +87,5 @@ LL | pub fn baz() {}
|
= note: read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information
error: aborting due to 10 previous errors
error: aborting due to 9 previous errors

View File

@@ -9,8 +9,13 @@ macro_rules! mac {
/// foo //~ ERROR unused doc comment
mac!();
/// a //~ ERROR unused doc comment
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
unsafe extern "C" { }
fn foo() {
/// a //~ ERROR unused doc comment
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
let x = 12;
/// multi-line //~ ERROR unused doc comment
@@ -19,6 +24,7 @@ fn foo() {
match x {
/// c //~ ERROR unused doc comment
1 => {},
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
_ => {}
}
@@ -32,6 +38,7 @@ fn foo() {
/// bar //~ ERROR unused doc comment
mac!();
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
let x = /** comment */ 47; //~ ERROR unused doc comment
/// dox //~ ERROR unused doc comment

View File

@@ -12,7 +12,28 @@ LL | #![deny(unused_doc_comments)]
| ^^^^^^^^^^^^^^^^^^^
error: unused doc comment
--> $DIR/useless-comment.rs:32:5
--> $DIR/useless-comment.rs:12:1
|
LL | /// a
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[doc(test(attr(allow(dead_code))))]
LL | unsafe extern "C" { }
| --------------------- rustdoc does not generate documentation for extern blocks
|
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:13:1
|
LL | #[doc(test(attr(allow(dead_code))))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe extern "C" { }
| --------------------- rustdoc does not generate documentation for extern blocks
|
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:38:5
|
LL | /// bar
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations
@@ -20,17 +41,28 @@ LL | /// bar
= help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion
error: unused doc comment
--> $DIR/useless-comment.rs:13:5
--> $DIR/useless-comment.rs:17:5
|
LL | /// a
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[doc(test(attr(allow(dead_code))))]
LL | let x = 12;
| ----------- rustdoc does not generate documentation for statements
|
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:16:5
--> $DIR/useless-comment.rs:18:5
|
LL | #[doc(test(attr(allow(dead_code))))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let x = 12;
| ----------- rustdoc does not generate documentation for statements
|
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:21:5
|
LL | / /// multi-line
LL | | /// doc comment
@@ -39,6 +71,7 @@ LL | | /// that is unused
LL | / match x {
LL | | /// c
LL | | 1 => {},
LL | | #[doc(test(attr(allow(dead_code))))]
LL | | _ => {}
LL | | }
| |_____- rustdoc does not generate documentation for expressions
@@ -46,7 +79,7 @@ LL | | }
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:20:9
--> $DIR/useless-comment.rs:25:9
|
LL | /// c
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,7 +89,17 @@ LL | 1 => {},
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:25:5
--> $DIR/useless-comment.rs:27:9
|
LL | #[doc(test(attr(allow(dead_code))))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | _ => {}
| ------- rustdoc does not generate documentation for match arms
|
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:31:5
|
LL | /// foo
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -66,7 +109,7 @@ LL | unsafe {}
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:28:5
--> $DIR/useless-comment.rs:34:5
|
LL | #[doc = "foo"]
| ^^^^^^^^^^^^^^
@@ -77,7 +120,7 @@ LL | 3;
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:29:5
--> $DIR/useless-comment.rs:35:5
|
LL | #[doc = "bar"]
| ^^^^^^^^^^^^^^
@@ -87,7 +130,17 @@ LL | 3;
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:35:13
--> $DIR/useless-comment.rs:41:5
|
LL | #[doc(test(attr(allow(dead_code))))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let x = /** comment */ 47;
| -------------------------- rustdoc does not generate documentation for statements
|
= help: use `//` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:42:13
|
LL | let x = /** comment */ 47;
| ^^^^^^^^^^^^^^ -- rustdoc does not generate documentation for expressions
@@ -95,7 +148,7 @@ LL | let x = /** comment */ 47;
= help: use `/* */` for a plain comment
error: unused doc comment
--> $DIR/useless-comment.rs:37:5
--> $DIR/useless-comment.rs:44:5
|
LL | /// dox
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -106,5 +159,5 @@ LL | | }
|
= help: use `//` for a plain comment
error: aborting due to 10 previous errors
error: aborting due to 15 previous errors

View File

@@ -10,4 +10,42 @@ mod test {
#![doc(test(attr(allow(warnings))))]
}
#[doc(test(attr(allow(dead_code))))]
static S: u32 = 5;
#[doc(test(attr(allow(dead_code))))]
const C: u32 = 5;
#[doc(test(attr(deny(dead_code))))]
struct A {
#[doc(test(attr(allow(dead_code))))]
field: u32
}
#[doc(test(attr(deny(dead_code))))]
union U {
#[doc(test(attr(allow(dead_code))))]
field: u32,
field2: u64,
}
#[doc(test(attr(deny(dead_code))))]
enum Enum {
#[doc(test(attr(allow(dead_code))))]
Variant1,
}
#[doc(test(attr(deny(dead_code))))]
impl A {
#[doc(test(attr(deny(dead_code))))]
fn method() {}
}
#[doc(test(attr(deny(dead_code))))]
trait MyTrait {
#[doc(test(attr(deny(dead_code))))]
fn my_trait_fn();
}
#[doc(test(attr(deny(dead_code))))]
pub fn foo() {}