Skip to content

Conversation

@GuillaumeGomez
Copy link
Member

Part of #131229.

This PR ports the doc attributes to the new attribute API. However, there are things that will need to be fixed in a follow-up:

  • Some part of cfg_old.rs are likely unused now, so they should be removed.
  • Not all error/lints are emitted at the same time anymore, making them kinda less useful considering that you need to run and fix rustc/rustdoc multiple times to get through all of them.
  • For coherency with the other attribute errors, I didn't modify the default output too much, meaning that we have some new messages now. I'll likely come back to that to check if the previous ones were better in a case-by-case approach.
  • doc(test(attr(...))) is handled in a horrifying manner currently. Until we can handle it correctly with the Attribute system, it'll remain that thing we're all very ashamed of. 😈
  • A type in rustdoc got its size increased, I'll check the impact on performance. But in any case, I plan to improve it in a follow-up so should be "ok".
  • Because of error reporting, some fields of Doc are suboptimal, like inline which instead of being an Option is a ThinVec because we report the error later on. Part of the things I'm not super happy about but can be postponed to future me.

And finally, once this PR is merged, I plan to finally stabilize doc_cfg feature. :)

cc @jdonszelmann
r? @JonathanBrouwer

@rustbot
Copy link
Collaborator

rustbot commented Dec 4, 2025

Some changes occurred in compiler/rustc_passes/src/check_attr.rs

cc @jdonszelmann

Some changes occurred in compiler/rustc_attr_parsing

cc @jdonszelmann

These commits modify the Cargo.lock file. Unintentional changes to Cargo.lock can be introduced when switching branches and rebasing PRs.

If this was unintentional then you should revert the changes before this PR is merged.
Otherwise, you can ignore this comment.

Some changes occurred in compiler/rustc_hir/src/attrs

cc @jdonszelmann

@rustbot rustbot added A-attributes Area: Attributes (`#[…]`, `#![…]`) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. labels Dec 4, 2025
@GuillaumeGomez
Copy link
Member Author

Just to be safe:

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Dec 4, 2025
Port `doc` attributes to new attribute API
@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Dec 4, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@GuillaumeGomez
Copy link
Member Author

Ah right, forgot about clippy. Updating it as well.

@rustbot
Copy link
Collaborator

rustbot commented Dec 4, 2025

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rustbot rustbot added the T-clippy Relevant to the Clippy team. label Dec 4, 2025
@rust-bors
Copy link

rust-bors bot commented Dec 4, 2025

☀️ Try build successful (CI)
Build commit: 4f24013 (4f240130369441697d5ccde454291d18ea71990c, parent: 5372fc9cb790c112ce707991b1fcc025cec8fdbe)

@rust-timer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (4f24013): comparison URL.

Overall result: ❌✅ regressions and improvements - please read the text below

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @rustbot label: +perf-regression-triaged. If not, please fix the regressions and do another perf run. If its results are neutral or positive, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.0% [0.2%, 3.1%] 30
Improvements ✅
(primary)
-0.5% [-1.3%, -0.1%] 39
Improvements ✅
(secondary)
-0.6% [-1.1%, -0.1%] 30
All ❌✅ (primary) -0.5% [-1.3%, -0.1%] 39

Max RSS (memory usage)

