Skip to content
Draft
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
32 changes: 12 additions & 20 deletions compiler/rustc_attr_parsing/src/attributes/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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<Symbol>) {
cx.emit_lint(INVALID_DOC_ATTRIBUTES, ExpectedNameValue, span);
Expand Down Expand Up @@ -163,9 +155,10 @@ impl DocParser {
return;
}

if !check_attr_crate_level(cx, path.span()) {
return;
}
cx.check_target(
sym::no_crate_inject.as_str(),
&AllowedTargets::AllowList(&[Allow(Target::Crate)]),

@JonathanBrouwer JonathanBrouwer Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you use AllowListWarnRest instead of AllowList it will warn instead of erroring.
Then to generate the right lint INVALID_DOC_ATTRIBUTES instead of the default UNUSED_ATTRIBUTES we should add a special case here

} else if is_diagnostic_attr {

View changes since the review

);

self.attribute.no_crate_inject = Some(path.span())
}
Expand Down Expand Up @@ -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);
}};
}
Expand All @@ -548,9 +542,7 @@ impl DocParser {
return;
};

if !check_attr_crate_level(cx, path.span()) {
return;
}
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.
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_attr_parsing/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information"
)]
pub(crate) struct AttrCrateLevelOnly;

#[derive(Diagnostic)]
#[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")]
pub(crate) struct DoNotRecommendDoesNotExpectArgs;
Expand Down
21 changes: 21 additions & 0 deletions tests/ui/attributes/doc-crate-level.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![feature(rustdoc_internals)]

#[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() {}

fn main() {}
98 changes: 98 additions & 0 deletions tests/ui/attributes/doc-crate-level.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]`
--> $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:19:1
|
LL | fn function() {}
| ^^^^^^^^^^^^^^^^

error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]`
--> $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:19:1
|
LL | fn function() {}
| ^^^^^^^^^^^^^^^^

error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]`
--> $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:19:1
|
LL | fn function() {}
| ^^^^^^^^^^^^^^^^

error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]`
--> $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: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
|
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() {}
| ^^^^^^^^^^^^^^^^

error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]`
--> $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: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
|
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() {}
| ^^^^^^^^^^^^^^^^

error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![doc]`
--> $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:19:1
|
LL | fn function() {}
| ^^^^^^^^^^^^^^^^

error: aborting due to 8 previous errors

2 changes: 0 additions & 2 deletions tests/ui/feature-gates/doc-rust-logo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

@JonathanBrouwer JonathanBrouwer Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason you removed this from the test?
This is here to show that this doc attribute, incorrectly, is not feature gated

View changes since the review

fn main() {}
11 changes: 1 addition & 10 deletions tests/ui/feature-gates/doc-rust-logo.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> 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`.
Loading