From 9d0b363733a503d81f85e87d841f4497434c5754 Mon Sep 17 00:00:00 2001 From: Eva van Houten Date: Sat, 22 Aug 2026 14:35:23 +0200 Subject: [PATCH 1/6] Replace check_attr_crate_level with check_target --- .../rustc_attr_parsing/src/attributes/doc.rs | 35 ++++++++----------- .../rustc_attr_parsing/src/diagnostics.rs | 7 ---- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index e315d6abea395..bc90ff0db03ac 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -14,8 +14,8 @@ use super::prelude::{ALL_TARGETS, AllowedTargets}; use super::{AcceptMapping, AttributeParser, template}; use crate::context::{AcceptContext, FinalizeContext}; use crate::diagnostics::{ - AttrCrateLevelOnly, DocAliasBadChar, DocAliasDuplicated, DocAliasEmpty, DocAliasMalformed, - DocAliasStartEnd, DocAttrNotCrateLevel, DocAttributeNotAttribute, DocAutoCfgExpectsHideOrShow, + DocAliasBadChar, DocAliasDuplicated, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, + DocAttrNotCrateLevel, DocAttributeNotAttribute, DocAutoCfgExpectsHideOrShow, DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues, DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues, DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocKeywordNotKeyword, DocTestLiteral, @@ -26,6 +26,7 @@ use crate::diagnostics::{ use crate::parser::{ ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser, }; +use crate::target_checking::Policy::Allow; fn check_keyword(cx: &mut AcceptContext<'_, '_>, keyword: Symbol, span: Span) -> bool { // FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we @@ -63,15 +64,6 @@ fn check_attr_not_crate_level( true } -/// Checks that an attribute is used at the crate level. Returns `true` if valid. -fn check_attr_crate_level(cx: &mut AcceptContext<'_, '_>, span: Span) -> bool { - if cx.shared.target != Target::Crate { - cx.emit_lint(INVALID_DOC_ATTRIBUTES, AttrCrateLevelOnly, span); - return false; - } - true -} - // FIXME: To be removed once merged and replace with `cx.expected_name_value(span, _name)`. fn expected_name_value(cx: &mut AcceptContext<'_, '_>, span: Span, _name: Option) { cx.emit_lint(INVALID_DOC_ATTRIBUTES, ExpectedNameValue, span); @@ -163,9 +155,10 @@ impl DocParser { return; } - if !check_attr_crate_level(cx, path.span()) { - return; - } + cx.check_target( + &sym::no_crate_inject.to_string(), + &AllowedTargets::AllowList(&[Allow(Target::Crate)]), + ); self.attribute.no_crate_inject = Some(path.span()) } @@ -530,9 +523,10 @@ impl DocParser { return; } let span = path.span(); - if !check_attr_crate_level(cx, span) { - return; - } + cx.check_target( + concat!("(", stringify!($ident), ")"), + &AllowedTargets::AllowList(&[Allow(Target::Crate)]), + ); self.attribute.$ident = Some(span); }}; } @@ -548,9 +542,10 @@ impl DocParser { return; }; - if !check_attr_crate_level(cx, path.span()) { - return; - } + cx.check_target( + &s.to_string(), + &AllowedTargets::AllowList(&[Allow(Target::Crate)]), + ); // FIXME: It's errorring when the attribute is passed multiple times on the command // line. diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index a37d56419adac..ec67fdad6410b 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -286,13 +286,6 @@ pub(crate) struct DocTestUnknown { #[diag("`#![doc(test(...)]` does not take a literal")] pub(crate) struct DocTestLiteral; -#[derive(Diagnostic)] -#[diag("this attribute can only be applied at the crate level")] -#[note( - "read for more information" -)] -pub(crate) struct AttrCrateLevelOnly; - #[derive(Diagnostic)] #[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")] pub(crate) struct DoNotRecommendDoesNotExpectArgs; From 6a28006dbc1c3f0c0a57f2ab7a4c6911a1fc8f0c Mon Sep 17 00:00:00 2001 From: Eva van Houten Date: Fri, 28 Aug 2026 14:13:43 +0200 Subject: [PATCH 2/6] Fix string casting --- compiler/rustc_attr_parsing/src/attributes/doc.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index bc90ff0db03ac..f2a4fe96a362a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -156,7 +156,7 @@ impl DocParser { } cx.check_target( - &sym::no_crate_inject.to_string(), + sym::no_crate_inject.as_str(), &AllowedTargets::AllowList(&[Allow(Target::Crate)]), ); @@ -542,10 +542,7 @@ impl DocParser { return; }; - cx.check_target( - &s.to_string(), - &AllowedTargets::AllowList(&[Allow(Target::Crate)]), - ); + cx.check_target(s.as_str(), &AllowedTargets::AllowList(&[Allow(Target::Crate)])); // FIXME: It's errorring when the attribute is passed multiple times on the command // line. From 7a056e3419526f88f0468ac1add0be7cfdf922d8 Mon Sep 17 00:00:00 2001 From: Eva van Houten Date: Fri, 28 Aug 2026 14:33:47 +0200 Subject: [PATCH 3/6] Add test for doc attr crate-level target checks --- tests/ui/attributes/doc-crate-level.rs | 43 +++++ tests/ui/attributes/doc-crate-level.stderr | 204 +++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 tests/ui/attributes/doc-crate-level.rs create mode 100644 tests/ui/attributes/doc-crate-level.stderr diff --git a/tests/ui/attributes/doc-crate-level.rs b/tests/ui/attributes/doc-crate-level.rs new file mode 100644 index 0000000000000..89d934a5dd257 --- /dev/null +++ b/tests/ui/attributes/doc-crate-level.rs @@ -0,0 +1,43 @@ +#![feature(rustdoc_internals)] +#![doc(fake_variadic)] +//~^ ERROR `#![doc(fake_variadic = "...")]` isn't allowed as a crate-level attribute +#![doc(alias = "test")] +//~^ ERROR `#![doc(alias = "...")]` isn't allowed as a crate-level attribute +#![doc(search_unbox)] +//~^ ERROR `#![doc(search_unbox = "...")]` isn't allowed as a crate-level attribute + +#[doc(rust_logo)] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +#[doc(html_favicon_url = "example.org")] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +#[doc(html_logo_url = "example.org")] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +#[doc(html_playground_url = "example.org")] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +#[doc(issue_tracker_base_url = "example.org")] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +#[doc(html_root_url = "example.org")] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +#[doc(html_no_source)] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +#[doc(test(no_crate_inject))] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +fn function() {} + +#![doc(rust_logo)] +//~^ ERROR an inner attribute is not permitted in this context +#![doc(html_favicon_url = "example.org")] +//~^ ERROR an inner attribute is not permitted in this context +#![doc(html_logo_url = "example.org")] +//~^ ERROR an inner attribute is not permitted in this context +#![doc(html_playground_url = "example.org")] +//~^ ERROR an inner attribute is not permitted in this context +#![doc(issue_tracker_base_url = "example.org")] +//~^ ERROR an inner attribute is not permitted in this context +#![doc(html_root_url = "example.org")] +//~^ ERROR an inner attribute is not permitted in this context +#![doc(html_no_source)] +//~^ ERROR an inner attribute is not permitted in this context +#![doc(test(no_crate_inject))] +//~^ ERROR an inner attribute is not permitted in this context +fn main() {} diff --git a/tests/ui/attributes/doc-crate-level.stderr b/tests/ui/attributes/doc-crate-level.stderr new file mode 100644 index 0000000000000..5f5b853e5fd4e --- /dev/null +++ b/tests/ui/attributes/doc-crate-level.stderr @@ -0,0 +1,204 @@ +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:27:1 + | +LL | #![doc(rust_logo)] + | ^^^^^^^^^^^^^^^^^^ +... +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:29:1 + | +LL | #![doc(html_favicon_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:31:1 + | +LL | #![doc(html_logo_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:33:1 + | +LL | #![doc(html_playground_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:35:1 + | +LL | #![doc(issue_tracker_base_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:37:1 + | +LL | #![doc(html_root_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:39:1 + | +LL | #![doc(html_no_source)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: an inner attribute is not permitted in this context + --> $DIR/doc-crate-level.rs:41:1 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn main() {} + | ------------ the inner attribute doesn't annotate this function + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + +error: `#![doc(fake_variadic = "...")]` isn't allowed as a crate-level attribute + --> $DIR/doc-crate-level.rs:2:8 + | +LL | #![doc(fake_variadic)] + | ^^^^^^^^^^^^^ + +error: `#![doc(alias = "...")]` isn't allowed as a crate-level attribute + --> $DIR/doc-crate-level.rs:4:16 + | +LL | #![doc(alias = "test")] + | ^^^^^^ + +error: `#![doc(search_unbox = "...")]` isn't allowed as a crate-level attribute + --> $DIR/doc-crate-level.rs:6:8 + | +LL | #![doc(search_unbox)] + | ^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:9:1 + | +LL | #[doc(rust_logo)] + | ^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:11:1 + | +LL | #[doc(html_favicon_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:13:1 + | +LL | #[doc(html_logo_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:15:1 + | +LL | #[doc(html_playground_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:17:1 + | +LL | #[doc(issue_tracker_base_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:19:1 + | +LL | #[doc(html_root_url = "example.org")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:21:1 + | +LL | #[doc(html_no_source)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` + --> $DIR/doc-crate-level.rs:23:1 + | +LL | #[doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this attribute does not have an `!`, which means it is applied to this function + --> $DIR/doc-crate-level.rs:25:1 + | +LL | fn function() {} + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 19 previous errors + From fc4c9efa73beef9eb87e6e9fe3944f154c8b175d Mon Sep 17 00:00:00 2001 From: Eva van Houten Date: Tue, 1 Sep 2026 14:05:36 +0200 Subject: [PATCH 4/6] Minimize doc-crate-level test for clarity --- tests/ui/attributes/doc-crate-level.rs | 22 --- tests/ui/attributes/doc-crate-level.stderr | 140 +++----------------- tests/ui/feature-gates/doc-rust-logo.rs | 2 - tests/ui/feature-gates/doc-rust-logo.stderr | 11 +- 4 files changed, 18 insertions(+), 157 deletions(-) diff --git a/tests/ui/attributes/doc-crate-level.rs b/tests/ui/attributes/doc-crate-level.rs index 89d934a5dd257..1acd8c46d35ad 100644 --- a/tests/ui/attributes/doc-crate-level.rs +++ b/tests/ui/attributes/doc-crate-level.rs @@ -1,10 +1,4 @@ #![feature(rustdoc_internals)] -#![doc(fake_variadic)] -//~^ ERROR `#![doc(fake_variadic = "...")]` isn't allowed as a crate-level attribute -#![doc(alias = "test")] -//~^ ERROR `#![doc(alias = "...")]` isn't allowed as a crate-level attribute -#![doc(search_unbox)] -//~^ ERROR `#![doc(search_unbox = "...")]` isn't allowed as a crate-level attribute #[doc(rust_logo)] //~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` @@ -24,20 +18,4 @@ //~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` fn function() {} -#![doc(rust_logo)] -//~^ ERROR an inner attribute is not permitted in this context -#![doc(html_favicon_url = "example.org")] -//~^ ERROR an inner attribute is not permitted in this context -#![doc(html_logo_url = "example.org")] -//~^ ERROR an inner attribute is not permitted in this context -#![doc(html_playground_url = "example.org")] -//~^ ERROR an inner attribute is not permitted in this context -#![doc(issue_tracker_base_url = "example.org")] -//~^ ERROR an inner attribute is not permitted in this context -#![doc(html_root_url = "example.org")] -//~^ ERROR an inner attribute is not permitted in this context -#![doc(html_no_source)] -//~^ ERROR an inner attribute is not permitted in this context -#![doc(test(no_crate_inject))] -//~^ ERROR an inner attribute is not permitted in this context fn main() {} diff --git a/tests/ui/attributes/doc-crate-level.stderr b/tests/ui/attributes/doc-crate-level.stderr index 5f5b853e5fd4e..b44d9c39b613a 100644 --- a/tests/ui/attributes/doc-crate-level.stderr +++ b/tests/ui/attributes/doc-crate-level.stderr @@ -1,204 +1,98 @@ -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:27:1 - | -LL | #![doc(rust_logo)] - | ^^^^^^^^^^^^^^^^^^ -... -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:29:1 - | -LL | #![doc(html_favicon_url = "example.org")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:31:1 - | -LL | #![doc(html_logo_url = "example.org")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:33:1 - | -LL | #![doc(html_playground_url = "example.org")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:35:1 - | -LL | #![doc(issue_tracker_base_url = "example.org")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:37:1 - | -LL | #![doc(html_root_url = "example.org")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:39:1 - | -LL | #![doc(html_no_source)] - | ^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: an inner attribute is not permitted in this context - --> $DIR/doc-crate-level.rs:41:1 - | -LL | #![doc(test(no_crate_inject))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | fn main() {} - | ------------ the inner attribute doesn't annotate this function - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - -error: `#![doc(fake_variadic = "...")]` isn't allowed as a crate-level attribute - --> $DIR/doc-crate-level.rs:2:8 - | -LL | #![doc(fake_variadic)] - | ^^^^^^^^^^^^^ - -error: `#![doc(alias = "...")]` isn't allowed as a crate-level attribute - --> $DIR/doc-crate-level.rs:4:16 - | -LL | #![doc(alias = "test")] - | ^^^^^^ - -error: `#![doc(search_unbox = "...")]` isn't allowed as a crate-level attribute - --> $DIR/doc-crate-level.rs:6:8 - | -LL | #![doc(search_unbox)] - | ^^^^^^^^^^^^ - error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:9:1 + --> $DIR/doc-crate-level.rs:3:1 | LL | #[doc(rust_logo)] | ^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:11:1 + --> $DIR/doc-crate-level.rs:5:1 | LL | #[doc(html_favicon_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:13:1 + --> $DIR/doc-crate-level.rs:7:1 | LL | #[doc(html_logo_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:15:1 + --> $DIR/doc-crate-level.rs:9:1 | LL | #[doc(html_playground_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:17:1 + --> $DIR/doc-crate-level.rs:11:1 | LL | #[doc(issue_tracker_base_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:19:1 + --> $DIR/doc-crate-level.rs:13:1 | LL | #[doc(html_root_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:21:1 + --> $DIR/doc-crate-level.rs:15:1 | LL | #[doc(html_no_source)] | ^^^^^^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:23:1 + --> $DIR/doc-crate-level.rs:17:1 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:25:1 + --> $DIR/doc-crate-level.rs:19:1 | LL | fn function() {} | ^^^^^^^^^^^^^^^^ -error: aborting due to 19 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/feature-gates/doc-rust-logo.rs b/tests/ui/feature-gates/doc-rust-logo.rs index 08857cc778f5b..2ad7272ceafe0 100644 --- a/tests/ui/feature-gates/doc-rust-logo.rs +++ b/tests/ui/feature-gates/doc-rust-logo.rs @@ -2,6 +2,4 @@ //~^ ERROR this subset of the `doc` attribute is meant for internal use only //! This is not an official rust crate -#[doc(rust_logo)] -//~^ WARN this attribute can only be applied at the crate level fn main() {} diff --git a/tests/ui/feature-gates/doc-rust-logo.stderr b/tests/ui/feature-gates/doc-rust-logo.stderr index f31837be284d1..a89bd2c17ed38 100644 --- a/tests/ui/feature-gates/doc-rust-logo.stderr +++ b/tests/ui/feature-gates/doc-rust-logo.stderr @@ -9,15 +9,6 @@ LL | #![doc(rust_logo)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: the `#[doc(rust_logo)]` attribute is used for Rust branding -warning: this attribute can only be applied at the crate level - --> $DIR/doc-rust-logo.rs:5:7 - | -LL | #[doc(rust_logo)] - | ^^^^^^^^^ - | - = note: read for more information - = note: `#[warn(invalid_doc_attributes)]` on by default - -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0658`. From 281ea9ed101eded61cfed0670626620db2eccb0e Mon Sep 17 00:00:00 2001 From: Eva van Houten Date: Fri, 11 Sep 2026 12:47:30 +0200 Subject: [PATCH 5/6] Restore crate level doc attr warning --- .../rustc_attr_parsing/src/attributes/doc.rs | 9 +- .../rustc_attr_parsing/src/diagnostics.rs | 7 ++ .../rustc_attr_parsing/src/target_checking.rs | 12 ++- tests/ui/attributes/doc-crate-level.rs | 17 ++-- tests/ui/attributes/doc-crate-level.stderr | 83 ++++++------------- 5 files changed, 59 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index f2a4fe96a362a..47bb4b13051df 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -157,7 +157,7 @@ impl DocParser { cx.check_target( sym::no_crate_inject.as_str(), - &AllowedTargets::AllowList(&[Allow(Target::Crate)]), + &AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]), ); self.attribute.no_crate_inject = Some(path.span()) @@ -525,7 +525,7 @@ impl DocParser { let span = path.span(); cx.check_target( concat!("(", stringify!($ident), ")"), - &AllowedTargets::AllowList(&[Allow(Target::Crate)]), + &AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]), ); self.attribute.$ident = Some(span); }}; @@ -542,7 +542,10 @@ impl DocParser { return; }; - cx.check_target(s.as_str(), &AllowedTargets::AllowList(&[Allow(Target::Crate)])); + cx.check_target( + s.as_str(), + &AllowedTargets::AllowListWarnRest(&[Allow(Target::Crate)]), + ); // FIXME: It's errorring when the attribute is passed multiple times on the command // line. diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index ec67fdad6410b..a37d56419adac 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -286,6 +286,13 @@ pub(crate) struct DocTestUnknown { #[diag("`#![doc(test(...)]` does not take a literal")] pub(crate) struct DocTestLiteral; +#[derive(Diagnostic)] +#[diag("this attribute can only be applied at the crate level")] +#[note( + "read for more information" +)] +pub(crate) struct AttrCrateLevelOnly; + #[derive(Diagnostic)] #[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")] pub(crate) struct DoNotRecommendDoesNotExpectArgs; diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 1e272b9674dab..77a4f2f0dc662 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -6,7 +6,7 @@ use rustc_attr_ir::{AttrItem, Attribute, AttributeKind}; use rustc_errors::{DiagArgValue, MultiSpan, StashKey}; use rustc_feature::Features; use rustc_lint_defs::builtin::{ - MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, USELESS_DEPRECATED, + INVALID_DOC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, USELESS_DEPRECATED, }; use rustc_span::{BytePos, FileName, RemapPathScopeComponents, Span, Symbol, sym}; @@ -236,6 +236,16 @@ impl<'sess> AttributeParser<'sess> { }) .unwrap_or_default(); + if name == "doc" { + let diag = crate::diagnostics::AttrCrateLevelOnly; + if warn { + cx.emit_lint(INVALID_DOC_ATTRIBUTES, diag, attr_span); + } else { + cx.emit_err(diag); + } + return; + } + let diag = crate::diagnostics::InvalidAttrStyle { name, is_used_as_inner, diff --git a/tests/ui/attributes/doc-crate-level.rs b/tests/ui/attributes/doc-crate-level.rs index 1acd8c46d35ad..c58866022e2c2 100644 --- a/tests/ui/attributes/doc-crate-level.rs +++ b/tests/ui/attributes/doc-crate-level.rs @@ -1,21 +1,22 @@ +//@check-pass #![feature(rustdoc_internals)] #[doc(rust_logo)] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level #[doc(html_favicon_url = "example.org")] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level #[doc(html_logo_url = "example.org")] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level #[doc(html_playground_url = "example.org")] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level #[doc(issue_tracker_base_url = "example.org")] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level #[doc(html_root_url = "example.org")] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level #[doc(html_no_source)] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level #[doc(test(no_crate_inject))] -//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` +//~^ WARN this attribute can only be applied at the crate level fn function() {} fn main() {} diff --git a/tests/ui/attributes/doc-crate-level.stderr b/tests/ui/attributes/doc-crate-level.stderr index b44d9c39b613a..71c385e576101 100644 --- a/tests/ui/attributes/doc-crate-level.stderr +++ b/tests/ui/attributes/doc-crate-level.stderr @@ -1,98 +1,67 @@ -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:3:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:4:1 | LL | #[doc(rust_logo)] | ^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information + = note: `#[warn(invalid_doc_attributes)]` on by default -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:5:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:6:1 | LL | #[doc(html_favicon_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:7:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:8:1 | LL | #[doc(html_logo_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:9:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:10:1 | LL | #[doc(html_playground_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:11:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:12:1 | LL | #[doc(issue_tracker_base_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:13:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:14:1 | LL | #[doc(html_root_url = "example.org")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:15:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:16:1 | LL | #[doc(html_no_source)] | ^^^^^^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information -error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]` - --> $DIR/doc-crate-level.rs:17:1 +warning: this attribute can only be applied at the crate level + --> $DIR/doc-crate-level.rs:18:1 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/doc-crate-level.rs:19:1 - | -LL | fn function() {} - | ^^^^^^^^^^^^^^^^ + = note: read for more information -error: aborting due to 8 previous errors +warning: 8 warnings emitted From 4899f5bbd43942e411b53d4accfd2c800c738268 Mon Sep 17 00:00:00 2001 From: Eva van Houten Date: Mon, 14 Sep 2026 15:37:41 +0200 Subject: [PATCH 6/6] Bless rustdoc ui test --- .../lints/invalid-crate-level-lint.rs | 3 ++- .../lints/invalid-crate-level-lint.stderr | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs index afb0a5987deb3..e38a63cd12550 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs @@ -6,7 +6,8 @@ pub mod bar { #![doc(test(no_crate_inject))] - //~^ ERROR can only be applied at the crate level + //~^ ERROR unused attribute + //~| WARN this was previously accepted by the compiler but is being phased out #[doc(test(no_crate_inject))] //~^ ERROR can only be applied at the crate level diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr index 7569cf575e510..b98b6fda035f4 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr @@ -1,8 +1,8 @@ error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:4:12 + --> $DIR/invalid-crate-level-lint.rs:4:1 | LL | #[doc(test(no_crate_inject))] - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: read for more information note: the lint level is defined here @@ -11,19 +11,24 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: this attribute can only be applied at the crate level +error: unused attribute --> $DIR/invalid-crate-level-lint.rs:8:17 | LL | #![doc(test(no_crate_inject))] - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: remove this attribute | - = note: read for more information +note: attribute also specified here + --> $DIR/invalid-crate-level-lint.rs:4:12 + | +LL | #[doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:11:16 + --> $DIR/invalid-crate-level-lint.rs:12:5 | LL | #[doc(test(no_crate_inject))] - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: read for more information