Results (primary -0.2%, secondary 1.5%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
1.3% [0.8%, 1.5%] 3
Regressions ❌
(secondary)
3.6% [1.4%, 6.4%] 11
Improvements ✅
(primary)
-1.0% [-2.3%, -0.5%] 6
Improvements ✅
(secondary)
-1.4% [-2.5%, -0.6%] 8
All ❌✅ (primary) -0.2% [-2.3%, 1.5%] 9

Cycles

Results (secondary 2.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
4.1% [2.2%, 9.8%] 8
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.8% [-5.5%, -2.2%] 3
All ❌✅ (primary) - - 0

Binary size

Results (primary -0.5%, secondary -0.1%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
0.1% [0.0%, 0.3%] 23
Regressions ❌
(secondary)
0.0% [0.0%, 0.0%] 9
Improvements ✅
(primary)
-0.9% [-3.1%, -0.0%] 28
Improvements ✅
(secondary)
-0.2% [-0.3%, -0.0%] 15
All ❌✅ (primary) -0.5% [-3.1%, 0.3%] 51

Bootstrap: 468.725s -> 473.041s (0.92%)
Artifact size: 386.77 MiB -> 389.06 MiB (0.59%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Dec 4, 2025
@GuillaumeGomez
Copy link
Member Author

GuillaumeGomez commented Dec 4, 2025

Strange, one doc test has a regression but all others are faster.

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Collaborator

bors commented Dec 5, 2025

☔ The latest upstream changes (presumably #149646) made this pull request unmergeable. Please resolve the merge conflicts.

@JonathanBrouwer
Copy link
Contributor

JonathanBrouwer commented Dec 5, 2025

I'd say since perf looks very good on primary benchmarks and is mixed on secondary benchmarks, I'm happy with perf as-is. I am wondering what caused the regression on include-blob though, it doesn't even use any doc attributes
@rustbot label: +perf-regression-triaged

@rustbot
Copy link
Collaborator

rustbot commented Dec 5, 2025

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rust-log-analyzer

This comment has been minimized.

ConstStabilityParser,
DocParser,
MacroUseParser,

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra newline

@@ -0,0 +1,571 @@
// FIXME: to be removed
#![allow(unused_imports)]
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Can you remove this one?

// if self.attribute.$ident.is_some() {
// cx.duplicate_key(path.span(), path.word_sym().unwrap());
// return;
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

Does leaving this out cause a regression? If so please fix, otherwise we can leave it like this

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, the opposite. If we uncomment it, bootstrap command will fail because rustdoc args are duplicated. Gonna need to fix that.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 5, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer
Copy link
Collaborator

The job aarch64-gnu-llvm-20-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)

---- [rustdoc-json] tests/rustdoc-json/keyword_private.rs stdout ----
------jsondocck stdout------------------------------

------jsondocck stderr------------------------------
/checkout/tests/rustdoc-json/keyword_private.rs:8, directive failed
matched to String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], keyword: \"match\", test_attrs: []})]") but want String("#[doc(keyword = \"match\")]")
/checkout/tests/rustdoc-json/keyword_private.rs:16, directive failed
matched to String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], keyword: \"break\", test_attrs: []})]") but want String("#[doc(keyword = \"break\")]")

------------------------------------------

error: jsondocck failed!
status: exit status: 1
command: "/checkout/obj/build/aarch64-unknown-linux-gnu/stage1-tools-bin/jsondocck" "--doc-dir" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/keyword_private" "--template" "/checkout/tests/rustdoc-json/keyword_private.rs"
stdout: none
--- stderr -------------------------------
/checkout/tests/rustdoc-json/keyword_private.rs:8, directive failed
matched to String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], keyword: \"match\", test_attrs: []})]") but want String("#[doc(keyword = \"match\")]")
/checkout/tests/rustdoc-json/keyword_private.rs:16, directive failed
matched to String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], keyword: \"break\", test_attrs: []})]") but want String("#[doc(keyword = \"break\")]")
------------------------------------------

Rustdoc Output:
status: exit status: 0
command: cd "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/keyword_private" && env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustdoc" "-L" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/lib/rustlib/aarch64-unknown-linux-gnu/lib" "-L" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/keyword_private/auxiliary" "-o" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/keyword_private" "--deny" "warnings" "/checkout/tests/rustdoc-json/keyword_private.rs" "-A" "internal_features" "--document-private-items" "--output-format" "json" "-Zunstable-options"
stdout: none
stderr: none

---- [rustdoc-json] tests/rustdoc-json/keyword_private.rs stdout end ----
---- [rustdoc-json] tests/rustdoc-json/visibility/doc_hidden_documented.rs stdout ----
------jsondocck stdout------------------------------

------jsondocck stderr------------------------------
/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs:4, directive failed
matched to Array [Object {"other": String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], test_attrs: []})]")}] but want Array [Object {"other": String("#[doc(hidden)]")}]
/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs:8, directive failed
matched to Array [Object {"other": String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], test_attrs: []})]")}] but want Array [Object {"other": String("#[doc(hidden)]")}]
/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs:12, directive failed
matched to Array [Object {"other": String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], test_attrs: []})]")}] but want Array [Object {"other": String("#[doc(hidden)]")}]

------------------------------------------

error: jsondocck failed!
status: exit status: 1
command: "/checkout/obj/build/aarch64-unknown-linux-gnu/stage1-tools-bin/jsondocck" "--doc-dir" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/visibility/doc_hidden_documented" "--template" "/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs"
stdout: none
--- stderr -------------------------------
/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs:4, directive failed
matched to Array [Object {"other": String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], test_attrs: []})]")}] but want Array [Object {"other": String("#[doc(hidden)]")}]
/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs:8, directive failed
matched to Array [Object {"other": String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], test_attrs: []})]")}] but want Array [Object {"other": String("#[doc(hidden)]")}]
/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs:12, directive failed
matched to Array [Object {"other": String("#[attr = Doc(DocAttribute {aliases: [], inline: [], cfg: [], auto_cfg: [],\nauto_cfg_change: [], test_attrs: []})]")}] but want Array [Object {"other": String("#[doc(hidden)]")}]
------------------------------------------

Rustdoc Output:
status: exit status: 0
command: cd "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/visibility/doc_hidden_documented" && env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustdoc" "-L" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/lib/rustlib/aarch64-unknown-linux-gnu/lib" "-L" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/visibility/doc_hidden_documented/auxiliary" "-o" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-json/visibility/doc_hidden_documented" "--deny" "warnings" "/checkout/tests/rustdoc-json/visibility/doc_hidden_documented.rs" "-A" "internal_features" "--document-hidden-items" "--output-format" "json" "-Zunstable-options"
stdout: none
stderr: none

---- [rustdoc-json] tests/rustdoc-json/visibility/doc_hidden_documented.rs stdout end ----

Copy link
Contributor

@JonathanBrouwer JonathanBrouwer left a comment

Choose a reason for hiding this comment

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

I did a first pass through all the code changes (not the tests yet). Will probably do a second pass because I went quite quickly and might've missed things, but this should be most of it

View changes since this review

pub no_crate_inject: Option<Span>,
}

impl Default for DocAttribute {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this different from a derive(Default)? If not, lets derive this

span,
errors::DocInlineOnlyUse {
attr_span: meta.span(),
item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
Copy link
Contributor

Choose a reason for hiding this comment

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

I see a bunch of these checks got removed, why?

self.check_doc_search_unbox(meta, hir_id);
}
}
fn check_doc_attrs(&self, attr: &DocAttribute, hir_id: HirId, target: Target) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ooh I love how much nicer this file got, ty :3

| CfgEntry::NameValue { .. }
| CfgEntry::Not(..)
| CfgEntry::Version(..)
| CfgEntry::All(..) => true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we recursively check for an Any inside the All here?

if cfgs.is_empty() { None } else { Some(CfgEntry::All(cfgs, DUMMY_SP)) }
}
CfgEntry::Version(..) => {
// FIXME: Should be handled.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this not handled the way it is?

{
let mut iter = stream.into_iter().peekable();
while let Some(token) = iter.next() {
if let TokenTree::Ident(i) = token {
Copy link
Contributor

Choose a reason for hiding this comment

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

This code silently fails if the span does not match the expected structure of an attribute. Is there anywhere we check that it does match it, and error otherwise?
Fine with this hack (honestly surprisingly elegant) but I'm anticipating this hack might be hard to fix and this code might be here for a while, wanna make sure it's not silently failing while it is

let i = i.to_string();
let peek = iter.peek();
match peek {
Some(TokenTree::Group(g)) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't fully understand the cases in this match here, could you add some clarifying comments?

Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => {
let mut new_attr = DocAttribute::default();
new_attr.cfg = d.cfg.clone();
Some(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr))))
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we doing this weird clone of only the cfg condition here? Performance?
(If so, please add a comment explaining it)

indexmap = { version = "2", features = ["serde"] }
itertools = "0.12"
minifier = { version = "0.3.5", default-features = false }
proc-macro2 = "1.0.103"
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we use TokenStream from rustc_ast instead of proc-macro2? Is that better/worse?

for lint in &delayed_lints.lints {
match lint {
DelayedLint::AttributeParsing(attribute_lint) => {
rustc_attr_parsing::emit_attribute_lint(attribute_lint, tcx)
Copy link
Contributor

Choose a reason for hiding this comment

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

This will slightly change after you rebase, but should be easy enough to fix

@bors
Copy link
Collaborator

bors commented Dec 6, 2025

☔ The latest upstream changes (presumably #149701) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-attributes Area: Attributes (`#[…]`, `#![…]`) perf-regression Performance regression. perf-regression-triaged The performance regression has been triaged. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants