From 186ea6e1e6cbd0edf35ae6aca4abf441856f168f Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Thu, 7 Aug 2025 10:41:43 +0200 Subject: Only run the pull workflow once per week --- src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 04d6469aeaa..5ff3118960d 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -3,8 +3,8 @@ name: rustc-pull on: workflow_dispatch: schedule: - # Run at 04:00 UTC every Monday and Thursday - - cron: '0 4 * * 1,4' + # Run at 04:00 UTC every Monday + - cron: '0 4 * * 1' jobs: pull: -- cgit 1.4.1-3-g733a5 From 9ade653e956e25c7a41d156d22b0de2cdd9ecb71 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Fri, 8 Aug 2025 15:58:53 -0700 Subject: Remove mentions of Discord. The official Discord server is no longer active (cannot be posted to). --- src/doc/rustc-dev-guide/src/about-this-guide.md | 1 - src/doc/rustc-dev-guide/src/diagnostics/error-codes.md | 2 +- src/doc/rustc-dev-guide/src/getting-started.md | 17 +++++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index 057e4a4ccee..f3957724967 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -74,7 +74,6 @@ You might also find the following sites useful: of the team procedures, active working groups, and the team calendar. - [std-dev-guide] -- a similar guide for developing the standard library. - [The t-compiler zulip][z] -- `#contribute` and `#wg-rustup` on [Discord](https://discord.gg/rust-lang). - The [Rust Internals forum][rif], a place to ask questions and discuss Rust's internals - The [Rust reference][rr], even though it doesn't specifically talk about diff --git a/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md b/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md index 1b6b87e4c8d..1693432b90d 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md @@ -20,7 +20,7 @@ explanations should help users understand why their code cannot be accepted by the compiler. Rust prides itself on helpful error messages and long-form explanations are no exception. However, before error explanations are overhauled[^new-explanations] it is a bit open as to how exactly they should be -written, as always: ask your reviewer or ask around on the Rust Discord or Zulip. +written, as always: ask your reviewer or ask around on the Rust Zulip. [^new-explanations]: See the draft RFC [here][new-explanations-rfc]. diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 04d2e37732f..a4272a401d1 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -11,7 +11,6 @@ quick guide for the most useful things. For more information, [see this chapter on how to build and run the compiler](./building/how-to-build-and-run.md). [internals]: https://internals.rust-lang.org -[rust-discord]: http://discord.gg/rust-lang [rust-zulip]: https://rust-lang.zulipchat.com [coc]: https://www.rust-lang.org/policies/code-of-conduct [walkthrough]: ./walkthrough.md @@ -20,8 +19,7 @@ chapter on how to build and run the compiler](./building/how-to-build-and-run.md ## Asking Questions If you have questions, please make a post on the [Rust Zulip server][rust-zulip] or -[internals.rust-lang.org][internals]. If you are contributing to Rustup, be aware they are not on -Zulip - you can ask questions in `#wg-rustup` [on Discord][rust-discord]. +[internals.rust-lang.org][internals]. See the [list of teams and working groups][governance] and [the Community page][community] on the official website for more resources. @@ -30,10 +28,12 @@ official website for more resources. As a reminder, all contributors are expected to follow our [Code of Conduct][coc]. -The compiler team (or `t-compiler`) usually hangs out in Zulip [in this -"stream"][z]; it will be easiest to get questions answered there. +The compiler team (or `t-compiler`) usually hangs out in Zulip in +[the #t-compiler channel][z-t-compiler]; +questions about how the compiler works can go in [#t-compiler/help][z-help]. -[z]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler +[z-t-compiler]: https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler +[z-help]: https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp **Please ask questions!** A lot of people report feeling that they are "wasting expert time", but nobody on `t-compiler` feels this way. Contributors are @@ -162,15 +162,12 @@ incredibly helpful: - [Triaging issues][triage]: categorizing, replicating, and minimizing issues is very helpful to the Rust maintainers. - [Working groups][wg]: there are a bunch of working groups on a wide variety of rust-related things. -- Answer questions in the _Get Help!_ channels on the [Rust Discord - server][rust-discord], on [users.rust-lang.org][users], or on - [StackOverflow][so]. +- Answer questions on [users.rust-lang.org][users], or on [Stack Overflow][so]. - Participate in the [RFC process](https://github.com/rust-lang/rfcs). - Find a [requested community library][community-library], build it, and publish it to [Crates.io](http://crates.io). Easier said than done, but very, very valuable! -[rust-discord]: https://discord.gg/rust-lang [users]: https://users.rust-lang.org/ [so]: http://stackoverflow.com/questions/tagged/rust [community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library -- cgit 1.4.1-3-g733a5 From c12f83e3acf3873876f0efdc4897d6012988a879 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 11 Aug 2025 10:41:00 +0800 Subject: add a tip for english writing --- src/doc/rustc-dev-guide/src/getting-started.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index a4272a401d1..87e26d37968 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -36,13 +36,15 @@ questions about how the compiler works can go in [#t-compiler/help][z-help]. [z-help]: https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp **Please ask questions!** A lot of people report feeling that they are "wasting -expert time", but nobody on `t-compiler` feels this way. Contributors are +expert's time", but nobody on `t-compiler` feels this way. Contributors are important to us. Also, if you feel comfortable, prefer public topics, as this means others can see the questions and answers, and perhaps even integrate them back into this guide :) +**Tip**: If you're not a native English speaker and feel unsure about writing, try using a translator to help. But avoid using LLM tools that generate long, complex words. In daily teamwork, **simple and clear words** are best for easy understanding. Even small typos or grammar mistakes can make you seem more human, and people connect better with humans. + ### Experts Not all `t-compiler` members are experts on all parts of `rustc`; it's a -- cgit 1.4.1-3-g733a5 From 97564bd0707fb6928cea73ed7c61ffda87d1fb3a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 11 Aug 2025 09:15:33 +0200 Subject: place link on the more suitable text Also, remove redundant word --- src/doc/rustc-dev-guide/src/git.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index 447c6fd4546..687830a7237 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -338,7 +338,7 @@ your fork with `git push --force-with-lease`. ### Keeping things up to date -The above section on [Rebasing](#rebasing) is a specific +The [above section](#rebasing) is a specific guide on rebasing work and dealing with merge conflicts. Here is some general advice about how to keep your local repo up-to-date with upstream changes: -- cgit 1.4.1-3-g733a5 From 538299a6f5fb34239bd4992786a19e5bd728737c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 11 Aug 2025 09:20:01 +0200 Subject: fix grammar --- src/doc/rustc-dev-guide/src/git.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index 687830a7237..8f0511a4548 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -344,7 +344,7 @@ Here is some general advice about how to keep your local repo up-to-date with upstream changes: Using `git pull upstream master` while on your local master branch regularly -will keep it up-to-date. You will also want to rebase your feature branches +will keep it up-to-date. You will also want to keep your feature branches up-to-date as well. After pulling, you can checkout the feature branches and rebase them: -- cgit 1.4.1-3-g733a5 From e17989edc7dfcf923626964cb8d59a570f32c8bf Mon Sep 17 00:00:00 2001 From: ywxt Date: Fri, 15 Aug 2025 14:16:32 +0800 Subject: Add the directive `compare-output-by-lines` --- src/doc/rustc-dev-guide/src/tests/directives.md | 1 + src/doc/rustc-dev-guide/src/tests/ui.md | 1 + src/tools/compiletest/src/directives.rs | 9 +++++++++ .../compiletest/src/directives/directive_names.rs | 1 + src/tools/compiletest/src/runtest.rs | 6 +++++- .../parallel-rustc/cache-after-waiting-issue-111528.rs | 14 ++++++++------ .../cache-after-waiting-issue-111528.stderr | 2 +- tests/ui/parallel-rustc/cycle_crash-issue-135870.rs | 8 ++++++++ .../ui/parallel-rustc/cycle_crash-issue-135870.stderr | 18 ++++++++++++++++++ tests/ui/parallel-rustc/cycle_crash.rs | 5 ----- tests/ui/parallel-rustc/cycle_crash.stderr | 18 ------------------ .../export-symbols-deadlock-issue-118205-2.rs | 4 ++++ .../export-symbols-deadlock-issue-118205.rs | 3 +++ tests/ui/parallel-rustc/hello_world.rs | 3 +++ .../parallel-rustc/read-stolen-value-issue-111520.rs | 11 +++++++---- 15 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 tests/ui/parallel-rustc/cycle_crash-issue-135870.rs create mode 100644 tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr delete mode 100644 tests/ui/parallel-rustc/cycle_crash.rs delete mode 100644 tests/ui/parallel-rustc/cycle_crash.stderr diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index f4ba9a044e6..fbbeb7e97d3 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -111,6 +111,7 @@ for more details. | `forbid-output` | A pattern which must not appear in stderr/`cfail` output | `ui`, `incremental` | Regex pattern | | `run-flags` | Flags passed to the test executable | `ui` | Arbitrary flags | | `known-bug` | No error annotation needed due to known bug | `ui`, `crashes`, `incremental` | Issue number `#123456` | +| `compare-output-by-lines` | Compare the output by lines, rather than as a single string | All | N/A | [^check_stdout]: presently this has a weird quirk where the test binary's stdout and stderr gets concatenated and then diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 25dd5814cf6..d3a2c406402 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -95,6 +95,7 @@ will check for output files: [Normalization](#normalization)). - `dont-check-compiler-stderr` — Ignores stderr from the compiler. - `dont-check-compiler-stdout` — Ignores stdout from the compiler. +- `compare-output-by-lines` — Some tests have non-deterministic orders of output, so we need to compare by lines. UI tests run with `-Zdeduplicate-diagnostics=no` flag which disables rustc's built-in diagnostic deduplication mechanism. This means you may see some diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 13e694b7f03..027ee063a49 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -205,6 +205,8 @@ pub struct TestProps { pub dont_require_annotations: HashSet, /// Whether pretty printers should be disabled in gdb. pub disable_gdb_pretty_printers: bool, + /// Compare the output by lines, rather than as a single string. + pub compare_output_by_lines: bool, } mod directives { @@ -254,6 +256,7 @@ mod directives { // This isn't a real directive, just one that is probably mistyped often pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags"; pub const DISABLE_GDB_PRETTY_PRINTERS: &'static str = "disable-gdb-pretty-printers"; + pub const COMPARE_OUTPUT_BY_LINES: &'static str = "compare-output-by-lines"; } impl TestProps { @@ -310,6 +313,7 @@ impl TestProps { add_core_stubs: false, dont_require_annotations: Default::default(), disable_gdb_pretty_printers: false, + compare_output_by_lines: false, } } @@ -664,6 +668,11 @@ impl TestProps { DISABLE_GDB_PRETTY_PRINTERS, &mut self.disable_gdb_pretty_printers, ); + config.set_name_directive( + ln, + COMPARE_OUTPUT_BY_LINES, + &mut self.compare_output_by_lines, + ); }, ); diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index f7955429d83..9bdd8e9fa11 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -17,6 +17,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "check-run-results", "check-stdout", "check-test-line-numbers-match", + "compare-output-by-lines", "compile-flags", "disable-gdb-pretty-printers", "doc-flags", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index be4663fffbe..af35302b55b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2749,7 +2749,11 @@ impl<'test> TestCx<'test> { // Wrapper tools set by `runner` might provide extra output on failure, // for example a WebAssembly runtime might print the stack trace of an // `unreachable` instruction by default. - let compare_output_by_lines = self.config.runner.is_some(); + // + // Also, some tests like `ui/parallel-rustc` have non-deterministic + // orders of output, so we need to compare by lines. + let compare_output_by_lines = + self.props.compare_output_by_lines || self.config.runner.is_some(); let tmp; let (expected, actual): (&str, &str) = if compare_output_by_lines { diff --git a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs index 73d173022f6..a357040a4e4 100644 --- a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs +++ b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs @@ -1,16 +1,18 @@ +// Test for #111528, the ice issue cause waiting on a query that panicked +// //@ compile-flags: -Z threads=16 //@ build-fail +//@ compare-output-by-lines -#![crate_type="rlib"] +#![crate_type = "rlib"] #![allow(warnings)] -#[export_name="fail"] -pub fn a() { -} +#[export_name = "fail"] +pub fn a() {} -#[export_name="fail"] +#[export_name = "fail"] pub fn b() { -//~^ ERROR symbol `fail` is already defined + //~^ ERROR symbol `fail` is already defined } fn main() {} diff --git a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr index 7963165e31b..80f63733fb3 100644 --- a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr +++ b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr @@ -1,5 +1,5 @@ error: symbol `fail` is already defined - --> $DIR/cache-after-waiting-issue-111528.rs:12:1 + --> $DIR/cache-after-waiting-issue-111528.rs:14:1 | LL | pub fn b() { | ^^^^^^^^^^ diff --git a/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs b/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs new file mode 100644 index 00000000000..4407e3aca80 --- /dev/null +++ b/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs @@ -0,0 +1,8 @@ +// Test for #135870, which causes a deadlock bug +// +//@ compile-flags: -Z threads=2 +//@ compare-output-by-lines + +const FOO: usize = FOO; //~ ERROR cycle detected when simplifying constant for the type system `FOO` + +fn main() {} diff --git a/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr new file mode 100644 index 00000000000..6e588d1f894 --- /dev/null +++ b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr @@ -0,0 +1,18 @@ +error[E0391]: cycle detected when simplifying constant for the type system `FOO` + --> $DIR/cycle_crash-issue-135870.rs:6:1 + | +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/cycle_crash-issue-135870.rs:6:20 + | +LL | const FOO: usize = FOO; + | ^^^ + = note: ...which again requires simplifying constant for the type system `FOO`, completing the cycle + = note: cycle used when running analysis passes on this crate + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/parallel-rustc/cycle_crash.rs b/tests/ui/parallel-rustc/cycle_crash.rs deleted file mode 100644 index 94ae11aef39..00000000000 --- a/tests/ui/parallel-rustc/cycle_crash.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ compile-flags: -Z threads=2 - -const FOO: usize = FOO; //~ERROR cycle detected when simplifying constant for the type system `FOO` - -fn main() {} diff --git a/tests/ui/parallel-rustc/cycle_crash.stderr b/tests/ui/parallel-rustc/cycle_crash.stderr deleted file mode 100644 index 7af3b8ee532..00000000000 --- a/tests/ui/parallel-rustc/cycle_crash.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0391]: cycle detected when simplifying constant for the type system `FOO` - --> $DIR/cycle_crash.rs:3:1 - | -LL | const FOO: usize = FOO; - | ^^^^^^^^^^^^^^^^ - | -note: ...which requires const-evaluating + checking `FOO`... - --> $DIR/cycle_crash.rs:3:20 - | -LL | const FOO: usize = FOO; - | ^^^ - = note: ...which again requires simplifying constant for the type system `FOO`, completing the cycle - = note: cycle used when running analysis passes on this crate - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs index 024df728736..523b1b2d1f3 100644 --- a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs +++ b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs @@ -1,6 +1,10 @@ +// Test for #118205, which causes a deadlock bug +// //@ compile-flags:-C extra-filename=-1 -Z threads=16 //@ no-prefer-dynamic //@ build-pass +//@ compare-output-by-lines + #![crate_name = "crateresolve1"] #![crate_type = "lib"] diff --git a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs index 3ccc1ea5f10..65f99c30643 100644 --- a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs +++ b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs @@ -1,5 +1,8 @@ +// Test for #118205, which causes a deadlock bug +// //@ compile-flags: -Z threads=16 //@ build-pass +//@ compare-output-by-lines pub static GLOBAL: isize = 3; diff --git a/tests/ui/parallel-rustc/hello_world.rs b/tests/ui/parallel-rustc/hello_world.rs index 56698fe2489..57891b92da0 100644 --- a/tests/ui/parallel-rustc/hello_world.rs +++ b/tests/ui/parallel-rustc/hello_world.rs @@ -1,5 +1,8 @@ +// Test for the basic function of parallel front end +// //@ compile-flags: -Z threads=8 //@ run-pass +//@ compare-output-by-lines fn main() { println!("Hello world!"); diff --git a/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs b/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs index ea8ecb67859..a6b37e62913 100644 --- a/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs +++ b/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs @@ -1,18 +1,21 @@ +// Test for #111520, which causes an ice bug cause of reading stolen value +// //@ compile-flags: -Z threads=16 //@ run-pass +//@ compare-output-by-lines #[repr(transparent)] struct Sched { i: i32, } impl Sched { - extern "C" fn get(self) -> i32 { self.i } + extern "C" fn get(self) -> i32 { + self.i + } } fn main() { let s = Sched { i: 4 }; - let f = || -> i32 { - s.get() - }; + let f = || -> i32 { s.get() }; println!("f: {}", f()); } -- cgit 1.4.1-3-g733a5 From 36ab1bf3676bce6f9b5a7359b118dc0b73c749ad Mon Sep 17 00:00:00 2001 From: ywxt Date: Fri, 15 Aug 2025 14:16:47 +0800 Subject: Add more tests --- .../ui/parallel-rustc/ty-variance-issue-124423.rs | 58 +++++ .../parallel-rustc/ty-variance-issue-124423.stderr | 287 +++++++++++++++++++++ .../ui/parallel-rustc/ty-variance-issue-127971.rs | 25 ++ .../parallel-rustc/ty-variance-issue-127971.stderr | 113 ++++++++ .../undefined-function-issue-120760.rs | 71 +++++ .../undefined-function-issue-120760.stderr | 35 +++ .../parallel-rustc/unexpected-type-issue-120601.rs | 28 ++ .../unexpected-type-issue-120601.stderr | 52 ++++ 8 files changed, 669 insertions(+) create mode 100644 tests/ui/parallel-rustc/ty-variance-issue-124423.rs create mode 100644 tests/ui/parallel-rustc/ty-variance-issue-124423.stderr create mode 100644 tests/ui/parallel-rustc/ty-variance-issue-127971.rs create mode 100644 tests/ui/parallel-rustc/ty-variance-issue-127971.stderr create mode 100644 tests/ui/parallel-rustc/undefined-function-issue-120760.rs create mode 100644 tests/ui/parallel-rustc/undefined-function-issue-120760.stderr create mode 100644 tests/ui/parallel-rustc/unexpected-type-issue-120601.rs create mode 100644 tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr diff --git a/tests/ui/parallel-rustc/ty-variance-issue-124423.rs b/tests/ui/parallel-rustc/ty-variance-issue-124423.rs new file mode 100644 index 00000000000..8d7f29f7764 --- /dev/null +++ b/tests/ui/parallel-rustc/ty-variance-issue-124423.rs @@ -0,0 +1,58 @@ +// Test for #124423, which causes an ice bug: only `variances_of` returns `&[ty::Variance]` +// +//@ compile-flags: -Z threads=16 +//@ compare-output-by-lines + +use std::fmt::Debug; + +fn elided(_: &impl Copy + 'a) -> _ { x } +//~^ ERROR ambiguous `+` in a type +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR the placeholder `_` is not allowed within types on item signatures for return types + +fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } +//~^ ERROR ambiguous `+` in a type +//~| ERROR at least one trait must be specified +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR use of undeclared lifetime name `'a` + +fn elided2( impl 'b) -> impl 'a + 'a { x } +//~^ ERROR expected one of `:` or `|`, found `'b` +//~| ERROR expected identifier, found keyword `impl` +//~| ERROR at least one trait must be specified +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR use of undeclared lifetime name `'a` + +fn explicit2<'a>(_: &'a impl Copy + 'a) -> impl Copy + 'a { x } +//~^ ERROR ambiguous `+` in a type + +fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } +//~^ ERROR ambiguous `+` in a type +//~| ERROR at least one trait must be specified +//~| ERROR use of undeclared lifetime name `'b` + +fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } +//~^ ERROR ambiguous `+` in a type +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR at least one trait is required for an object type + +fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } +//~^ ERROR ambiguous `+` in a type +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR at least one trait is required for an object type +//~| ERROR no function or associated item named `u32` found for struct `Box<_, _>` in the current scope + +fn elided4(_: &impl Copy + 'a) -> new { x(x) } +//~^ ERROR ambiguous `+` in a type +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR cannot find type `new` in this scope + +trait LifetimeTrait<'a> {} + +impl<'a> LifetimeTrait<'a> for &'a Box {} +//~^ ERROR at least one trait is required for an object type + +fn main() {} diff --git a/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr b/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr new file mode 100644 index 00000000000..7ba89f75bd1 --- /dev/null +++ b/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr @@ -0,0 +1,287 @@ +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-124423.rs:8:15 + | +LL | fn elided(_: &impl Copy + 'a) -> _ { x } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn elided(_: &(impl Copy + 'a)) -> _ { x } + | + + + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-124423.rs:13:24 + | +LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn explicit<'b>(_: &'a (impl Copy + 'a)) -> impl 'a { x } + | + + + +error: expected identifier, found keyword `impl` + --> $DIR/ty-variance-issue-124423.rs:20:13 + | +LL | fn elided2( impl 'b) -> impl 'a + 'a { x } + | ^^^^ expected identifier, found keyword + +error: expected one of `:` or `|`, found `'b` + --> $DIR/ty-variance-issue-124423.rs:20:18 + | +LL | fn elided2( impl 'b) -> impl 'a + 'a { x } + | ^^ expected one of `:` or `|` + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-124423.rs:27:25 + | +LL | fn explicit2<'a>(_: &'a impl Copy + 'a) -> impl Copy + 'a { x } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn explicit2<'a>(_: &'a (impl Copy + 'a)) -> impl Copy + 'a { x } + | + + + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-124423.rs:30:16 + | +LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn foo<'a>(_: &(impl Copy + 'a)) -> impl 'b + 'a { x } + | + + + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-124423.rs:35:16 + | +LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn elided3(_: &(impl Copy + 'a)) -> Box { Box::new(x) } + | + + + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-124423.rs:41:17 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn x<'b>(_: &'a (impl Copy + 'a)) -> Box { Box::u32(x) } + | + + + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-124423.rs:48:16 + | +LL | fn elided4(_: &impl Copy + 'a) -> new { x(x) } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn elided4(_: &(impl Copy + 'a)) -> new { x(x) } + | + + + +error: at least one trait must be specified + --> $DIR/ty-variance-issue-124423.rs:13:43 + | +LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | ^^^^^^^ + +error: at least one trait must be specified + --> $DIR/ty-variance-issue-124423.rs:20:25 + | +LL | fn elided2( impl 'b) -> impl 'a + 'a { x } + | ^^^^^^^^^^^^ + +error: at least one trait must be specified + --> $DIR/ty-variance-issue-124423.rs:30:35 + | +LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | ^^^^^^^^^^^^ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:8:27 + | +LL | fn elided(_: &impl Copy + 'a) -> _ { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn elided<'a>(_: &impl Copy + 'a) -> _ { x } + | ++++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:13:21 + | +LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn explicit<'a, 'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:13:36 + | +LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn explicit<'a, 'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:13:48 + | +LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn explicit<'a, 'b>(_: &'a impl Copy + 'a) -> impl 'a { x } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:20:30 + | +LL | fn elided2( impl 'b) -> impl 'a + 'a { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn elided2<'a>( impl 'b) -> impl 'a + 'a { x } + | ++++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:20:35 + | +LL | fn elided2( impl 'b) -> impl 'a + 'a { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn elided2<'a>( impl 'b) -> impl 'a + 'a { x } + | ++++ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/ty-variance-issue-124423.rs:30:40 + | +LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'b` here + | +LL | fn foo<'b, 'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:35:28 + | +LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn elided3<'a>(_: &impl Copy + 'a) -> Box { Box::new(x) } + | ++++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:35:43 + | +LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn elided3<'a>(_: &impl Copy + 'a) -> Box { Box::new(x) } + | ++++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:41:14 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:41:29 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-124423.rs:48:28 + | +LL | fn elided4(_: &impl Copy + 'a) -> new { x(x) } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn elided4<'a>(_: &impl Copy + 'a) -> new { x(x) } + | ++++ + +error[E0412]: cannot find type `new` in this scope + --> $DIR/ty-variance-issue-124423.rs:48:36 + | +LL | fn elided4(_: &impl Copy + 'a) -> new { x(x) } + | ^^^ not found in this scope + +error[E0224]: at least one trait is required for an object type + --> $DIR/ty-variance-issue-124423.rs:35:39 + | +LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } + | ^^^^^^ + +error[E0224]: at least one trait is required for an object type + --> $DIR/ty-variance-issue-124423.rs:41:40 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^^^^^ + +error[E0224]: at least one trait is required for an object type + --> $DIR/ty-variance-issue-124423.rs:55:40 + | +LL | impl<'a> LifetimeTrait<'a> for &'a Box {} + | ^^^^^^ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/ty-variance-issue-124423.rs:8:34 + | +LL | fn elided(_: &impl Copy + 'a) -> _ { x } + | ^ not allowed in type signatures + +error[E0599]: no function or associated item named `u32` found for struct `Box<_, _>` in the current scope + --> $DIR/ty-variance-issue-124423.rs:41:55 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^^ function or associated item not found in `Box<_, _>` + | +note: if you're trying to build a new `Box<_, _>` consider using one of the following associated functions: + Box::::new + Box::::new_uninit + Box::::new_zeroed + Box::::try_new + and 22 others + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + +error: aborting due to 30 previous errors + +Some errors have detailed explanations: E0121, E0224, E0261, E0412, E0599. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/parallel-rustc/ty-variance-issue-127971.rs b/tests/ui/parallel-rustc/ty-variance-issue-127971.rs new file mode 100644 index 00000000000..a17916843e7 --- /dev/null +++ b/tests/ui/parallel-rustc/ty-variance-issue-127971.rs @@ -0,0 +1,25 @@ +// Test for #127971, which causes an ice bug: only `variances_of` returns `&[ty::Variance]` +// +//@ compile-flags: -Z threads=16 +//@ compare-output-by-lines + +use std::fmt::Debug; + +fn elided(_: &impl Copy + 'a) -> _ { x } +//~^ ERROR ambiguous `+` in a type +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR the placeholder `_` is not allowed within types on item signatures for return types + +fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } +//~^ ERROR ambiguous `+` in a type +//~| ERROR at least one trait must be specified +//~| ERROR use of undeclared lifetime name `'b` + +fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } +//~^ ERROR ambiguous `+` in a type +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR use of undeclared lifetime name `'a` +//~| ERROR at least one trait is required for an object type +//~| ERROR no function or associated item named `u32` found for struct `Box<_, _>` in the current scope + +fn main() {} diff --git a/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr b/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr new file mode 100644 index 00000000000..9929d3ee22c --- /dev/null +++ b/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr @@ -0,0 +1,113 @@ +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-127971.rs:8:15 + | +LL | fn elided(_: &impl Copy + 'a) -> _ { x } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn elided(_: &(impl Copy + 'a)) -> _ { x } + | + + + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-127971.rs:13:16 + | +LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn foo<'a>(_: &(impl Copy + 'a)) -> impl 'b + 'a { x } + | + + + +error: ambiguous `+` in a type + --> $DIR/ty-variance-issue-127971.rs:18:17 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^^^^^^^^^^^^^ + | +help: try adding parentheses + | +LL | fn x<'b>(_: &'a (impl Copy + 'a)) -> Box { Box::u32(x) } + | + + + +error: at least one trait must be specified + --> $DIR/ty-variance-issue-127971.rs:13:35 + | +LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | ^^^^^^^^^^^^ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-127971.rs:8:27 + | +LL | fn elided(_: &impl Copy + 'a) -> _ { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn elided<'a>(_: &impl Copy + 'a) -> _ { x } + | ++++ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/ty-variance-issue-127971.rs:13:40 + | +LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'b` here + | +LL | fn foo<'b, 'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-127971.rs:18:14 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ty-variance-issue-127971.rs:18:29 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | +++ + +error[E0224]: at least one trait is required for an object type + --> $DIR/ty-variance-issue-127971.rs:18:40 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^^^^^ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/ty-variance-issue-127971.rs:8:34 + | +LL | fn elided(_: &impl Copy + 'a) -> _ { x } + | ^ not allowed in type signatures + +error[E0599]: no function or associated item named `u32` found for struct `Box<_, _>` in the current scope + --> $DIR/ty-variance-issue-127971.rs:18:55 + | +LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } + | ^^^ function or associated item not found in `Box<_, _>` + | +note: if you're trying to build a new `Box<_, _>` consider using one of the following associated functions: + Box::::new + Box::::new_uninit + Box::::new_zeroed + Box::::try_new + and 22 others + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0121, E0224, E0261, E0599. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/parallel-rustc/undefined-function-issue-120760.rs b/tests/ui/parallel-rustc/undefined-function-issue-120760.rs new file mode 100644 index 00000000000..2665c30945b --- /dev/null +++ b/tests/ui/parallel-rustc/undefined-function-issue-120760.rs @@ -0,0 +1,71 @@ +// Test for #120760, which causes an ice bug: no index for a field +// +//@ compile-flags: -Z threads=45 +//@ edition: 2021 +//@ compare-output-by-lines + +type BoxFuture = std::pin::Pin>>; + +fn main() { + let _ = f(); +} + +async fn f() { + run("dependency").await; //~ ERROR cannot find function `run` in this scope +} + +struct InMemoryStorage; + +pub struct User<'dep> { + pub name: &'a str, //~ ERROR use of undeclared lifetime name `'a` +} + +impl<'a> StorageRequest for SaveUser<'a> { + fn execute(&self) -> BoxFuture> { + todo!() + } +} + +trait Storage { + type Error; +} + +impl Storage for InMemoryStorage { + type Error = String; +} + +trait StorageRequestReturnType { + type Output; +} + +trait StorageRequest: StorageRequestReturnType { + fn execute( + &self, + ) -> BoxFuture::Output, ::Error>>; +} + +pub struct SaveUser<'a> { + pub name: &'a str, +} + +impl<'a> StorageRequestReturnType for SaveUser<'a> { + type Output = (); +} + +impl<'dep> User<'dep> { + async fn save(self) + where + S: Storage, + for<'a> SaveUser<'a>: StorageRequest, + { + let _ = run("dependency").await; //~ ERROR cannot find function `run` in this scope + } +} + +async fn execute(dep: &str) +where + S: Storage, + for<'a> SaveUser<'a>: StorageRequest, +{ + User { dep }.save().await; //~ ERROR struct `User<'_>` has no field named `dep` +} diff --git a/tests/ui/parallel-rustc/undefined-function-issue-120760.stderr b/tests/ui/parallel-rustc/undefined-function-issue-120760.stderr new file mode 100644 index 00000000000..87af5372219 --- /dev/null +++ b/tests/ui/parallel-rustc/undefined-function-issue-120760.stderr @@ -0,0 +1,35 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/undefined-function-issue-120760.rs:20:16 + | +LL | pub name: &'a str, + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | pub struct User<'a, 'dep> { + | +++ + +error[E0425]: cannot find function `run` in this scope + --> $DIR/undefined-function-issue-120760.rs:14:5 + | +LL | run("dependency").await; + | ^^^ not found in this scope + +error[E0425]: cannot find function `run` in this scope + --> $DIR/undefined-function-issue-120760.rs:61:17 + | +LL | let _ = run("dependency").await; + | ^^^ not found in this scope + +error[E0560]: struct `User<'_>` has no field named `dep` + --> $DIR/undefined-function-issue-120760.rs:70:12 + | +LL | User { dep }.save().await; + | ^^^ `User<'_>` does not have this field + | + = note: available fields are: `name` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0261, E0425, E0560. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/parallel-rustc/unexpected-type-issue-120601.rs b/tests/ui/parallel-rustc/unexpected-type-issue-120601.rs new file mode 100644 index 00000000000..2e215aa301a --- /dev/null +++ b/tests/ui/parallel-rustc/unexpected-type-issue-120601.rs @@ -0,0 +1,28 @@ +// Test for #120601, which causes an ice bug cause of unexpected type +// +//@ compile-flags: -Z threads=40 +//@ compare-output-by-lines + +struct T; +struct Tuple(i32); + +async fn foo() -> Result<(), ()> { + Unstable2(()) +} +//~^^^ ERROR `async fn` is not permitted in Rust 2015 +//~^^^ ERROR cannot find function, tuple struct or tuple variant `Unstable2` in this scope + +async fn tuple() -> Tuple { + Tuple(1i32) +} +//~^^^ ERROR `async fn` is not permitted in Rust 2015 + +async fn match_() { + match tuple() { + Tuple(_) => {} + } +} +//~^^^^^ ERROR `async fn` is not permitted in Rust 2015 +//~^^^^ ERROR mismatched types + +fn main() {} diff --git a/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr b/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr new file mode 100644 index 00000000000..ed563bb0c4e --- /dev/null +++ b/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr @@ -0,0 +1,52 @@ +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/unexpected-type-issue-120601.rs:9:1 + | +LL | async fn foo() -> Result<(), ()> { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/unexpected-type-issue-120601.rs:15:1 + | +LL | async fn tuple() -> Tuple { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/unexpected-type-issue-120601.rs:20:1 + | +LL | async fn match_() { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0425]: cannot find function, tuple struct or tuple variant `Unstable2` in this scope + --> $DIR/unexpected-type-issue-120601.rs:10:5 + | +LL | Unstable2(()) + | ^^^^^^^^^ not found in this scope + +error[E0308]: mismatched types + --> $DIR/unexpected-type-issue-120601.rs:22:9 + | +LL | match tuple() { + | ------- this expression has type `impl Future` +LL | Tuple(_) => {} + | ^^^^^^^^ expected future, found `Tuple` + | + = note: expected opaque type `impl Future` + found struct `Tuple` +help: consider `await`ing on the `Future` + | +LL | match tuple().await { + | ++++++ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0425, E0670. +For more information about an error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From a3510c7199806c153f927f25e2bff2c095aa60df Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 18 Aug 2025 04:22:18 +0000 Subject: Prepare for merging from rust-lang/rust This updates the rust-version file to 425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 6ec700b9b4d..a399b5cd77e 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -6bcdcc73bd11568fd85f5a38b58e1eda054ad1cd +425a9c0a0e365c0b8c6cfd00c2ded83a73bed9a0 -- cgit 1.4.1-3-g733a5 From 06eb782c4e727c33d8548a791998a0a2cbedceae Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 22 Aug 2025 14:58:42 -0400 Subject: modify `LazyLock` poison panic message Fixes an issue where if the underlying `Once` panics because it is poisoned, the panic displays the wrong message. Signed-off-by: Connor Tsui --- library/std/src/sync/lazy_lock.rs | 9 ++-- library/std/tests/sync/lazy_lock.rs | 84 +++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index a40e29a772a..3231125f7a1 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -244,7 +244,11 @@ impl T> LazyLock { #[inline] #[stable(feature = "lazy_cell", since = "1.80.0")] pub fn force(this: &LazyLock) -> &T { - this.once.call_once(|| { + this.once.call_once_force(|state| { + if state.is_poisoned() { + panic_poisoned(); + } + // SAFETY: `call_once` only runs this closure once, ever. let data = unsafe { &mut *this.data.get() }; let f = unsafe { ManuallyDrop::take(&mut data.f) }; @@ -257,8 +261,7 @@ impl T> LazyLock { // * the closure was called and initialized `value`. // * the closure was called and panicked, so this point is never reached. // * the closure was not called, but a previous call initialized `value`. - // * the closure was not called because the Once is poisoned, so this point - // is never reached. + // * the closure was not called because the Once is poisoned, which we handled above. // So `value` has definitely been initialized and will not be modified again. unsafe { &*(*this.data.get()).value } } diff --git a/library/std/tests/sync/lazy_lock.rs b/library/std/tests/sync/lazy_lock.rs index 6c14b79f2ce..68aeea834b4 100644 --- a/library/std/tests/sync/lazy_lock.rs +++ b/library/std/tests/sync/lazy_lock.rs @@ -33,16 +33,6 @@ fn lazy_default() { assert_eq!(CALLED.load(SeqCst), 1); } -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn lazy_poisoning() { - let x: LazyCell = LazyCell::new(|| panic!("kaboom")); - for _ in 0..2 { - let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); - assert!(res.is_err()); - } -} - #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn sync_lazy_new() { @@ -123,16 +113,6 @@ fn static_sync_lazy_via_fn() { assert_eq!(xs(), &vec![1, 2, 3]); } -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn sync_lazy_poisoning() { - let x: LazyLock = LazyLock::new(|| panic!("kaboom")); - for _ in 0..2 { - let res = panic::catch_unwind(|| x.len()); - assert!(res.is_err()); - } -} - // Check that we can infer `T` from closure's type. #[test] fn lazy_type_inference() { @@ -145,17 +125,6 @@ fn is_sync_send() { assert_traits::>(); } -#[test] -#[should_panic = "has previously been poisoned"] -fn lazy_force_mut_panic() { - let mut lazy = LazyLock::::new(|| panic!()); - panic::catch_unwind(panic::AssertUnwindSafe(|| { - let _ = LazyLock::force_mut(&mut lazy); - })) - .unwrap_err(); - let _ = &*lazy; -} - #[test] fn lazy_force_mut() { let s = "abc".to_owned(); @@ -165,3 +134,56 @@ fn lazy_force_mut() { p.clear(); LazyLock::force_mut(&mut lazy); } + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn lazy_poisoning() { + let x: LazyCell = LazyCell::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); + assert!(res.is_err()); + } +} + +/// Verifies that when a `LazyLock` is poisoned, it panics with the correct error message ("LazyLock +/// instance has previously been poisoned") instead of the underlying `Once` error message. +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[should_panic(expected = "LazyLock instance has previously been poisoned")] +fn lazy_lock_deref_panic() { + let lazy: LazyLock = LazyLock::new(|| panic!("initialization failed")); + + // First access will panic during initialization. + let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { + let _ = &*lazy; + })); + + // Second access should panic with the poisoned message. + let _ = &*lazy; +} + +#[test] +#[should_panic(expected = "LazyLock instance has previously been poisoned")] +fn lazy_lock_deref_mut_panic() { + let mut lazy: LazyLock = LazyLock::new(|| panic!("initialization failed")); + + // First access will panic during initialization. + let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { + let _ = LazyLock::force_mut(&mut lazy); + })); + + // Second access should panic with the poisoned message. + let _ = &*lazy; +} + +/// Verifies that when the initialization closure panics with a custom message, that message is +/// preserved and not overridden by `LazyLock`. +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[should_panic(expected = "custom panic message from closure")] +fn lazy_lock_preserves_closure_panic_message() { + let lazy: LazyLock = LazyLock::new(|| panic!("custom panic message from closure")); + + // This should panic with the original message from the closure. + let _ = &*lazy; +} -- cgit 1.4.1-3-g733a5 From ba3099f60b25437b7a871a3dfe7aa71bf867cd90 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 14 Feb 2025 17:34:29 +0100 Subject: Add support for macro expansion in rustdoc source code pages --- src/librustdoc/html/highlight.rs | 222 +++++++++++++++++++++++------ src/librustdoc/html/render/context.rs | 7 +- src/librustdoc/html/render/mod.rs | 2 +- src/librustdoc/html/render/span_map.rs | 114 ++++++++++++++- src/librustdoc/html/static/css/rustdoc.css | 57 ++++++-- 5 files changed, 339 insertions(+), 63 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 272180fb990..165b0c587b0 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -5,6 +5,7 @@ //! //! Use the `render_with_highlighting` to highlight some rust code. +use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::{self, Display, Write}; @@ -17,7 +18,7 @@ use rustc_span::{BytePos, DUMMY_SP, Span}; use super::format::{self, write_str}; use crate::clean::PrimitiveType; use crate::html::escape::EscapeBodyText; -use crate::html::render::{Context, LinkFromSrc}; +use crate::html::render::{Context, ExpandedCode, LinkFromSrc}; /// This type is needed in case we want to render links on items to allow to go to their definition. pub(crate) struct HrefContext<'a, 'tcx> { @@ -163,7 +164,7 @@ struct TokenHandler<'a, 'tcx, F: Write> { current_class: Option, /// We need to keep the `Class` for each element because it could contain a `Span` which is /// used to generate links. - pending_elems: Vec<(&'a str, Option)>, + pending_elems: Vec<(Cow<'a, str>, Option)>, href_context: Option>, write_line_number: fn(&mut F, u32, &'static str), } @@ -271,6 +272,62 @@ fn empty_line_number(out: &mut impl Write, _: u32, extra: &'static str) { out.write_str(extra).unwrap(); } +fn get_expansion<'a, W: Write>( + token_handler: &mut TokenHandler<'_, '_, W>, + expanded_codes: Option<&'a Vec>, + line: u32, +) -> Option<&'a ExpandedCode> { + if let Some(expanded_codes) = expanded_codes + && let Some(expanded_code) = expanded_codes.iter().find(|code| code.start_line == line) + { + let (closing, reopening) = if let Some(current_class) = token_handler.current_class + && let class = current_class.as_html() + && !class.is_empty() + { + ("", format!("")) + } else { + ("", String::new()) + }; + let id = format!("expand-{line}"); + token_handler.pending_elems.push(( + Cow::Owned(format!( + "{closing}\ +\ + \ + {reopening}", + )), + Some(Class::Expansion), + )); + Some(expanded_code) + } else { + None + } +} + +fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option)>, expanded_code: &ExpandedCode) { + out.push(( + Cow::Owned(format!( + "{}", + expanded_code.code, + )), + Some(Class::Expansion), + )); +} + +fn end_expansion(token_handler: &mut TokenHandler<'_, '_, W>, level: usize) { + if level == 0 { + token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); + return; + } + let mut out = String::new(); + let mut end = String::new(); + for (tag, class) in token_handler.closing_tags.iter().skip(token_handler.closing_tags.len() - level) { + out.push_str(tag); + end.push_str(&format!("", class.as_html())); + } + token_handler.pending_elems.push((Cow::Owned(format!("{out}{end}")), Some(Class::Expansion))); +} + #[derive(Clone, Copy)] pub(super) struct LineInfo { pub(super) start_line: u32, @@ -317,7 +374,7 @@ pub(super) fn write_code( closing_tags: Vec::new(), pending_exit_span: None, current_class: None, - pending_elems: Vec::new(), + pending_elems: Vec::with_capacity(20), href_context, write_line_number: match line_info { Some(line_info) => { @@ -338,12 +395,21 @@ pub(super) fn write_code( (0, u32::MAX) }; + let expanded_codes = token_handler + .href_context + .as_ref() + .and_then(|c| c.context.shared.expanded_codes.get(&c.file_span.lo())); + let mut current_expansion = + get_expansion(&mut token_handler, expanded_codes, line); + token_handler.write_pending_elems(None); + let mut level = 0; + Classifier::new( &src, token_handler.href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), decoration_info, ) - .highlight(&mut |highlight| { + .highlight(&mut |span, highlight| { match highlight { Highlight::Token { text, class } => { // If we received a `ExitSpan` event and then have a non-compatible `Class`, we @@ -369,10 +435,32 @@ pub(super) fn write_code( if text == "\n" { line += 1; if line < max_lines { - token_handler.pending_elems.push((text, Some(Class::Backline(line)))); + token_handler + .pending_elems + .push((Cow::Borrowed(text), Some(Class::Backline(line)))); + } + if current_expansion.is_none() { + current_expansion = + get_expansion(&mut token_handler, expanded_codes, line); } } else { - token_handler.pending_elems.push((text, class)); + token_handler.pending_elems.push((Cow::Borrowed(text), class)); + + let mut need_end = false; + if let Some(ref current_expansion) = current_expansion { + if current_expansion.span.lo() == span.hi() { + start_expansion(&mut token_handler.pending_elems, current_expansion); + } else if current_expansion.end_line == line + && span.hi() >= current_expansion.span.hi() + { + need_end = true; + } + } + if need_end { + end_expansion(&mut token_handler, level); + current_expansion = None; + level = 0; + } } } Highlight::EnterSpan { class } => { @@ -392,6 +480,9 @@ pub(super) fn write_code( if should_add { let closing_tag = enter_span(token_handler.out, class, &token_handler.href_context); + if current_expansion.is_some() { + level += 1; + } token_handler.closing_tags.push((closing_tag, class)); } @@ -400,6 +491,9 @@ pub(super) fn write_code( } Highlight::ExitSpan => { token_handler.current_class = None; + if current_expansion.is_some() { + level -= 1; + } token_handler.pending_exit_span = Some( token_handler .closing_tags @@ -440,6 +534,8 @@ enum Class { QuestionMark, Decoration(&'static str), Backline(u32), + /// Macro expansion. + Expansion, } impl Class { @@ -489,6 +585,7 @@ impl Class { Class::QuestionMark => "question-mark", Class::Decoration(kind) => kind, Class::Backline(_) => "", + Class::Expansion => "", } } @@ -513,7 +610,8 @@ impl Class { | Self::Lifetime | Self::QuestionMark | Self::Decoration(_) - | Self::Backline(_) => None, + | Self::Backline(_) + | Self::Expansion => None, } } } @@ -628,6 +726,13 @@ impl Decorations { } } +/// Convenient wrapper to create a [`Span`] from a position in the file. +fn new_span(lo: u32, text: &str, file_span: Span) -> Span { + let hi = lo + text.len() as u32; + let file_lo = file_span.lo(); + file_span.with_lo(file_lo + BytePos(lo)).with_hi(file_lo + BytePos(hi)) +} + /// Processes program tokens, classifying strings of text by highlighting /// category (`Class`). struct Classifier<'src> { @@ -660,13 +765,6 @@ impl<'src> Classifier<'src> { } } - /// Convenient wrapper to create a [`Span`] from a position in the file. - fn new_span(&self, lo: u32, text: &str) -> Span { - let hi = lo + text.len() as u32; - let file_lo = self.file_span.lo(); - self.file_span.with_lo(file_lo + BytePos(lo)).with_hi(file_lo + BytePos(hi)) - } - /// Concatenate colons and idents as one when possible. fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> { let start = self.byte_pos as usize; @@ -735,18 +833,18 @@ impl<'src> Classifier<'src> { /// The general structure for this method is to iterate over each token, /// possibly giving it an HTML span with a class specifying what flavor of /// token is used. - fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'src>)) { + fn highlight(mut self, sink: &mut dyn FnMut(Span, Highlight<'src>)) { loop { if let Some(decs) = self.decorations.as_mut() { let byte_pos = self.byte_pos; let n_starts = decs.starts.iter().filter(|(i, _)| byte_pos >= *i).count(); for (_, kind) in decs.starts.drain(0..n_starts) { - sink(Highlight::EnterSpan { class: Class::Decoration(kind) }); + sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Decoration(kind) }); } let n_ends = decs.ends.iter().filter(|i| byte_pos >= **i).count(); for _ in decs.ends.drain(0..n_ends) { - sink(Highlight::ExitSpan); + sink(DUMMY_SP, Highlight::ExitSpan); } } @@ -784,14 +882,20 @@ impl<'src> Classifier<'src> { &mut self, token: TokenKind, text: &'src str, - sink: &mut dyn FnMut(Highlight<'src>), + sink: &mut dyn FnMut(Span, Highlight<'src>), before: u32, ) { let lookahead = self.peek(); - let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None }); - let whitespace = |sink: &mut dyn FnMut(_)| { + let file_span = self.file_span; + let no_highlight = |sink: &mut dyn FnMut(_, _)| sink(new_span(before, text, file_span), Highlight::Token { text, class: None }); + let whitespace = |sink: &mut dyn FnMut(_, _)| { + let mut start = 0u32; for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) { - sink(Highlight::Token { text: part, class: None }); + sink( + new_span(before + start, part, file_span), + Highlight::Token { text: part, class: None }, + ); + start += part.len() as u32; } }; let class = match token { @@ -807,8 +911,8 @@ impl<'src> Classifier<'src> { // leading identifier. TokenKind::Bang if self.in_macro => { self.in_macro = false; - sink(Highlight::Token { text, class: None }); - sink(Highlight::ExitSpan); + sink(new_span(before, text, file_span), Highlight::Token { text, class: None }); + sink(DUMMY_SP, Highlight::ExitSpan); return; } @@ -819,12 +923,18 @@ impl<'src> Classifier<'src> { Some((TokenKind::Whitespace, _)) => return whitespace(sink), Some((TokenKind::Ident, "mut")) => { self.next(); - sink(Highlight::Token { text: "*mut", class: Some(Class::RefKeyWord) }); + sink( + DUMMY_SP, + Highlight::Token { text: "*mut", class: Some(Class::RefKeyWord) }, + ); return; } Some((TokenKind::Ident, "const")) => { self.next(); - sink(Highlight::Token { text: "*const", class: Some(Class::RefKeyWord) }); + sink( + DUMMY_SP, + Highlight::Token { text: "*const", class: Some(Class::RefKeyWord) }, + ); return; } _ => Class::RefKeyWord, @@ -832,18 +942,21 @@ impl<'src> Classifier<'src> { TokenKind::And => match self.tokens.peek() { Some((TokenKind::And, _)) => { self.next(); - sink(Highlight::Token { text: "&&", class: None }); + sink(DUMMY_SP, Highlight::Token { text: "&&", class: None }); return; } Some((TokenKind::Eq, _)) => { self.next(); - sink(Highlight::Token { text: "&=", class: None }); + sink(DUMMY_SP, Highlight::Token { text: "&=", class: None }); return; } Some((TokenKind::Whitespace, _)) => return whitespace(sink), Some((TokenKind::Ident, "mut")) => { self.next(); - sink(Highlight::Token { text: "&mut", class: Some(Class::RefKeyWord) }); + sink( + DUMMY_SP, + Highlight::Token { text: "&mut", class: Some(Class::RefKeyWord) }, + ); return; } _ => Class::RefKeyWord, @@ -853,19 +966,19 @@ impl<'src> Classifier<'src> { TokenKind::Eq => match lookahead { Some(TokenKind::Eq) => { self.next(); - sink(Highlight::Token { text: "==", class: None }); + sink(DUMMY_SP, Highlight::Token { text: "==", class: None }); return; } Some(TokenKind::Gt) => { self.next(); - sink(Highlight::Token { text: "=>", class: None }); + sink(DUMMY_SP, Highlight::Token { text: "=>", class: None }); return; } _ => return no_highlight(sink), }, TokenKind::Minus if lookahead == Some(TokenKind::Gt) => { self.next(); - sink(Highlight::Token { text: "->", class: None }); + sink(DUMMY_SP, Highlight::Token { text: "->", class: None }); return; } @@ -916,16 +1029,22 @@ impl<'src> Classifier<'src> { self.next(); if let Some(TokenKind::OpenBracket) = self.peek() { self.in_attribute = true; - sink(Highlight::EnterSpan { class: Class::Attribute }); + sink( + new_span(before, text, file_span), + Highlight::EnterSpan { class: Class::Attribute }, + ); } - sink(Highlight::Token { text: "#", class: None }); - sink(Highlight::Token { text: "!", class: None }); + sink(DUMMY_SP, Highlight::Token { text: "#", class: None }); + sink(DUMMY_SP, Highlight::Token { text: "!", class: None }); return; } // Case 2: #[outer_attribute] Some(TokenKind::OpenBracket) => { self.in_attribute = true; - sink(Highlight::EnterSpan { class: Class::Attribute }); + sink( + new_span(before, text, file_span), + Highlight::EnterSpan { class: Class::Attribute }, + ); } _ => (), } @@ -934,8 +1053,8 @@ impl<'src> Classifier<'src> { TokenKind::CloseBracket => { if self.in_attribute { self.in_attribute = false; - sink(Highlight::Token { text: "]", class: None }); - sink(Highlight::ExitSpan); + sink(new_span(before, text, file_span), Highlight::Token { text: "]", class: None }); + sink(DUMMY_SP, Highlight::ExitSpan); return; } return no_highlight(sink); @@ -956,15 +1075,16 @@ impl<'src> Classifier<'src> { TokenKind::GuardedStrPrefix => return no_highlight(sink), TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => { self.in_macro = true; - sink(Highlight::EnterSpan { class: Class::Macro(self.new_span(before, text)) }); - sink(Highlight::Token { text, class: None }); + let span = new_span(before, text, file_span); + sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Macro(span) }); + sink(span, Highlight::Token { text, class: None }); return; } TokenKind::Ident => match get_real_ident_class(text, false) { None => match text { - "Option" | "Result" => Class::PreludeTy(self.new_span(before, text)), + "Option" | "Result" => Class::PreludeTy(new_span(before, text, file_span)), "Some" | "None" | "Ok" | "Err" => { - Class::PreludeVal(self.new_span(before, text)) + Class::PreludeVal(new_span(before, text, file_span)) } // "union" is a weak keyword and is only considered as a keyword when declaring // a union type. @@ -973,13 +1093,13 @@ impl<'src> Classifier<'src> { self.in_macro_nonterminal = false; Class::MacroNonTerminal } - "self" | "Self" => Class::Self_(self.new_span(before, text)), - _ => Class::Ident(self.new_span(before, text)), + "self" | "Self" => Class::Self_(new_span(before, text, file_span)), + _ => Class::Ident(new_span(before, text, file_span)), }, Some(c) => c, }, TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => { - Class::Ident(self.new_span(before, text)) + Class::Ident(new_span(before, text, file_span)) } TokenKind::Lifetime { .. } | TokenKind::RawLifetime @@ -988,8 +1108,13 @@ impl<'src> Classifier<'src> { }; // Anything that didn't return above is the simple case where we the // class just spans a single token, so we can use the `string` method. + let mut start = 0u32; for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) { - sink(Highlight::Token { text: part, class: Some(class) }); + sink( + new_span(before + start, part, file_span), + Highlight::Token { text: part, class: Some(class) }, + ); + start += part.len() as u32; } } @@ -1042,9 +1167,9 @@ fn exit_span(out: &mut impl Write, closing_tag: &str) { /// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function /// will then try to find this `span` in the `span_correspondence_map`. If found, it'll then /// generate a link for this element (which corresponds to where its definition is located). -fn string( +fn string( out: &mut W, - text: T, + text: EscapeBodyText<'_>, klass: Option, href_context: &Option>, open_tag: bool, @@ -1052,6 +1177,9 @@ fn string( ) { if let Some(Class::Backline(line)) = klass { write_line_number_callback(out, line, "\n"); + } else if let Some(Class::Expansion) = klass { + // Nothing to escape here so we get the text to write it directly. + out.write_str(text.0).unwrap(); } else if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag) { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index e4fca09d64f..33dec1c24e6 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -12,7 +12,7 @@ use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::edition::Edition; -use rustc_span::{FileName, Symbol, sym}; +use rustc_span::{BytePos, FileName, Symbol, sym}; use tracing::info; use super::print_item::{full_path, print_item, print_item_path}; @@ -29,6 +29,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; +use crate::html::render::ExpandedCode; use crate::html::render::write_shared::write_shared; use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources, static_files}; @@ -139,6 +140,7 @@ pub(crate) struct SharedContext<'tcx> { /// Correspondence map used to link types used in the source code pages to allow to click on /// links to jump to the type's definition. pub(crate) span_correspondence_map: FxHashMap, + pub(crate) expanded_codes: FxHashMap>, /// The [`Cache`] used during rendering. pub(crate) cache: Cache, pub(crate) call_locations: AllCallLocations, @@ -548,7 +550,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } } - let (local_sources, matches) = collect_spans_and_sources( + let (local_sources, matches, expanded_codes) = collect_spans_and_sources( tcx, &krate, &src_root, @@ -579,6 +581,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { cache, call_locations, should_merge: options.should_merge, + expanded_codes, }; let dst = output; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8d7f0577506..0986b9ca47b 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -63,7 +63,7 @@ use serde::{Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; -pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; +pub(crate) use self::span_map::{ExpandedCode, LinkFromSrc, collect_spans_and_sources}; pub(crate) use self::write_shared::*; use crate::clean::{self, ItemId, RenderedLink}; use crate::display::{Joined as _, MaybeDisplay as _}; diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 846d3ad310c..842da838049 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -30,12 +30,26 @@ pub(crate) enum LinkFromSrc { Doc(DefId), } +/// Contains information about macro expansion in the source code pages. +#[derive(Debug)] +pub(crate) struct ExpandedCode { + /// The line where the macro expansion starts. + pub(crate) start_line: u32, + /// The line where the macro expansion ends. + pub(crate) end_line: u32, + /// The source code of the expanded macro. + pub(crate) code: String, + /// The span of macro callsite. + pub(crate) span: Span, +} + /// This function will do at most two things: /// /// 1. Generate a `span` correspondence map which links an item `span` to its definition `span`. /// 2. Collect the source code files. /// -/// It returns the `krate`, the source code files and the `span` correspondence map. +/// It returns the source code files, the `span` correspondence map and the expanded macros +/// correspondence map. /// /// Note about the `span` correspondence map: the keys are actually `(lo, hi)` of `span`s. We don't /// need the `span` context later on, only their position, so instead of keeping a whole `Span`, we @@ -46,17 +60,23 @@ pub(crate) fn collect_spans_and_sources( src_root: &Path, include_sources: bool, generate_link_to_definition: bool, -) -> (FxIndexMap, FxHashMap) { +) -> ( + FxIndexMap, + FxHashMap, + FxHashMap>, +) { if include_sources { let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() }; + let mut expanded_visitor = ExpandedCodeVisitor { tcx, expanded_codes: Vec::new() }; + tcx.hir_walk_toplevel_module(&mut expanded_visitor); if generate_link_to_definition { tcx.hir_walk_toplevel_module(&mut visitor); } let sources = sources::collect_local_sources(tcx, src_root, krate); - (sources, visitor.matches) + (sources, visitor.matches, expanded_visitor.compute_expanded()) } else { - (Default::default(), Default::default()) + (Default::default(), Default::default(), Default::default()) } } @@ -292,3 +312,89 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { intravisit::walk_item(self, item); } } + +/// Contains temporary information of macro expanded code. +/// +/// As we go through the HIR visitor, if any span overlaps with another, they will +/// both be merged. +struct ExpandedCodeInfo { + /// Callsite of the macro. + span: Span, + /// Expanded macro source code. + code: String, +} + +/// HIR visitor which retrieves expanded macro. +/// +/// Once done, the `expanded_codes` will be transformed into a vec of [`ExpandedCode`] +/// which contains more information needed when running the source code highlighter. +pub struct ExpandedCodeVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + expanded_codes: Vec, +} + +impl<'tcx> ExpandedCodeVisitor<'tcx> { + fn handle_new_span) -> String>(&mut self, new_span: Span, f: F) { + if new_span.is_dummy() || !new_span.from_expansion() { + return; + } + let new_span = new_span.source_callsite(); + if let Some(index) = + self.expanded_codes.iter().position(|info| info.span.overlaps(new_span)) + { + if !self.expanded_codes[index].span.contains(new_span) { + // We replace the item. + let info = &mut self.expanded_codes[index]; + info.span = new_span; + info.code = f(self.tcx); + } + } else { + // We add a new item. + self.expanded_codes.push(ExpandedCodeInfo { + span: new_span, + code: f(self.tcx), + }); + } + } + + fn compute_expanded(mut self) -> FxHashMap> { + self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span)); + let source_map = self.tcx.sess.source_map(); + let mut expanded: FxHashMap> = FxHashMap::default(); + for ExpandedCodeInfo { span, code } in self.expanded_codes { + if let Ok(lines) = source_map.span_to_lines(span) + && !lines.lines.is_empty() + { + let mut out = String::new(); + super::highlight::write_code(&mut out, &code, None, None, None); + let first = lines.lines.first().unwrap(); + let end = lines.lines.last().unwrap(); + expanded.entry(lines.file.start_pos).or_default().push(ExpandedCode { + start_line: first.line_index as u32 + 1, + end_line: end.line_index as u32 + 1, + code: out, + span, + }); + } + } + expanded + } +} + +impl<'tcx> Visitor<'tcx> for ExpandedCodeVisitor<'tcx> { + type NestedFilter = nested_filter::All; + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.tcx + } + + fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { + self.handle_new_span(expr.span, |tcx| rustc_hir_pretty::expr_to_string(&tcx, expr)); + intravisit::walk_expr(self, expr); + } + + fn visit_item(&mut self, item: &'tcx rustc_hir::Item<'tcx>) { + self.handle_new_span(item.span, |tcx| rustc_hir_pretty::item_to_string(&tcx, item)); + intravisit::walk_item(self, item); + } +} diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index dc27d7943d9..9403701d454 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -956,14 +956,55 @@ rustdoc-topbar { .example-wrap.digits-8 { --example-wrap-digits-count: 8ch; } .example-wrap.digits-9 { --example-wrap-digits-count: 9ch; } -.example-wrap [data-nosnippet] { +.example-wrap .expansion { + position: relative; + display: inline; +} +.example-wrap .expansion > input { + display: none; +} +.example-wrap .expansion > label { + position: absolute; + left: -20px; + top: 0; + border: 1px solid var(--border-color); + border-radius: 4px; + cursor: pointer; + color: var(--main-color); + padding: 0 2px; + line-height: 20px; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + +} +.example-wrap .expansion .expanded { + display: none; + color: var(--main-color); +} +.example-wrap .expansion > input:checked ~ .expanded, +.example-wrap .expansion > input:checked ~ * .expanded { + display: inherit; +} +.example-wrap .expansion > input:checked ~ .original, +.example-wrap .expansion > input:checked ~ * .original { + display: none; +} + +.src .example-wrap [data-nosnippet] { width: calc(var(--example-wrap-digits-count) + var(--line-number-padding) * 2); } -.example-wrap pre > code { +.src .example-wrap pre > code { padding-left: calc( var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + var(--line-number-right-margin)); } +.src .example-wrap .expansion [data-nosnippet] { + left: calc(( + var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + + var(--line-number-right-margin)) * -1); +} .example-wrap [data-nosnippet] { color: var(--src-line-numbers-span-color); @@ -978,23 +1019,21 @@ rustdoc-topbar { position: absolute; left: 0; } -.example-wrap .line-highlighted[data-nosnippet] { - background-color: var(--src-line-number-highlighted-background-color); -} .example-wrap pre > code { position: relative; - display: block; -} -:root.word-wrap-source-code .example-wrap pre > code { word-break: break-all; + display: block; white-space: pre-wrap; } -:root.word-wrap-source-code .example-wrap pre > code * { +.example-wrap pre > code * { word-break: break-all; } .example-wrap [data-nosnippet]:target { border-right: none; } +.example-wrap .line-highlighted[data-nosnippet] { + background-color: var(--src-line-number-highlighted-background-color); +} .example-wrap.hide-lines [data-nosnippet] { display: none; } -- cgit 1.4.1-3-g733a5 From f8ce37621ae83f1323a83f833ab2abecdc20a0ba Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 18 Feb 2025 18:07:14 +0100 Subject: Add new unstable `--generate-macro-expansion` rustdoc command line flag --- src/librustdoc/config.rs | 11 +++++++++++ src/librustdoc/html/highlight.rs | 25 ++++++++++++++++--------- src/librustdoc/html/render/context.rs | 2 ++ src/librustdoc/html/render/span_map.rs | 10 +++++----- src/librustdoc/lib.rs | 8 ++++++++ 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index c52c7236883..450ac04b40d 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -305,6 +305,8 @@ pub(crate) struct RenderOptions { pub(crate) parts_out_dir: Option, /// disable minification of CSS/JS pub(crate) disable_minification: bool, + /// If `true`, HTML source pages will generate the possibility to expand macros. + pub(crate) generate_macro_expansion: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -786,6 +788,7 @@ impl Options { let show_type_layout = matches.opt_present("show-type-layout"); let nocapture = matches.opt_present("nocapture"); let generate_link_to_definition = matches.opt_present("generate-link-to-definition"); + let generate_macro_expansion = matches.opt_present("generate-macro-expansion"); let extern_html_root_takes_precedence = matches.opt_present("extern-html-root-takes-precedence"); let html_no_source = matches.opt_present("html-no-source"); @@ -801,6 +804,13 @@ impl Options { .with_note("`--generate-link-to-definition` option will be ignored") .emit(); } + if generate_macro_expansion && (show_coverage || output_format != OutputFormat::Html) { + dcx.struct_warn( + "`--generate-macro-expansion` option can only be used with HTML output format", + ) + .with_note("`--generate-macro-expansion` option will be ignored") + .emit(); + } let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); let with_examples = matches.opt_strs("with-examples"); @@ -881,6 +891,7 @@ impl Options { unstable_features, emit, generate_link_to_definition, + generate_macro_expansion, call_locations, no_emit_shared: false, html_no_source, diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 165b0c587b0..7e362791af9 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -281,7 +281,7 @@ fn get_expansion<'a, W: Write>( && let Some(expanded_code) = expanded_codes.iter().find(|code| code.start_line == line) { let (closing, reopening) = if let Some(current_class) = token_handler.current_class - && let class = current_class.as_html() + && let class = current_class.as_html() && !class.is_empty() { ("", format!("")) @@ -321,11 +321,15 @@ fn end_expansion(token_handler: &mut TokenHandler<'_, '_, W>, level: u } let mut out = String::new(); let mut end = String::new(); - for (tag, class) in token_handler.closing_tags.iter().skip(token_handler.closing_tags.len() - level) { + for (tag, class) in + token_handler.closing_tags.iter().skip(token_handler.closing_tags.len() - level) + { out.push_str(tag); end.push_str(&format!("", class.as_html())); } - token_handler.pending_elems.push((Cow::Owned(format!("{out}{end}")), Some(Class::Expansion))); + token_handler + .pending_elems + .push((Cow::Owned(format!("{out}{end}")), Some(Class::Expansion))); } #[derive(Clone, Copy)] @@ -399,8 +403,7 @@ pub(super) fn write_code( .href_context .as_ref() .and_then(|c| c.context.shared.expanded_codes.get(&c.file_span.lo())); - let mut current_expansion = - get_expansion(&mut token_handler, expanded_codes, line); + let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line); token_handler.write_pending_elems(None); let mut level = 0; @@ -440,8 +443,7 @@ pub(super) fn write_code( .push((Cow::Borrowed(text), Some(Class::Backline(line)))); } if current_expansion.is_none() { - current_expansion = - get_expansion(&mut token_handler, expanded_codes, line); + current_expansion = get_expansion(&mut token_handler, expanded_codes, line); } } else { token_handler.pending_elems.push((Cow::Borrowed(text), class)); @@ -887,7 +889,9 @@ impl<'src> Classifier<'src> { ) { let lookahead = self.peek(); let file_span = self.file_span; - let no_highlight = |sink: &mut dyn FnMut(_, _)| sink(new_span(before, text, file_span), Highlight::Token { text, class: None }); + let no_highlight = |sink: &mut dyn FnMut(_, _)| { + sink(new_span(before, text, file_span), Highlight::Token { text, class: None }) + }; let whitespace = |sink: &mut dyn FnMut(_, _)| { let mut start = 0u32; for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) { @@ -1053,7 +1057,10 @@ impl<'src> Classifier<'src> { TokenKind::CloseBracket => { if self.in_attribute { self.in_attribute = false; - sink(new_span(before, text, file_span), Highlight::Token { text: "]", class: None }); + sink( + new_span(before, text, file_span), + Highlight::Token { text: "]", class: None }, + ); sink(DUMMY_SP, Highlight::ExitSpan); return; } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 33dec1c24e6..af9af5ba2b3 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -492,6 +492,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { generate_redirect_map, show_type_layout, generate_link_to_definition, + generate_macro_expansion, call_locations, no_emit_shared, html_no_source, @@ -556,6 +557,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { &src_root, include_sources, generate_link_to_definition, + generate_macro_expansion, ); let (sender, receiver) = channel(); diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 842da838049..10969188dc7 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -60,6 +60,7 @@ pub(crate) fn collect_spans_and_sources( src_root: &Path, include_sources: bool, generate_link_to_definition: bool, + generate_macro_expansion: bool, ) -> ( FxIndexMap, FxHashMap, @@ -69,7 +70,9 @@ pub(crate) fn collect_spans_and_sources( let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() }; let mut expanded_visitor = ExpandedCodeVisitor { tcx, expanded_codes: Vec::new() }; - tcx.hir_walk_toplevel_module(&mut expanded_visitor); + if generate_macro_expansion { + tcx.hir_walk_toplevel_module(&mut expanded_visitor); + } if generate_link_to_definition { tcx.hir_walk_toplevel_module(&mut visitor); } @@ -350,10 +353,7 @@ impl<'tcx> ExpandedCodeVisitor<'tcx> { } } else { // We add a new item. - self.expanded_codes.push(ExpandedCodeInfo { - span: new_span, - code: f(self.tcx), - }); + self.expanded_codes.push(ExpandedCodeInfo { span: new_span, code: f(self.tcx) }); } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 28dbd8ba7d3..09e7a42e944 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -704,6 +704,14 @@ fn opts() -> Vec { "removed, see issue #44136 for more information", "[rust]", ), + opt( + Unstable, + Flag, + "", + "generate-macro-expansion", + "Add possibility to expand macros in the HTML source code pages", + "", + ), ] } -- cgit 1.4.1-3-g733a5 From 36421286d50f4dd7eaab93219adc054d5c2ced2b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 21 Feb 2025 00:44:32 +0100 Subject: Add documentation for `--generate-macro-expansion` --- src/doc/rustdoc/src/unstable-features.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 7bd2970eee7..724e260009e 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -796,3 +796,7 @@ will be split as follows: "you today?", ] ``` + +## `--generate-macro-expansion`: Generate macros expansion toggles in source code + +This flag enables the generation of toggles to expand macros in the HTML source code pages, -- cgit 1.4.1-3-g733a5 From 18739ee9973b6c96ac4b8ef17a87ae3d178bd288 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 21 Feb 2025 10:39:50 +0100 Subject: Update `tests/run-make/rustdoc-default-output` test --- tests/run-make/rustdoc-default-output/output-default.stdout | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 506f135ff8e..765c2060c72 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -212,6 +212,9 @@ Options: removed, see issue #44136 for more information + --generate-macro-expansion + Add possibility to expand macros in the HTML source + code pages @path Read newline separated options from `path` -- cgit 1.4.1-3-g733a5 From 6c1b481e7bc6cdce1646c53beaa9112d2f7b1d9f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 24 Feb 2025 18:13:50 +0100 Subject: Correctly handle multiple macro expansions on a same line --- src/librustdoc/html/highlight.rs | 57 ++++++++++++++++++++++-------- src/librustdoc/html/static/css/rustdoc.css | 10 +++--- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 7e362791af9..39fec2cbe69 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -272,14 +272,25 @@ fn empty_line_number(out: &mut impl Write, _: u32, extra: &'static str) { out.write_str(extra).unwrap(); } +fn get_next_expansion<'a>( + expanded_codes: Option<&'a Vec>, + line: u32, + span: Span, +) -> Option<&'a ExpandedCode> { + if let Some(expanded_codes) = expanded_codes { + expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() >= span.lo()) + } else { + None + } +} + fn get_expansion<'a, W: Write>( token_handler: &mut TokenHandler<'_, '_, W>, expanded_codes: Option<&'a Vec>, line: u32, + span: Span, ) -> Option<&'a ExpandedCode> { - if let Some(expanded_codes) = expanded_codes - && let Some(expanded_code) = expanded_codes.iter().find(|code| code.start_line == line) - { + if let Some(expanded_code) = get_next_expansion(expanded_codes, line, span) { let (closing, reopening) = if let Some(current_class) = token_handler.current_class && let class = current_class.as_html() && !class.is_empty() @@ -314,10 +325,21 @@ fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option)>, expanded_code: )); } -fn end_expansion(token_handler: &mut TokenHandler<'_, '_, W>, level: usize) { +fn end_expansion<'a, W: Write>( + token_handler: &mut TokenHandler<'_, '_, W>, + expanded_codes: Option<&'a Vec>, + level: usize, + line: u32, + span: Span, +) -> Option<&'a ExpandedCode> { + if let Some(expanded_code) = get_next_expansion(expanded_codes, line, span) { + // We close the current "original" content. + token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); + return Some(expanded_code); + } if level == 0 { token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); - return; + return None; } let mut out = String::new(); let mut end = String::new(); @@ -330,6 +352,7 @@ fn end_expansion(token_handler: &mut TokenHandler<'_, '_, W>, level: u token_handler .pending_elems .push((Cow::Owned(format!("{out}{end}")), Some(Class::Expansion))); + None } #[derive(Clone, Copy)] @@ -399,11 +422,14 @@ pub(super) fn write_code( (0, u32::MAX) }; - let expanded_codes = token_handler - .href_context - .as_ref() - .and_then(|c| c.context.shared.expanded_codes.get(&c.file_span.lo())); - let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line); + let (expanded_codes, file_span) = match token_handler.href_context.as_ref().and_then(|c| { + let expanded_codes = c.context.shared.expanded_codes.get(&c.file_span.lo())?; + Some((expanded_codes, c.file_span)) + }) { + Some((expanded_codes, file_span)) => (Some(expanded_codes), file_span), + None => (None, DUMMY_SP), + }; + let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line, file_span); token_handler.write_pending_elems(None); let mut level = 0; @@ -443,7 +469,8 @@ pub(super) fn write_code( .push((Cow::Borrowed(text), Some(Class::Backline(line)))); } if current_expansion.is_none() { - current_expansion = get_expansion(&mut token_handler, expanded_codes, line); + current_expansion = + get_expansion(&mut token_handler, expanded_codes, line, span); } } else { token_handler.pending_elems.push((Cow::Borrowed(text), class)); @@ -459,9 +486,11 @@ pub(super) fn write_code( } } if need_end { - end_expansion(&mut token_handler, level); - current_expansion = None; - level = 0; + current_expansion = + end_expansion(&mut token_handler, expanded_codes, level, line, span); + if current_expansion.is_none() { + level = 0; + } } } } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 9403701d454..f3dafffbb6b 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -992,10 +992,10 @@ rustdoc-topbar { display: none; } -.src .example-wrap [data-nosnippet] { +.example-wrap [data-nosnippet] { width: calc(var(--example-wrap-digits-count) + var(--line-number-padding) * 2); } -.src .example-wrap pre > code { +.example-wrap pre > code { padding-left: calc( var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + var(--line-number-right-margin)); @@ -1021,11 +1021,13 @@ rustdoc-topbar { } .example-wrap pre > code { position: relative; - word-break: break-all; display: block; +} +:root.word-wrap-source-code .example-wrap pre > code { + word-break: break-all; white-space: pre-wrap; } -.example-wrap pre > code * { +:root.word-wrap-source-code .example-wrap pre > code * { word-break: break-all; } .example-wrap [data-nosnippet]:target { -- cgit 1.4.1-3-g733a5 From b7a415017c853d0fee920d6b2822efd1dd5dd509 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Feb 2025 18:12:35 +0100 Subject: Update rustdoc-gui tests --- src/doc/rustdoc/src/unstable-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 724e260009e..bb28e3abbf3 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -799,4 +799,4 @@ will be split as follows: ## `--generate-macro-expansion`: Generate macros expansion toggles in source code -This flag enables the generation of toggles to expand macros in the HTML source code pages, +This flag enables the generation of toggles to expand macros in the HTML source code pages. -- cgit 1.4.1-3-g733a5 From e6f9e2e6237713a59cebedc38b2cac254a544c00 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 26 Feb 2025 01:13:05 +0100 Subject: Add GUI test for `--generate-macro-expansion` option --- tests/rustdoc-gui/macro-expansion.goml | 74 ++++++++++++++++++++++++ tests/rustdoc-gui/sidebar-source-code.goml | 2 +- tests/rustdoc-gui/src/macro_expansion/Cargo.lock | 7 +++ tests/rustdoc-gui/src/macro_expansion/Cargo.toml | 7 +++ tests/rustdoc-gui/src/macro_expansion/lib.rs | 51 ++++++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-gui/macro-expansion.goml create mode 100644 tests/rustdoc-gui/src/macro_expansion/Cargo.lock create mode 100644 tests/rustdoc-gui/src/macro_expansion/Cargo.toml create mode 100644 tests/rustdoc-gui/src/macro_expansion/lib.rs diff --git a/tests/rustdoc-gui/macro-expansion.goml b/tests/rustdoc-gui/macro-expansion.goml new file mode 100644 index 00000000000..3d790ae37a8 --- /dev/null +++ b/tests/rustdoc-gui/macro-expansion.goml @@ -0,0 +1,74 @@ +// This test ensures that the macro expansion is generated and working as expected. +go-to: "file://" + |DOC_PATH| + "/src/macro_expansion/lib.rs.html" + +define-function: ( + "check-expansion", + [line, original_content], + block { + assert-text: ("a[id='" + |line| + "'] + .expansion .original", |original_content|) + // The "original" content should be expanded. + assert-css: ("a[id='" + |line| + "'] + .expansion .original", {"display": "inline"}) + // The expanded macro should be hidden. + assert-css: ("a[id='" + |line| + "'] + .expansion .expanded", {"display": "none"}) + + // We "expand" the macro. + click: "a[id='" + |line| + "'] + .expansion label" + // The "original" content is hidden. + assert-css: ("a[id='" + |line| + "'] + .expansion .original", {"display": "none"}) + // The expanded macro is visible. + assert-css: ("a[id='" + |line| + "'] + .expansion .expanded", {"display": "inline"}) + + // We collapse the macro. + click: "a[id='" + |line| + "'] + .expansion label" + // The "original" content is expanded. + assert-css: ("a[id='" + |line| + "'] + .expansion .original", {"display": "inline"}) + // The expanded macro is hidden. + assert-css: ("a[id='" + |line| + "'] + .expansion .expanded", {"display": "none"}) + } +) + +// First we check the derive macro expansion at line 12. +call-function: ("check-expansion", {"line": 12, "original_content": "Debug"}) +// Then we check the `bar` macro expansion at line 22. +call-function: ("check-expansion", {"line": 22, "original_content": "bar!(y)"}) +// Then we check the `println` macro expansion at line 23-25. +call-function: ("check-expansion", {"line": 23, "original_content": 'println!(" +24 {y} +25 ")'}) + +// Then finally we check when there are two macro calls on a same line. +assert-count: ("#expand-27 ~ .original", 2) +assert-count: ("#expand-27 ~ .expanded", 2) + +store-value: (repeat_o, '/following-sibling::*[@class="original"]') +store-value: (repeat_e, '/following-sibling::*[@class="expanded"]') +assert-text: ('//*[@id="expand-27"]' + |repeat_o|, "stringify!(foo)") +assert-text: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, "stringify!(bar)") +assert-text: ('//*[@id="expand-27"]' + |repeat_e|, '"foo"') +assert-text: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, '"bar"') + +// The "original" content should be expanded. +assert-css: ('//*[@id="expand-27"]' + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) +// The expanded macro should be hidden. +assert-css: ('//*[@id="expand-27"]' + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, {"display": "none"}) + +// We "expand" the macro (because the line starts with a string, the label is not at the "top +// level" of the ``, so we need to use a different selector). +click: ".expansion label[for='expand-27']" +// The "original" content is hidden. +assert-css: ('//*[@id="expand-27"]' + |repeat_o|, {"display": "none"}) +assert-css: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, {"display": "none"}) +// The expanded macro is visible. +assert-css: ('//*[@id="expand-27"]' + |repeat_e|, {"display": "inline"}) +assert-css: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, {"display": "inline"}) + +// We collapse the macro. +click: ".expansion label[for='expand-27']" +// The "original" content is expanded. +assert-css: ('//*[@id="expand-27"]' + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) +// The expanded macro is hidden. +assert-css: ('//*[@id="expand-27"]' + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, {"display": "none"}) diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml index 0ac88612cef..3f6914a89d6 100644 --- a/tests/rustdoc-gui/sidebar-source-code.goml +++ b/tests/rustdoc-gui/sidebar-source-code.goml @@ -71,7 +71,7 @@ assert: "//*[@class='dir-entry' and @open]/*[normalize-space()='sub_mod']" // Only "another_folder" should be "open" in "lib2". assert: "//*[@class='dir-entry' and not(@open)]/*[normalize-space()='another_mod']" // All other trees should be collapsed. -assert-count: ("//*[@id='src-sidebar']/details[not(normalize-space()='lib2') and not(@open)]", 11) +assert-count: ("//*[@id='src-sidebar']/details[not(normalize-space()='lib2') and not(@open)]", 12) // We now switch to mobile mode. set-window-size: (600, 600) diff --git a/tests/rustdoc-gui/src/macro_expansion/Cargo.lock b/tests/rustdoc-gui/src/macro_expansion/Cargo.lock new file mode 100644 index 00000000000..9c5cee8fb9d --- /dev/null +++ b/tests/rustdoc-gui/src/macro_expansion/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "macro_expansion" +version = "0.1.0" diff --git a/tests/rustdoc-gui/src/macro_expansion/Cargo.toml b/tests/rustdoc-gui/src/macro_expansion/Cargo.toml new file mode 100644 index 00000000000..6d362850fc5 --- /dev/null +++ b/tests/rustdoc-gui/src/macro_expansion/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "macro_expansion" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs" diff --git a/tests/rustdoc-gui/src/macro_expansion/lib.rs b/tests/rustdoc-gui/src/macro_expansion/lib.rs new file mode 100644 index 00000000000..7926e0143d8 --- /dev/null +++ b/tests/rustdoc-gui/src/macro_expansion/lib.rs @@ -0,0 +1,51 @@ +// Test crate used to check the `--generate-macro-expansion` option. +//@ compile-flags: -Zunstable-options --generate-macro-expansion --generate-link-to-definition + +#[macro_export] +macro_rules! bar { + ($x:ident) => {{ + $x += 2; + $x *= 2; + }} +} + +macro_rules! bar2 { + () => { + fn foo2() -> impl std::fmt::Display { + String::new() + } + } +} + +macro_rules! bar3 { + () => { + fn foo3() {} + fn foo4() -> String { String::new() } + } +} + +bar2!(); +bar3!(); + +#[derive(Debug, PartialEq)] +pub struct Bar; + +#[derive(Debug +)] +pub struct Bar2; + +fn y_f(_: &str, _: &str, _: &str) {} + +fn foo() { + let mut y = 0; + bar!(y); + println!(" + {y} + "); + // comment + println!(" + {y} + "); + let s = y_f("\ +bla", stringify!(foo), stringify!(bar)); +} -- cgit 1.4.1-3-g733a5 From 3de3b279f0e2490ed1a76f4a9abe75656a1c0dab Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 12 May 2025 14:07:23 +0200 Subject: Go around firefox bug --- src/librustdoc/html/static/css/rustdoc.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f3dafffbb6b..0b7a8abdf09 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1001,7 +1001,13 @@ rustdoc-topbar { + var(--line-number-right-margin)); } .src .example-wrap .expansion [data-nosnippet] { - left: calc(( + /* FIXME: Once is solved, uncomment + next line and remove the two other rules. */ + /*left: calc(( + var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + + var(--line-number-right-margin)) * -1);*/ + position: initial; + margin-left: calc(( var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + var(--line-number-right-margin)) * -1); } -- cgit 1.4.1-3-g733a5 From ab000e15e1c6fd571882ebd0852ee39fabe6820d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 19 May 2025 17:00:41 +0200 Subject: Clean up computation of macro expansion span and correctly handle spans open inside expansion spans --- src/librustdoc/html/highlight.rs | 75 ++++++++++++++++++++++++++-------- src/librustdoc/html/render/span_map.rs | 43 ++++++++++++++----- 2 files changed, 90 insertions(+), 28 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 39fec2cbe69..3457ac5b232 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -169,6 +169,17 @@ struct TokenHandler<'a, 'tcx, F: Write> { write_line_number: fn(&mut F, u32, &'static str), } +impl std::fmt::Debug for TokenHandler<'_, '_, F> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TokenHandler") + .field("closing_tags", &self.closing_tags) + .field("pending_exit_span", &self.pending_exit_span) + .field("current_class", &self.current_class) + .field("pending_elems", &self.pending_elems) + .finish() + } +} + impl TokenHandler<'_, '_, F> { fn handle_exit_span(&mut self) { // We can't get the last `closing_tags` element using `pop()` because `closing_tags` is @@ -221,6 +232,12 @@ impl TokenHandler<'_, '_, F> { } else { None }; + let mut last_pending = None; + // To prevent opening a macro expansion span being closed right away because + // the currently open item is replaced by a new class. + if let Some((_, Some(Class::Expansion))) = self.pending_elems.last() { + last_pending = self.pending_elems.pop(); + } for (text, class) in self.pending_elems.iter() { string( self.out, @@ -234,6 +251,16 @@ impl TokenHandler<'_, '_, F> { if let Some(close_tag) = close_tag { exit_span(self.out, close_tag); } + if let Some((text, class)) = last_pending { + string( + self.out, + EscapeBodyText(&text), + class, + &self.href_context, + close_tag.is_none(), + self.write_line_number, + ); + } } self.pending_elems.clear(); true @@ -278,7 +305,7 @@ fn get_next_expansion<'a>( span: Span, ) -> Option<&'a ExpandedCode> { if let Some(expanded_codes) = expanded_codes { - expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() >= span.lo()) + expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() > span.lo()) } else { None } @@ -328,7 +355,7 @@ fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option)>, expanded_code: fn end_expansion<'a, W: Write>( token_handler: &mut TokenHandler<'_, '_, W>, expanded_codes: Option<&'a Vec>, - level: usize, + expansion_start_tags: &[(&'static str, Class)], line: u32, span: Span, ) -> Option<&'a ExpandedCode> { @@ -337,15 +364,27 @@ fn end_expansion<'a, W: Write>( token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); return Some(expanded_code); } - if level == 0 { + if expansion_start_tags.is_empty() && token_handler.closing_tags.is_empty() { + // No need tag opened so we can just close expansion. token_handler.pending_elems.push((Cow::Borrowed(""), Some(Class::Expansion))); return None; } + + // If tags were opened inside the expansion, we need to close them and re-open them outside + // of the expansion span. let mut out = String::new(); let mut end = String::new(); - for (tag, class) in - token_handler.closing_tags.iter().skip(token_handler.closing_tags.len() - level) + + let mut closing_tags = token_handler.closing_tags.iter().peekable(); + let mut start_closing_tags = expansion_start_tags.iter().peekable(); + + while let (Some(tag), Some(start_tag)) = (closing_tags.peek(), start_closing_tags.peek()) + && tag == start_tag { + closing_tags.next(); + start_closing_tags.next(); + } + for (tag, class) in start_closing_tags.chain(closing_tags) { out.push_str(tag); end.push_str(&format!("", class.as_html())); } @@ -431,7 +470,7 @@ pub(super) fn write_code( }; let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line, file_span); token_handler.write_pending_elems(None); - let mut level = 0; + let mut expansion_start_tags = Vec::new(); Classifier::new( &src, @@ -471,6 +510,12 @@ pub(super) fn write_code( if current_expansion.is_none() { current_expansion = get_expansion(&mut token_handler, expanded_codes, line, span); + expansion_start_tags = token_handler.closing_tags.clone(); + } + if let Some(ref current_expansion) = current_expansion + && current_expansion.span.lo() == span.hi() + { + start_expansion(&mut token_handler.pending_elems, current_expansion); } } else { token_handler.pending_elems.push((Cow::Borrowed(text), class)); @@ -486,11 +531,13 @@ pub(super) fn write_code( } } if need_end { - current_expansion = - end_expansion(&mut token_handler, expanded_codes, level, line, span); - if current_expansion.is_none() { - level = 0; - } + current_expansion = end_expansion( + &mut token_handler, + expanded_codes, + &expansion_start_tags, + line, + span, + ); } } } @@ -511,9 +558,6 @@ pub(super) fn write_code( if should_add { let closing_tag = enter_span(token_handler.out, class, &token_handler.href_context); - if current_expansion.is_some() { - level += 1; - } token_handler.closing_tags.push((closing_tag, class)); } @@ -522,9 +566,6 @@ pub(super) fn write_code( } Highlight::ExitSpan => { token_handler.current_class = None; - if current_expansion.is_some() { - level -= 1; - } token_handler.pending_exit_span = Some( token_handler .closing_tags diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 10969188dc7..04071a9e066 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -325,6 +325,8 @@ struct ExpandedCodeInfo { span: Span, /// Expanded macro source code. code: String, + /// Expanded span + expanded_span: Span, } /// HIR visitor which retrieves expanded macro. @@ -341,19 +343,32 @@ impl<'tcx> ExpandedCodeVisitor<'tcx> { if new_span.is_dummy() || !new_span.from_expansion() { return; } - let new_span = new_span.source_callsite(); + let callsite_span = new_span.source_callsite(); if let Some(index) = - self.expanded_codes.iter().position(|info| info.span.overlaps(new_span)) + self.expanded_codes.iter().position(|info| info.span.overlaps(callsite_span)) { - if !self.expanded_codes[index].span.contains(new_span) { + let info = &mut self.expanded_codes[index]; + if new_span.contains(info.expanded_span) { // We replace the item. - let info = &mut self.expanded_codes[index]; - info.span = new_span; + info.span = callsite_span; + info.expanded_span = new_span; info.code = f(self.tcx); + } else { + // We push the new item after the existing one. + let expanded_code = &mut self.expanded_codes[index]; + expanded_code.code.push('\n'); + expanded_code.code.push_str(&f(self.tcx)); + let lo = BytePos(expanded_code.expanded_span.lo().0.min(new_span.lo().0)); + let hi = BytePos(expanded_code.expanded_span.hi().0.min(new_span.hi().0)); + expanded_code.expanded_span = expanded_code.expanded_span.with_lo(lo).with_hi(hi); } } else { // We add a new item. - self.expanded_codes.push(ExpandedCodeInfo { span: new_span, code: f(self.tcx) }); + self.expanded_codes.push(ExpandedCodeInfo { + span: callsite_span, + code: f(self.tcx), + expanded_span: new_span, + }); } } @@ -361,7 +376,7 @@ impl<'tcx> ExpandedCodeVisitor<'tcx> { self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span)); let source_map = self.tcx.sess.source_map(); let mut expanded: FxHashMap> = FxHashMap::default(); - for ExpandedCodeInfo { span, code } in self.expanded_codes { + for ExpandedCodeInfo { span, code, .. } in self.expanded_codes { if let Ok(lines) = source_map.span_to_lines(span) && !lines.lines.is_empty() { @@ -389,12 +404,18 @@ impl<'tcx> Visitor<'tcx> for ExpandedCodeVisitor<'tcx> { } fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { - self.handle_new_span(expr.span, |tcx| rustc_hir_pretty::expr_to_string(&tcx, expr)); - intravisit::walk_expr(self, expr); + if expr.span.from_expansion() { + self.handle_new_span(expr.span, |tcx| rustc_hir_pretty::expr_to_string(&tcx, expr)); + } else { + intravisit::walk_expr(self, expr); + } } fn visit_item(&mut self, item: &'tcx rustc_hir::Item<'tcx>) { - self.handle_new_span(item.span, |tcx| rustc_hir_pretty::item_to_string(&tcx, item)); - intravisit::walk_item(self, item); + if item.span.from_expansion() { + self.handle_new_span(item.span, |tcx| rustc_hir_pretty::item_to_string(&tcx, item)); + } else { + intravisit::walk_item(self, item); + } } } -- cgit 1.4.1-3-g733a5 From f8b8cc4cceda39ec2c0a933fd1ba4b9951401862 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 19 May 2025 23:10:09 +0200 Subject: Do macro expansion at AST level rather than HIR --- src/librustdoc/core.rs | 13 ++- src/librustdoc/formats/renderer.rs | 18 ++--- src/librustdoc/html/highlight.rs | 3 +- src/librustdoc/html/macro_expansion.rs | 140 +++++++++++++++++++++++++++++++++ src/librustdoc/html/mod.rs | 1 + src/librustdoc/html/render/context.rs | 29 +++---- src/librustdoc/html/render/mod.rs | 2 +- src/librustdoc/html/render/span_map.rs | 135 +------------------------------ src/librustdoc/json/mod.rs | 20 ++--- src/librustdoc/lib.rs | 50 ++++++++++-- src/librustdoc/scrape_examples.rs | 4 +- tests/rustdoc-gui/macro-expansion.goml | 54 ++++++------- 12 files changed, 264 insertions(+), 205 deletions(-) create mode 100644 src/librustdoc/html/macro_expansion.rs diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index e89733b2f6d..b8aaafcb517 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -31,6 +31,7 @@ use crate::clean::inline::build_trait; use crate::clean::{self, ItemId}; use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; use crate::formats::cache::Cache; +use crate::html::macro_expansion::{ExpandedCode, source_macro_expansion}; use crate::passes; use crate::passes::Condition::*; use crate::passes::collect_intra_doc_links::LinkCollector; @@ -334,11 +335,19 @@ pub(crate) fn run_global_ctxt( show_coverage: bool, render_options: RenderOptions, output_format: OutputFormat, -) -> (clean::Crate, RenderOptions, Cache) { +) -> (clean::Crate, RenderOptions, Cache, FxHashMap>) { // Certain queries assume that some checks were run elsewhere // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), // so type-check everything other than function bodies in this crate before running lints. + let expanded_macros = { + // We need for these variables to be removed to ensure that the `Crate` won't be "stolen" + // anymore. + let (_resolver, krate) = &*tcx.resolver_for_lowering().borrow(); + + source_macro_expansion(&krate, &render_options, output_format, tcx.sess.source_map()) + }; + // NOTE: this does not call `tcx.analysis()` so that we won't // typeck function bodies or run the default rustc lints. // (see `override_queries` in the `config`) @@ -448,7 +457,7 @@ pub(crate) fn run_global_ctxt( tcx.dcx().abort_if_errors(); - (krate, ctxt.render_options, ctxt.cache) + (krate, ctxt.render_options, ctxt.cache, expanded_macros) } /// Due to , diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index aa4be4db997..305c8c39ba7 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -31,15 +31,6 @@ pub(crate) trait FormatRenderer<'tcx>: Sized { /// reset the information between each call to `item` by using `restore_module_data`. type ModuleData; - /// Sets up any state required for the renderer. When this is called the cache has already been - /// populated. - fn init( - krate: clean::Crate, - options: RenderOptions, - cache: Cache, - tcx: TyCtxt<'tcx>, - ) -> Result<(Self, clean::Crate), Error>; - /// This method is called right before call [`Self::item`]. This method returns a type /// containing information that needs to be reset after the [`Self::item`] method has been /// called with the [`Self::restore_module_data`] method. @@ -105,18 +96,23 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>( } /// Main method for rendering a crate. -pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( +pub(crate) fn run_format< + 'tcx, + T: FormatRenderer<'tcx>, + F: FnOnce(clean::Crate, RenderOptions, Cache, TyCtxt<'tcx>) -> Result<(T, clean::Crate), Error>, +>( krate: clean::Crate, options: RenderOptions, cache: Cache, tcx: TyCtxt<'tcx>, + init: F, ) -> Result<(), Error> { let prof = &tcx.sess.prof; let emit_crate = options.should_emit_crate(); let (mut format_renderer, krate) = prof .verbose_generic_activity_with_arg("create_renderer", T::descr()) - .run(|| T::init(krate, options, cache, tcx))?; + .run(|| init(krate, options, cache, tcx))?; if !emit_crate { return Ok(()); diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 3457ac5b232..5a25d659b0f 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -18,7 +18,8 @@ use rustc_span::{BytePos, DUMMY_SP, Span}; use super::format::{self, write_str}; use crate::clean::PrimitiveType; use crate::html::escape::EscapeBodyText; -use crate::html::render::{Context, ExpandedCode, LinkFromSrc}; +use crate::html::macro_expansion::ExpandedCode; +use crate::html::render::{Context, LinkFromSrc}; /// This type is needed in case we want to render links on items to allow to go to their definition. pub(crate) struct HrefContext<'a, 'tcx> { diff --git a/src/librustdoc/html/macro_expansion.rs b/src/librustdoc/html/macro_expansion.rs new file mode 100644 index 00000000000..7b6758868b7 --- /dev/null +++ b/src/librustdoc/html/macro_expansion.rs @@ -0,0 +1,140 @@ +use rustc_ast::visit::{Visitor, walk_crate, walk_expr, walk_item}; +use rustc_ast::{Crate, Expr, Item}; +use rustc_data_structures::fx::FxHashMap; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, Span}; + +use crate::config::{OutputFormat, RenderOptions}; + +/// It returns the expanded macros correspondence map. +pub(crate) fn source_macro_expansion( + krate: &Crate, + render_options: &RenderOptions, + output_format: OutputFormat, + source_map: &SourceMap, +) -> FxHashMap> { + if output_format == OutputFormat::Html + && !render_options.html_no_source + && render_options.generate_macro_expansion + { + let mut expanded_visitor = ExpandedCodeVisitor { expanded_codes: Vec::new(), source_map }; + walk_crate(&mut expanded_visitor, krate); + expanded_visitor.compute_expanded() + } else { + Default::default() + } +} + +/// Contains information about macro expansion in the source code pages. +#[derive(Debug)] +pub(crate) struct ExpandedCode { + /// The line where the macro expansion starts. + pub(crate) start_line: u32, + /// The line where the macro expansion ends. + pub(crate) end_line: u32, + /// The source code of the expanded macro. + pub(crate) code: String, + /// The span of macro callsite. + pub(crate) span: Span, +} + +/// Contains temporary information of macro expanded code. +/// +/// As we go through the HIR visitor, if any span overlaps with another, they will +/// both be merged. +struct ExpandedCodeInfo { + /// Callsite of the macro. + span: Span, + /// Expanded macro source code. + code: String, + /// Expanded span + expanded_span: Span, +} + +/// HIR visitor which retrieves expanded macro. +/// +/// Once done, the `expanded_codes` will be transformed into a vec of [`ExpandedCode`] +/// which contains more information needed when running the source code highlighter. +pub(crate) struct ExpandedCodeVisitor<'ast> { + expanded_codes: Vec, + source_map: &'ast SourceMap, +} + +impl<'ast> ExpandedCodeVisitor<'ast> { + fn handle_new_span String>(&mut self, new_span: Span, f: F) { + if new_span.is_dummy() || !new_span.from_expansion() { + return; + } + let callsite_span = new_span.source_callsite(); + if let Some(index) = + self.expanded_codes.iter().position(|info| info.span.overlaps(callsite_span)) + { + let info = &mut self.expanded_codes[index]; + if new_span.contains(info.expanded_span) { + // We replace the item. + info.span = callsite_span; + info.expanded_span = new_span; + info.code = f(); + } else { + // We push the new item after the existing one. + let expanded_code = &mut self.expanded_codes[index]; + expanded_code.code.push('\n'); + expanded_code.code.push_str(&f()); + let lo = BytePos(expanded_code.expanded_span.lo().0.min(new_span.lo().0)); + let hi = BytePos(expanded_code.expanded_span.hi().0.min(new_span.hi().0)); + expanded_code.expanded_span = expanded_code.expanded_span.with_lo(lo).with_hi(hi); + } + } else { + // We add a new item. + self.expanded_codes.push(ExpandedCodeInfo { + span: callsite_span, + code: f(), + expanded_span: new_span, + }); + } + } + + fn compute_expanded(mut self) -> FxHashMap> { + self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span)); + let mut expanded: FxHashMap> = FxHashMap::default(); + for ExpandedCodeInfo { span, code, .. } in self.expanded_codes { + if let Ok(lines) = self.source_map.span_to_lines(span) + && !lines.lines.is_empty() + { + let mut out = String::new(); + super::highlight::write_code(&mut out, &code, None, None, None); + let first = lines.lines.first().unwrap(); + let end = lines.lines.last().unwrap(); + expanded.entry(lines.file.start_pos).or_default().push(ExpandedCode { + start_line: first.line_index as u32 + 1, + end_line: end.line_index as u32 + 1, + code: out, + span, + }); + } + } + expanded + } +} + +// We need to use the AST pretty printing because: +// +// 1. HIR pretty printing doesn't display accurately the code (like `impl Trait`). +// 2. `SourceMap::snippet_opt` might fail if the source is not available. +impl<'ast> Visitor<'ast> for ExpandedCodeVisitor<'ast> { + fn visit_expr(&mut self, expr: &'ast Expr) { + if expr.span.from_expansion() { + self.handle_new_span(expr.span, || rustc_ast_pretty::pprust::expr_to_string(expr)); + } else { + walk_expr(self, expr); + } + } + + fn visit_item(&mut self, item: &'ast Item) { + if item.span.from_expansion() { + self.handle_new_span(item.span, || rustc_ast_pretty::pprust::item_to_string(item)); + } else { + walk_item(self, item); + } + } +} diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs index 481ed16c05f..170449fc033 100644 --- a/src/librustdoc/html/mod.rs +++ b/src/librustdoc/html/mod.rs @@ -4,6 +4,7 @@ pub(crate) mod highlight; pub(crate) mod layout; mod length_limit; // used by the error-index generator, so it needs to be public +pub(crate) mod macro_expansion; pub mod markdown; pub(crate) mod render; pub(crate) mod sources; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index af9af5ba2b3..faaecaf0cbb 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -28,8 +28,8 @@ use crate::formats::FormatRenderer; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; +use crate::html::macro_expansion::ExpandedCode; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; -use crate::html::render::ExpandedCode; use crate::html::render::write_shared::write_shared; use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources, static_files}; @@ -460,20 +460,13 @@ impl<'tcx> Context<'tcx> { } } -/// Generates the documentation for `crate` into the directory `dst` -impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { - fn descr() -> &'static str { - "html" - } - - const RUN_ON_MODULE: bool = true; - type ModuleData = ContextInfo; - - fn init( +impl<'tcx> Context<'tcx> { + pub(crate) fn init( krate: clean::Crate, options: RenderOptions, cache: Cache, tcx: TyCtxt<'tcx>, + expanded_codes: FxHashMap>, ) -> Result<(Self, clean::Crate), Error> { // need to save a copy of the options for rendering the index page let md_opts = options.clone(); @@ -492,7 +485,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { generate_redirect_map, show_type_layout, generate_link_to_definition, - generate_macro_expansion, call_locations, no_emit_shared, html_no_source, @@ -551,13 +543,12 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } } - let (local_sources, matches, expanded_codes) = collect_spans_and_sources( + let (local_sources, matches) = collect_spans_and_sources( tcx, &krate, &src_root, include_sources, generate_link_to_definition, - generate_macro_expansion, ); let (sender, receiver) = channel(); @@ -609,6 +600,16 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { Ok((cx, krate)) } +} + +/// Generates the documentation for `crate` into the directory `dst` +impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { + fn descr() -> &'static str { + "html" + } + + const RUN_ON_MODULE: bool = true; + type ModuleData = ContextInfo; fn save_module_data(&mut self) -> Self::ModuleData { self.deref_id_map.borrow_mut().clear(); diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 0986b9ca47b..8d7f0577506 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -63,7 +63,7 @@ use serde::{Serialize, Serializer}; use tracing::{debug, info}; pub(crate) use self::context::*; -pub(crate) use self::span_map::{ExpandedCode, LinkFromSrc, collect_spans_and_sources}; +pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; pub(crate) use self::write_shared::*; use crate::clean::{self, ItemId, RenderedLink}; use crate::display::{Joined as _, MaybeDisplay as _}; diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 04071a9e066..8bc2e0bd957 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -30,26 +30,12 @@ pub(crate) enum LinkFromSrc { Doc(DefId), } -/// Contains information about macro expansion in the source code pages. -#[derive(Debug)] -pub(crate) struct ExpandedCode { - /// The line where the macro expansion starts. - pub(crate) start_line: u32, - /// The line where the macro expansion ends. - pub(crate) end_line: u32, - /// The source code of the expanded macro. - pub(crate) code: String, - /// The span of macro callsite. - pub(crate) span: Span, -} - /// This function will do at most two things: /// /// 1. Generate a `span` correspondence map which links an item `span` to its definition `span`. /// 2. Collect the source code files. /// -/// It returns the source code files, the `span` correspondence map and the expanded macros -/// correspondence map. +/// It returns the source code files and the `span` correspondence map. /// /// Note about the `span` correspondence map: the keys are actually `(lo, hi)` of `span`s. We don't /// need the `span` context later on, only their position, so instead of keeping a whole `Span`, we @@ -60,26 +46,17 @@ pub(crate) fn collect_spans_and_sources( src_root: &Path, include_sources: bool, generate_link_to_definition: bool, - generate_macro_expansion: bool, -) -> ( - FxIndexMap, - FxHashMap, - FxHashMap>, -) { +) -> (FxIndexMap, FxHashMap) { if include_sources { let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() }; - let mut expanded_visitor = ExpandedCodeVisitor { tcx, expanded_codes: Vec::new() }; - if generate_macro_expansion { - tcx.hir_walk_toplevel_module(&mut expanded_visitor); - } if generate_link_to_definition { tcx.hir_walk_toplevel_module(&mut visitor); } let sources = sources::collect_local_sources(tcx, src_root, krate); - (sources, visitor.matches, expanded_visitor.compute_expanded()) + (sources, visitor.matches) } else { - (Default::default(), Default::default(), Default::default()) + (Default::default(), Default::default()) } } @@ -315,107 +292,3 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { intravisit::walk_item(self, item); } } - -/// Contains temporary information of macro expanded code. -/// -/// As we go through the HIR visitor, if any span overlaps with another, they will -/// both be merged. -struct ExpandedCodeInfo { - /// Callsite of the macro. - span: Span, - /// Expanded macro source code. - code: String, - /// Expanded span - expanded_span: Span, -} - -/// HIR visitor which retrieves expanded macro. -/// -/// Once done, the `expanded_codes` will be transformed into a vec of [`ExpandedCode`] -/// which contains more information needed when running the source code highlighter. -pub struct ExpandedCodeVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - expanded_codes: Vec, -} - -impl<'tcx> ExpandedCodeVisitor<'tcx> { - fn handle_new_span) -> String>(&mut self, new_span: Span, f: F) { - if new_span.is_dummy() || !new_span.from_expansion() { - return; - } - let callsite_span = new_span.source_callsite(); - if let Some(index) = - self.expanded_codes.iter().position(|info| info.span.overlaps(callsite_span)) - { - let info = &mut self.expanded_codes[index]; - if new_span.contains(info.expanded_span) { - // We replace the item. - info.span = callsite_span; - info.expanded_span = new_span; - info.code = f(self.tcx); - } else { - // We push the new item after the existing one. - let expanded_code = &mut self.expanded_codes[index]; - expanded_code.code.push('\n'); - expanded_code.code.push_str(&f(self.tcx)); - let lo = BytePos(expanded_code.expanded_span.lo().0.min(new_span.lo().0)); - let hi = BytePos(expanded_code.expanded_span.hi().0.min(new_span.hi().0)); - expanded_code.expanded_span = expanded_code.expanded_span.with_lo(lo).with_hi(hi); - } - } else { - // We add a new item. - self.expanded_codes.push(ExpandedCodeInfo { - span: callsite_span, - code: f(self.tcx), - expanded_span: new_span, - }); - } - } - - fn compute_expanded(mut self) -> FxHashMap> { - self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span)); - let source_map = self.tcx.sess.source_map(); - let mut expanded: FxHashMap> = FxHashMap::default(); - for ExpandedCodeInfo { span, code, .. } in self.expanded_codes { - if let Ok(lines) = source_map.span_to_lines(span) - && !lines.lines.is_empty() - { - let mut out = String::new(); - super::highlight::write_code(&mut out, &code, None, None, None); - let first = lines.lines.first().unwrap(); - let end = lines.lines.last().unwrap(); - expanded.entry(lines.file.start_pos).or_default().push(ExpandedCode { - start_line: first.line_index as u32 + 1, - end_line: end.line_index as u32 + 1, - code: out, - span, - }); - } - } - expanded - } -} - -impl<'tcx> Visitor<'tcx> for ExpandedCodeVisitor<'tcx> { - type NestedFilter = nested_filter::All; - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } - - fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { - if expr.span.from_expansion() { - self.handle_new_span(expr.span, |tcx| rustc_hir_pretty::expr_to_string(&tcx, expr)); - } else { - intravisit::walk_expr(self, expr); - } - } - - fn visit_item(&mut self, item: &'tcx rustc_hir::Item<'tcx>) { - if item.span.from_expansion() { - self.handle_new_span(item.span, |tcx| rustc_hir_pretty::item_to_string(&tcx, item)); - } else { - intravisit::walk_item(self, item); - } - } -} diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 760e48baffa..b724d7e866a 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -175,15 +175,8 @@ fn target(sess: &rustc_session::Session) -> types::Target { } } -impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { - fn descr() -> &'static str { - "json" - } - - const RUN_ON_MODULE: bool = false; - type ModuleData = (); - - fn init( +impl<'tcx> JsonRenderer<'tcx> { + pub(crate) fn init( krate: clean::Crate, options: RenderOptions, cache: Cache, @@ -205,6 +198,15 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { krate, )) } +} + +impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { + fn descr() -> &'static str { + "json" + } + + const RUN_ON_MODULE: bool = false; + type ModuleData = (); fn save_module_data(&mut self) -> Self::ModuleData { unreachable!("RUN_ON_MODULE = false, should never call save_module_data") diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 09e7a42e944..fe56fdb2271 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -80,6 +80,8 @@ use rustc_session::{EarlyDiagCtxt, getopts}; use tracing::info; use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION; +use crate::error::Error; +use crate::formats::cache::Cache; /// A macro to create a FxHashMap. /// @@ -734,13 +736,23 @@ pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) { } } -fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>( +fn run_renderer< + 'tcx, + T: formats::FormatRenderer<'tcx>, + F: FnOnce( + clean::Crate, + config::RenderOptions, + Cache, + TyCtxt<'tcx>, + ) -> Result<(T, clean::Crate), Error>, +>( krate: clean::Crate, renderopts: config::RenderOptions, cache: formats::cache::Cache, tcx: TyCtxt<'tcx>, + init: F, ) { - match formats::run_format::(krate, renderopts, cache, tcx) { + match formats::run_format::(krate, renderopts, cache, tcx, init) { Ok(_) => tcx.dcx().abort_if_errors(), Err(e) => { let mut msg = @@ -870,6 +882,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let scrape_examples_options = options.scrape_examples_options.clone(); let bin_crate = options.bin_crate; + let output_format = options.output_format; let config = core::create_config(input, options, &render_options); let registered_lints = config.register_lints.is_some(); @@ -894,9 +907,10 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { sess.dcx().fatal("Compilation failed, aborting rustdoc"); } - let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || { - core::run_global_ctxt(tcx, show_coverage, render_options, output_format) - }); + let (krate, render_opts, mut cache, expanded_macros) = sess + .time("run_global_ctxt", || { + core::run_global_ctxt(tcx, show_coverage, render_options, output_format) + }); info!("finished with rustc"); if let Some(options) = scrape_examples_options { @@ -927,10 +941,32 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { info!("going to format"); match output_format { config::OutputFormat::Html => sess.time("render_html", || { - run_renderer::>(krate, render_opts, cache, tcx) + run_renderer( + krate, + render_opts, + cache, + tcx, + |krate, render_opts, cache, tcx| { + html::render::Context::init( + krate, + render_opts, + cache, + tcx, + expanded_macros, + ) + }, + ) }), config::OutputFormat::Json => sess.time("render_json", || { - run_renderer::>(krate, render_opts, cache, tcx) + run_renderer( + krate, + render_opts, + cache, + tcx, + |krate, render_opts, cache, tcx| { + json::JsonRenderer::init(krate, render_opts, cache, tcx) + }, + ) }), // Already handled above with doctest runners. config::OutputFormat::Doctest => unreachable!(), diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 9f71d6ae789..16034c11827 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -18,7 +18,6 @@ use rustc_span::edition::Edition; use rustc_span::{BytePos, FileName, SourceFile}; use tracing::{debug, trace, warn}; -use crate::formats::renderer::FormatRenderer; use crate::html::render::Context; use crate::{clean, config, formats}; @@ -276,7 +275,8 @@ pub(crate) fn run( let inner = move || -> Result<(), String> { // Generates source files for examples renderopts.no_emit_shared = true; - let (cx, _) = Context::init(krate, renderopts, cache, tcx).map_err(|e| e.to_string())?; + let (cx, _) = Context::init(krate, renderopts, cache, tcx, Default::default()) + .map_err(|e| e.to_string())?; // Collect CrateIds corresponding to provided target crates // If two different versions of the crate in the dependency tree, then examples will be diff --git a/tests/rustdoc-gui/macro-expansion.goml b/tests/rustdoc-gui/macro-expansion.goml index 3d790ae37a8..c398c58adac 100644 --- a/tests/rustdoc-gui/macro-expansion.goml +++ b/tests/rustdoc-gui/macro-expansion.goml @@ -27,48 +27,48 @@ define-function: ( } ) -// First we check the derive macro expansion at line 12. -call-function: ("check-expansion", {"line": 12, "original_content": "Debug"}) -// Then we check the `bar` macro expansion at line 22. -call-function: ("check-expansion", {"line": 22, "original_content": "bar!(y)"}) +// First we check the derive macro expansion at line 33. +call-function: ("check-expansion", {"line": 33, "original_content": "Debug"}) +// Then we check the `bar` macro expansion at line 41. +call-function: ("check-expansion", {"line": 41, "original_content": "bar!(y)"}) // Then we check the `println` macro expansion at line 23-25. -call-function: ("check-expansion", {"line": 23, "original_content": 'println!(" -24 {y} -25 ")'}) +call-function: ("check-expansion", {"line": 42, "original_content": 'println!(" +43 {y} +44 ")'}) // Then finally we check when there are two macro calls on a same line. -assert-count: ("#expand-27 ~ .original", 2) -assert-count: ("#expand-27 ~ .expanded", 2) +assert-count: ("#expand-50 ~ .original", 2) +assert-count: ("#expand-50 ~ .expanded", 2) store-value: (repeat_o, '/following-sibling::*[@class="original"]') store-value: (repeat_e, '/following-sibling::*[@class="expanded"]') -assert-text: ('//*[@id="expand-27"]' + |repeat_o|, "stringify!(foo)") -assert-text: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, "stringify!(bar)") -assert-text: ('//*[@id="expand-27"]' + |repeat_e|, '"foo"') -assert-text: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, '"bar"') +assert-text: ('//*[@id="expand-50"]' + |repeat_o|, "stringify!(foo)") +assert-text: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, "stringify!(bar)") +assert-text: ('//*[@id="expand-50"]' + |repeat_e|, '"foo"') +assert-text: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, '"bar"') // The "original" content should be expanded. -assert-css: ('//*[@id="expand-27"]' + |repeat_o|, {"display": "inline"}) -assert-css: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) // The expanded macro should be hidden. -assert-css: ('//*[@id="expand-27"]' + |repeat_e|, {"display": "none"}) -assert-css: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "none"}) // We "expand" the macro (because the line starts with a string, the label is not at the "top // level" of the ``, so we need to use a different selector). -click: ".expansion label[for='expand-27']" +click: ".expansion label[for='expand-50']" // The "original" content is hidden. -assert-css: ('//*[@id="expand-27"]' + |repeat_o|, {"display": "none"}) -assert-css: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, {"display": "none"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "none"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "none"}) // The expanded macro is visible. -assert-css: ('//*[@id="expand-27"]' + |repeat_e|, {"display": "inline"}) -assert-css: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, {"display": "inline"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "inline"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "inline"}) // We collapse the macro. -click: ".expansion label[for='expand-27']" +click: ".expansion label[for='expand-50']" // The "original" content is expanded. -assert-css: ('//*[@id="expand-27"]' + |repeat_o|, {"display": "inline"}) -assert-css: ('//*[@id="expand-27"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) // The expanded macro is hidden. -assert-css: ('//*[@id="expand-27"]' + |repeat_e|, {"display": "none"}) -assert-css: ('//*[@id="expand-27"]' + |repeat_e| + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "none"}) -- cgit 1.4.1-3-g733a5 From b968ef8d198046bce1c77381a8069d2f837a2455 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 26 May 2025 15:04:48 +0200 Subject: Make macro expansion feature buttons accessible --- src/librustdoc/html/highlight.rs | 7 +++-- src/librustdoc/html/static/css/rustdoc.css | 14 ++++----- tests/rustdoc-gui/macro-expansion.goml | 49 +++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 5a25d659b0f..40530f67d6e 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -332,8 +332,11 @@ fn get_expansion<'a, W: Write>( Cow::Owned(format!( "{closing}\ \ - \ - {reopening}", + {reopening}", )), Some(Class::Expansion), )); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 0b7a8abdf09..86f1a42bc01 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -961,10 +961,10 @@ rustdoc-topbar { display: inline; } .example-wrap .expansion > input { - display: none; -} -.example-wrap .expansion > label { + display: block; position: absolute; + appearance: none; + content: '↕'; left: -20px; top: 0; border: 1px solid var(--border-color); @@ -973,11 +973,9 @@ rustdoc-topbar { color: var(--main-color); padding: 0 2px; line-height: 20px; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - +} +.example-wrap .expansion > input::after { + content: "↕"; } .example-wrap .expansion .expanded { display: none; diff --git a/tests/rustdoc-gui/macro-expansion.goml b/tests/rustdoc-gui/macro-expansion.goml index c398c58adac..5e9a3049405 100644 --- a/tests/rustdoc-gui/macro-expansion.goml +++ b/tests/rustdoc-gui/macro-expansion.goml @@ -12,14 +12,14 @@ define-function: ( assert-css: ("a[id='" + |line| + "'] + .expansion .expanded", {"display": "none"}) // We "expand" the macro. - click: "a[id='" + |line| + "'] + .expansion label" + click: "a[id='" + |line| + "'] + .expansion input[type=checkbox]" // The "original" content is hidden. assert-css: ("a[id='" + |line| + "'] + .expansion .original", {"display": "none"}) // The expanded macro is visible. assert-css: ("a[id='" + |line| + "'] + .expansion .expanded", {"display": "inline"}) // We collapse the macro. - click: "a[id='" + |line| + "'] + .expansion label" + click: "a[id='" + |line| + "'] + .expansion input[type=checkbox]" // The "original" content is expanded. assert-css: ("a[id='" + |line| + "'] + .expansion .original", {"display": "inline"}) // The expanded macro is hidden. @@ -31,7 +31,7 @@ define-function: ( call-function: ("check-expansion", {"line": 33, "original_content": "Debug"}) // Then we check the `bar` macro expansion at line 41. call-function: ("check-expansion", {"line": 41, "original_content": "bar!(y)"}) -// Then we check the `println` macro expansion at line 23-25. +// Then we check the `println` macro expansion at line 42-44. call-function: ("check-expansion", {"line": 42, "original_content": 'println!(" 43 {y} 44 ")'}) @@ -56,7 +56,7 @@ assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "none // We "expand" the macro (because the line starts with a string, the label is not at the "top // level" of the ``, so we need to use a different selector). -click: ".expansion label[for='expand-50']" +click: "#expand-50" // The "original" content is hidden. assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "none"}) assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "none"}) @@ -65,10 +65,49 @@ assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "inline"}) assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "inline"}) // We collapse the macro. -click: ".expansion label[for='expand-50']" +click: "#expand-50" // The "original" content is expanded. assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "inline"}) assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) // The expanded macro is hidden. assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "none"}) assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "none"}) + +// Checking the line 46 `println` which needs to be handled differently because the line number is +// inside a "comment" span. +assert-text: ("#expand-46 ~ .original", 'println!(" +47 {y} +48 ")') +// The "original" content should be expanded. +assert-css: ("#expand-46 ~ .original", {"display": "inline"}) +// The expanded macro should be hidden. +assert-css: ("#expand-46 ~ .expanded", {"display": "none"}) + +// We "expand" the macro. +click: "#expand-46" +// The "original" content is hidden. +assert-css: ("#expand-46 ~ .original", {"display": "none"}) +// The expanded macro is visible. +assert-css: ("#expand-46 ~ .expanded", {"display": "inline"}) + +// We collapse the macro. +click: "#expand-46" +// The "original" content is expanded. +assert-css: ("#expand-46 ~ .original", {"display": "inline"}) +// The expanded macro is hidden. +assert-css: ("#expand-46 ~ .expanded", {"display": "none"}) + +// Ensure that the toggles are focusable and can be interacted with keyboard. +focus: "//a[@id='27']" +press-key: "Tab" +assert: "#expand-27:focus" +assert-css: ("#expand-27 ~ .expanded", {"display": "none"}) +assert-css: ("#expand-27 ~ .original", {"display": "inline"}) +// We now expand the macro. +press-key: "Space" +assert-css: ("#expand-27 ~ .expanded", {"display": "inline"}) +assert-css: ("#expand-27 ~ .original", {"display": "none"}) +// We collapse the macro. +press-key: "Space" +assert-css: ("#expand-27 ~ .expanded", {"display": "none"}) +assert-css: ("#expand-27 ~ .original", {"display": "inline"}) -- cgit 1.4.1-3-g733a5 From bd5f3d76789028e0cdb432ff20f744b6fb24efc2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 29 Jun 2025 12:26:06 +0200 Subject: Improve code --- src/librustdoc/html/highlight.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 40530f67d6e..583c178fa11 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -233,12 +233,10 @@ impl TokenHandler<'_, '_, F> { } else { None }; - let mut last_pending = None; // To prevent opening a macro expansion span being closed right away because // the currently open item is replaced by a new class. - if let Some((_, Some(Class::Expansion))) = self.pending_elems.last() { - last_pending = self.pending_elems.pop(); - } + let last_pending = + self.pending_elems.pop_if(|(_, class)| *class == Some(Class::Expansion)); for (text, class) in self.pending_elems.iter() { string( self.out, -- cgit 1.4.1-3-g733a5 From 0e9b126695847418a681b96b87b5976fb069394a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 1 Aug 2025 16:57:39 +0200 Subject: Also support statements and patterns for macro expansion --- src/librustdoc/html/macro_expansion.rs | 20 ++++++++++++++++++-- tests/rustdoc/macro/macro_expansion.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/rustdoc/macro/macro_expansion.rs diff --git a/src/librustdoc/html/macro_expansion.rs b/src/librustdoc/html/macro_expansion.rs index 7b6758868b7..d76663a3d1a 100644 --- a/src/librustdoc/html/macro_expansion.rs +++ b/src/librustdoc/html/macro_expansion.rs @@ -1,5 +1,5 @@ -use rustc_ast::visit::{Visitor, walk_crate, walk_expr, walk_item}; -use rustc_ast::{Crate, Expr, Item}; +use rustc_ast::visit::{Visitor, walk_crate, walk_expr, walk_item, walk_pat, walk_stmt}; +use rustc_ast::{Crate, Expr, Item, Pat, Stmt}; use rustc_data_structures::fx::FxHashMap; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Span}; @@ -137,4 +137,20 @@ impl<'ast> Visitor<'ast> for ExpandedCodeVisitor<'ast> { walk_item(self, item); } } + + fn visit_stmt(&mut self, stmt: &'ast Stmt) { + if stmt.span.from_expansion() { + self.handle_new_span(stmt.span, || rustc_ast_pretty::pprust::stmt_to_string(stmt)); + } else { + walk_stmt(self, stmt); + } + } + + fn visit_pat(&mut self, pat: &'ast Pat) { + if pat.span.from_expansion() { + self.handle_new_span(pat.span, || rustc_ast_pretty::pprust::pat_to_string(pat)); + } else { + walk_pat(self, pat); + } + } } diff --git a/tests/rustdoc/macro/macro_expansion.rs b/tests/rustdoc/macro/macro_expansion.rs new file mode 100644 index 00000000000..c989ccad967 --- /dev/null +++ b/tests/rustdoc/macro/macro_expansion.rs @@ -0,0 +1,28 @@ +// This test checks that patterns and statements are also getting expanded. + +//@ compile-flags: -Zunstable-options --generate-macro-expansion + +#![crate_name = "foo"] + +//@ has 'src/foo/macro_expansion.rs.html' +//@ count - '//span[@class="expansion"]' 2 + +macro_rules! pat { + ($x:literal) => { + Some($x) + } +} + +macro_rules! stmt { + ($x:expr) => {{ + let _ = $x; + }} +} + +fn bar() { + match Some("hello") { + pat!("blolb") => {} + _ => {} + } + stmt!(1) +} -- cgit 1.4.1-3-g733a5 From f5fddc776ca450c08fe8fc3ecbe5e2c969055d01 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Aug 2025 01:38:57 +0200 Subject: Improve code and add test with macro coming from another file from the same crate --- src/librustdoc/html/highlight.rs | 22 +++--- src/librustdoc/html/macro_expansion.rs | 8 +- src/librustdoc/html/mod.rs | 2 +- src/librustdoc/lib.rs | 26 +++---- tests/rustdoc-gui/macro-expansion.goml | 103 ++++++++++++++----------- tests/rustdoc-gui/src/macro_expansion/lib.rs | 5 ++ tests/rustdoc-gui/src/macro_expansion/other.rs | 6 ++ 7 files changed, 92 insertions(+), 80 deletions(-) create mode 100644 tests/rustdoc-gui/src/macro_expansion/other.rs diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 583c178fa11..feafb41dc99 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -298,21 +298,17 @@ fn empty_line_number(out: &mut impl Write, _: u32, extra: &'static str) { out.write_str(extra).unwrap(); } -fn get_next_expansion<'a>( - expanded_codes: Option<&'a Vec>, +fn get_next_expansion( + expanded_codes: &[ExpandedCode], line: u32, span: Span, -) -> Option<&'a ExpandedCode> { - if let Some(expanded_codes) = expanded_codes { - expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() > span.lo()) - } else { - None - } +) -> Option<&ExpandedCode> { + expanded_codes.iter().find(|code| code.start_line == line && code.span.lo() > span.lo()) } fn get_expansion<'a, W: Write>( token_handler: &mut TokenHandler<'_, '_, W>, - expanded_codes: Option<&'a Vec>, + expanded_codes: &'a [ExpandedCode], line: u32, span: Span, ) -> Option<&'a ExpandedCode> { @@ -356,7 +352,7 @@ fn start_expansion(out: &mut Vec<(Cow<'_, str>, Option)>, expanded_code: fn end_expansion<'a, W: Write>( token_handler: &mut TokenHandler<'_, '_, W>, - expanded_codes: Option<&'a Vec>, + expanded_codes: &'a [ExpandedCode], expansion_start_tags: &[(&'static str, Class)], line: u32, span: Span, @@ -467,8 +463,8 @@ pub(super) fn write_code( let expanded_codes = c.context.shared.expanded_codes.get(&c.file_span.lo())?; Some((expanded_codes, c.file_span)) }) { - Some((expanded_codes, file_span)) => (Some(expanded_codes), file_span), - None => (None, DUMMY_SP), + Some((expanded_codes, file_span)) => (expanded_codes.as_slice(), file_span), + None => (&[] as &[ExpandedCode], DUMMY_SP), }; let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, line, file_span); token_handler.write_pending_elems(None); @@ -1257,7 +1253,7 @@ fn string( if let Some(Class::Backline(line)) = klass { write_line_number_callback(out, line, "\n"); } else if let Some(Class::Expansion) = klass { - // Nothing to escape here so we get the text to write it directly. + // This has already been escaped so we get the text to write it directly. out.write_str(text.0).unwrap(); } else if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag) diff --git a/src/librustdoc/html/macro_expansion.rs b/src/librustdoc/html/macro_expansion.rs index d76663a3d1a..9098e92a5cd 100644 --- a/src/librustdoc/html/macro_expansion.rs +++ b/src/librustdoc/html/macro_expansion.rs @@ -45,16 +45,16 @@ pub(crate) struct ExpandedCode { struct ExpandedCodeInfo { /// Callsite of the macro. span: Span, - /// Expanded macro source code. + /// Expanded macro source code (HTML escaped). code: String, - /// Expanded span + /// Span of macro-generated code. expanded_span: Span, } /// HIR visitor which retrieves expanded macro. /// /// Once done, the `expanded_codes` will be transformed into a vec of [`ExpandedCode`] -/// which contains more information needed when running the source code highlighter. +/// which contains the information needed when running the source code highlighter. pub(crate) struct ExpandedCodeVisitor<'ast> { expanded_codes: Vec, source_map: &'ast SourceMap, @@ -71,7 +71,7 @@ impl<'ast> ExpandedCodeVisitor<'ast> { { let info = &mut self.expanded_codes[index]; if new_span.contains(info.expanded_span) { - // We replace the item. + // New macro expansion recursively contains the old one, so replace it. info.span = callsite_span; info.expanded_span = new_span; info.code = f(); diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs index 170449fc033..d42f4782845 100644 --- a/src/librustdoc/html/mod.rs +++ b/src/librustdoc/html/mod.rs @@ -3,8 +3,8 @@ pub(crate) mod format; pub(crate) mod highlight; pub(crate) mod layout; mod length_limit; -// used by the error-index generator, so it needs to be public pub(crate) mod macro_expansion; +// used by the error-index generator, so it needs to be public pub mod markdown; pub(crate) mod render; pub(crate) mod sources; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index fe56fdb2271..d891d1fba25 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -665,6 +665,14 @@ fn opts() -> Vec { "disable the minification of CSS/JS files (perma-unstable, do not use with cached files)", "", ), + opt( + Unstable, + Flag, + "", + "generate-macro-expansion", + "Add possibility to expand macros in the HTML source code pages", + "", + ), // deprecated / removed options opt( Stable, @@ -706,14 +714,6 @@ fn opts() -> Vec { "removed, see issue #44136 for more information", "[rust]", ), - opt( - Unstable, - Flag, - "", - "generate-macro-expansion", - "Add possibility to expand macros in the HTML source code pages", - "", - ), ] } @@ -958,15 +958,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { ) }), config::OutputFormat::Json => sess.time("render_json", || { - run_renderer( - krate, - render_opts, - cache, - tcx, - |krate, render_opts, cache, tcx| { - json::JsonRenderer::init(krate, render_opts, cache, tcx) - }, - ) + run_renderer(krate, render_opts, cache, tcx, json::JsonRenderer::init) }), // Already handled above with doctest runners. config::OutputFormat::Doctest => unreachable!(), diff --git a/tests/rustdoc-gui/macro-expansion.goml b/tests/rustdoc-gui/macro-expansion.goml index 5e9a3049405..b87d0e4870a 100644 --- a/tests/rustdoc-gui/macro-expansion.goml +++ b/tests/rustdoc-gui/macro-expansion.goml @@ -28,86 +28,99 @@ define-function: ( ) // First we check the derive macro expansion at line 33. -call-function: ("check-expansion", {"line": 33, "original_content": "Debug"}) +call-function: ("check-expansion", {"line": 35, "original_content": "Debug"}) // Then we check the `bar` macro expansion at line 41. -call-function: ("check-expansion", {"line": 41, "original_content": "bar!(y)"}) +call-function: ("check-expansion", {"line": 43, "original_content": "bar!(y)"}) // Then we check the `println` macro expansion at line 42-44. -call-function: ("check-expansion", {"line": 42, "original_content": 'println!(" -43 {y} -44 ")'}) +call-function: ("check-expansion", {"line": 44, "original_content": 'println!(" +45 {y} +46 ")'}) // Then finally we check when there are two macro calls on a same line. -assert-count: ("#expand-50 ~ .original", 2) -assert-count: ("#expand-50 ~ .expanded", 2) +assert-count: ("#expand-52 ~ .original", 2) +assert-count: ("#expand-52 ~ .expanded", 2) store-value: (repeat_o, '/following-sibling::*[@class="original"]') store-value: (repeat_e, '/following-sibling::*[@class="expanded"]') -assert-text: ('//*[@id="expand-50"]' + |repeat_o|, "stringify!(foo)") -assert-text: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, "stringify!(bar)") -assert-text: ('//*[@id="expand-50"]' + |repeat_e|, '"foo"') -assert-text: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, '"bar"') +store-value: (expand_id, "expand-52") +assert-text: ('//*[@id="' + |expand_id| + '"]' + |repeat_o|, "stringify!(foo)") +assert-text: ('//*[@id="' + |expand_id| + '"]' + |repeat_o| + |repeat_o|, "stringify!(bar)") +assert-text: ('//*[@id="' + |expand_id| + '"]' + |repeat_e|, '"foo"') +assert-text: ('//*[@id="' + |expand_id| + '"]' + |repeat_e| + |repeat_e|, '"bar"') // The "original" content should be expanded. -assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "inline"}) -assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) // The expanded macro should be hidden. -assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "none"}) -assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_e| + |repeat_e|, {"display": "none"}) // We "expand" the macro (because the line starts with a string, the label is not at the "top // level" of the ``, so we need to use a different selector). -click: "#expand-50" +click: "#" + |expand_id| // The "original" content is hidden. -assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "none"}) -assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "none"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_o|, {"display": "none"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_o| + |repeat_o|, {"display": "none"}) // The expanded macro is visible. -assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "inline"}) -assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "inline"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_e|, {"display": "inline"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_e| + |repeat_e|, {"display": "inline"}) // We collapse the macro. -click: "#expand-50" +click: "#" + |expand_id| // The "original" content is expanded. -assert-css: ('//*[@id="expand-50"]' + |repeat_o|, {"display": "inline"}) -assert-css: ('//*[@id="expand-50"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_o|, {"display": "inline"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_o| + |repeat_o|, {"display": "inline"}) // The expanded macro is hidden. -assert-css: ('//*[@id="expand-50"]' + |repeat_e|, {"display": "none"}) -assert-css: ('//*[@id="expand-50"]' + |repeat_e| + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_e|, {"display": "none"}) +assert-css: ('//*[@id="' + |expand_id| + '"]' + |repeat_e| + |repeat_e|, {"display": "none"}) -// Checking the line 46 `println` which needs to be handled differently because the line number is +// Checking the line 48 `println` which needs to be handled differently because the line number is // inside a "comment" span. -assert-text: ("#expand-46 ~ .original", 'println!(" -47 {y} -48 ")') +store-value: (expand_id, "expand-48") +assert-text: ("#" + |expand_id| + " ~ .original", 'println!(" +49 {y} +50 ")') // The "original" content should be expanded. -assert-css: ("#expand-46 ~ .original", {"display": "inline"}) +assert-css: ("#" + |expand_id| + " ~ .original", {"display": "inline"}) // The expanded macro should be hidden. -assert-css: ("#expand-46 ~ .expanded", {"display": "none"}) +assert-css: ("#" + |expand_id| + " ~ .expanded", {"display": "none"}) // We "expand" the macro. -click: "#expand-46" +click: "#" + |expand_id| // The "original" content is hidden. -assert-css: ("#expand-46 ~ .original", {"display": "none"}) +assert-css: ("#" + |expand_id| + " ~ .original", {"display": "none"}) // The expanded macro is visible. -assert-css: ("#expand-46 ~ .expanded", {"display": "inline"}) +assert-css: ("#" + |expand_id| + " ~ .expanded", {"display": "inline"}) // We collapse the macro. -click: "#expand-46" +click: "#" + |expand_id| // The "original" content is expanded. -assert-css: ("#expand-46 ~ .original", {"display": "inline"}) +assert-css: ("#" + |expand_id| + " ~ .original", {"display": "inline"}) // The expanded macro is hidden. -assert-css: ("#expand-46 ~ .expanded", {"display": "none"}) +assert-css: ("#" + |expand_id| + " ~ .expanded", {"display": "none"}) // Ensure that the toggles are focusable and can be interacted with keyboard. -focus: "//a[@id='27']" +focus: "//a[@id='29']" press-key: "Tab" -assert: "#expand-27:focus" -assert-css: ("#expand-27 ~ .expanded", {"display": "none"}) -assert-css: ("#expand-27 ~ .original", {"display": "inline"}) +store-value: (expand_id, "expand-29") +assert: "#" + |expand_id| + ":focus" +assert-css: ("#" + |expand_id| +" ~ .expanded", {"display": "none"}) +assert-css: ("#" + |expand_id| +" ~ .original", {"display": "inline"}) // We now expand the macro. press-key: "Space" -assert-css: ("#expand-27 ~ .expanded", {"display": "inline"}) -assert-css: ("#expand-27 ~ .original", {"display": "none"}) +assert-css: ("#" + |expand_id| + " ~ .expanded", {"display": "inline"}) +assert-css: ("#" + |expand_id| + " ~ .original", {"display": "none"}) // We collapse the macro. press-key: "Space" -assert-css: ("#expand-27 ~ .expanded", {"display": "none"}) -assert-css: ("#expand-27 ~ .original", {"display": "inline"}) +assert-css: ("#" + |expand_id| + " ~ .expanded", {"display": "none"}) +assert-css: ("#" + |expand_id| + " ~ .original", {"display": "inline"}) + +// Now we check a macro coming from another file. +store-value: (expand_id, "expand-55") +// We "expand" the macro. +click: "#" + |expand_id| +// The "original" content is hidden. +assert-css: ("#" + |expand_id| + " ~ .original", {"display": "none"}) +// The expanded macro is visible. +assert-css: ("#" + |expand_id| + " ~ .expanded", {"display": "inline"}) +assert-text: ("#" + |expand_id| + " ~ .expanded", "{ y += 2; };") diff --git a/tests/rustdoc-gui/src/macro_expansion/lib.rs b/tests/rustdoc-gui/src/macro_expansion/lib.rs index 7926e0143d8..62a92d5d15e 100644 --- a/tests/rustdoc-gui/src/macro_expansion/lib.rs +++ b/tests/rustdoc-gui/src/macro_expansion/lib.rs @@ -1,6 +1,8 @@ // Test crate used to check the `--generate-macro-expansion` option. //@ compile-flags: -Zunstable-options --generate-macro-expansion --generate-link-to-definition +mod other; + #[macro_export] macro_rules! bar { ($x:ident) => {{ @@ -48,4 +50,7 @@ fn foo() { "); let s = y_f("\ bla", stringify!(foo), stringify!(bar)); + + // Macro from another file. + other_macro!(y); } diff --git a/tests/rustdoc-gui/src/macro_expansion/other.rs b/tests/rustdoc-gui/src/macro_expansion/other.rs new file mode 100644 index 00000000000..8661b01be38 --- /dev/null +++ b/tests/rustdoc-gui/src/macro_expansion/other.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! other_macro { + ($x:ident) => {{ + $x += 2; + }} +} -- cgit 1.4.1-3-g733a5 From d0913c571cde92c4d0db6b1bac94ac320c0e9d2e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Aug 2025 12:15:37 +0200 Subject: Update `tests/run-make/rustdoc-default-output/` output --- tests/run-make/rustdoc-default-output/output-default.stdout | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 765c2060c72..badbc0b6d15 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -194,6 +194,9 @@ Options: --disable-minification disable the minification of CSS/JS files (perma-unstable, do not use with cached files) + --generate-macro-expansion + Add possibility to expand macros in the HTML source + code pages --plugin-path DIR removed, see issue #44136 for @@ -212,9 +215,6 @@ Options: removed, see issue #44136 for more information - --generate-macro-expansion - Add possibility to expand macros in the HTML source - code pages @path Read newline separated options from `path` -- cgit 1.4.1-3-g733a5 From 399f5e244cfb77b2c3addaeeae6007df5618bb9d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova <58857108+ada4a@users.noreply.github.com> Date: Sat, 23 Aug 2025 23:05:30 +0200 Subject: std/src/lib.rs: mention "search button" instead of "search bar" --- library/std/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ab417b6c72f..f0de03c9a28 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -15,7 +15,7 @@ //! //! If you already know the name of what you are looking for, the fastest way to //! find it is to use the search -//! bar at the top of the page. +//! button at the top of the page. //! //! Otherwise, you may want to jump to one of these useful sections: //! -- cgit 1.4.1-3-g733a5 From d4cbd9a440d0106fed53cc173dd539492f95f382 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 27 Jul 2025 12:56:02 +0200 Subject: Add lint against integer to pointer transmutes --- compiler/rustc_lint/messages.ftl | 8 ++ compiler/rustc_lint/src/lints.rs | 42 +++++++++ compiler/rustc_lint/src/transmute.rs | 92 ++++++++++++++++++- tests/ui/lint/int_to_ptr.fixed | 47 ++++++++++ tests/ui/lint/int_to_ptr.rs | 47 ++++++++++ tests/ui/lint/int_to_ptr.stderr | 171 +++++++++++++++++++++++++++++++++++ 6 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 tests/ui/lint/int_to_ptr.fixed create mode 100644 tests/ui/lint/int_to_ptr.rs create mode 100644 tests/ui/lint/int_to_ptr.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 940a07c94df..417e5a97069 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -462,6 +462,14 @@ lint_invalid_reference_casting_note_book = for more information, visit + .help_exposed_provenance = for more information about exposed provenance, see + .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance + .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + lint_legacy_derive_helpers = derive helper attribute is used before it is introduced .label = the attribute is introduced here diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 6c509734626..5c91b36133b 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + #![allow(rustc::untranslatable_diagnostic)] use std::num::NonZero; @@ -1542,6 +1544,46 @@ impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> { } } +// transmute.rs +#[derive(LintDiagnostic)] +#[diag(lint_int_to_ptr_transmutes)] +#[note] +#[note(lint_note_exposed_provenance)] +#[help(lint_suggestion_without_provenance_mut)] +#[help(lint_help_transmute)] +#[help(lint_help_exposed_provenance)] +pub(crate) struct IntegerToPtrTransmutes<'tcx> { + #[subdiagnostic] + pub suggestion: IntegerToPtrTransmutesSuggestion<'tcx>, +} + +#[derive(Subdiagnostic)] +pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { + #[multipart_suggestion( + lint_suggestion_with_exposed_provenance, + applicability = "machine-applicable" + )] + ToPtr { + dst: Ty<'tcx>, + suffix: &'static str, + #[suggestion_part(code = "std::ptr::with_exposed_provenance{suffix}::<{dst}>(")] + start_call: Span, + }, + #[multipart_suggestion( + lint_suggestion_with_exposed_provenance, + applicability = "machine-applicable" + )] + ToRef { + dst: Ty<'tcx>, + suffix: &'static str, + ref_mutbl: &'static str, + #[suggestion_part( + code = "&{ref_mutbl}*std::ptr::with_exposed_provenance{suffix}::<{dst}>(" + )] + start_call: Span, + }, +} + // types.rs #[derive(LintDiagnostic)] #[diag(lint_range_endpoint_out_of_range)] diff --git a/compiler/rustc_lint/src/transmute.rs b/compiler/rustc_lint/src/transmute.rs index bc1d4587d07..239c8649041 100644 --- a/compiler/rustc_lint/src/transmute.rs +++ b/compiler/rustc_lint/src/transmute.rs @@ -1,3 +1,4 @@ +use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; @@ -7,6 +8,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::sym; +use crate::lints::{IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion}; use crate::{LateContext, LateLintPass}; declare_lint! { @@ -67,9 +69,44 @@ declare_lint! { "detects transmutes that can also be achieved by other operations" } +declare_lint! { + /// The `integer_to_ptr_transmutes` lint detects integer to pointer + /// transmutes where the resulting pointers are undefined behavior to dereference. + /// + /// ### Example + /// + /// ```rust + /// fn foo(a: usize) -> *const u8 { + /// unsafe { + /// std::mem::transmute::(a) + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Any attempt to use the resulting pointers are undefined behavior as the resulting + /// pointers won't have any provenance. + /// + /// Alternatively, [`std::ptr::with_exposed_provenance`] should be used, as they do not + /// carry the provenance requirement. If wanting to create pointers without provenance + /// [`std::ptr::without_provenance`] should be used instead. + /// + /// See [`std::mem::transmute`] in the reference for more details. + /// + /// [`std::mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html + /// [`std::ptr::with_exposed_provenance`]: https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html + /// [`std::ptr::without_provenance`]: https://doc.rust-lang.org/std/ptr/fn.without_provenance.html + pub INTEGER_TO_PTR_TRANSMUTES, + Warn, + "detects integer to pointer transmutes", +} + pub(crate) struct CheckTransmutes; -impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]); +impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES, INTEGER_TO_PTR_TRANSMUTES]); impl<'tcx> LateLintPass<'tcx> for CheckTransmutes { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { @@ -94,9 +131,62 @@ impl<'tcx> LateLintPass<'tcx> for CheckTransmutes { check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst); check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst); + check_int_to_ptr_transmute(cx, expr, arg, src, dst); } } +/// Check for transmutes from integer to pointers (*const/*mut and &/&mut). +/// +/// Using the resulting pointers would be undefined behavior. +fn check_int_to_ptr_transmute<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + src: Ty<'tcx>, + dst: Ty<'tcx>, +) { + if !matches!(src.kind(), ty::Uint(_) | ty::Int(_)) { + return; + } + let (ty::Ref(_, inner_ty, mutbl) | ty::RawPtr(inner_ty, mutbl)) = dst.kind() else { + return; + }; + // bail-out if the argument is literal 0 as we have other lints for those cases + if matches!(arg.kind, hir::ExprKind::Lit(hir::Lit { node: LitKind::Int(v, _), .. }) if v == 0) { + return; + } + // bail-out if the inner type is a ZST + let Ok(layout_inner_ty) = cx.tcx.layout_of(cx.typing_env().as_query_input(*inner_ty)) else { + return; + }; + if layout_inner_ty.is_1zst() { + return; + } + + let suffix = if mutbl.is_mut() { "_mut" } else { "" }; + cx.tcx.emit_node_span_lint( + INTEGER_TO_PTR_TRANSMUTES, + expr.hir_id, + expr.span, + IntegerToPtrTransmutes { + suggestion: if dst.is_ref() { + IntegerToPtrTransmutesSuggestion::ToRef { + dst: *inner_ty, + suffix, + ref_mutbl: mutbl.prefix_str(), + start_call: expr.span.shrink_to_lo().until(arg.span), + } + } else { + IntegerToPtrTransmutesSuggestion::ToPtr { + dst: *inner_ty, + suffix, + start_call: expr.span.shrink_to_lo().until(arg.span), + } + }, + }, + ); +} + /// Check for transmutes that exhibit undefined behavior. /// For example, transmuting pointers to integers in a const context. /// diff --git a/tests/ui/lint/int_to_ptr.fixed b/tests/ui/lint/int_to_ptr.fixed new file mode 100644 index 00000000000..8f373492e6f --- /dev/null +++ b/tests/ui/lint/int_to_ptr.fixed @@ -0,0 +1,47 @@ +// Checks for the `integer_to_pointer_transmutes` lint + +//@ check-pass +//@ run-rustfix + +#![allow(unused_unsafe)] +#![allow(dead_code)] + +unsafe fn should_lint(a: usize) { + let _ptr: *const u8 = unsafe { std::ptr::with_exposed_provenance::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ptr: *mut u8 = unsafe { std::ptr::with_exposed_provenance_mut::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static u8 = unsafe { &*std::ptr::with_exposed_provenance::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static mut u8 = unsafe { &mut *std::ptr::with_exposed_provenance_mut::(a) }; + //~^ WARN transmuting an integer to a pointer + + let _ptr = unsafe { std::ptr::with_exposed_provenance::(42usize) }; + //~^ WARN transmuting an integer to a pointer + let _ptr = unsafe { std::ptr::with_exposed_provenance::(a + a) }; + //~^ WARN transmuting an integer to a pointer +} + +const unsafe fn should_lintin_const(a: usize) { + let _ptr: *const u8 = unsafe { std::ptr::with_exposed_provenance::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ptr: *mut u8 = unsafe { std::ptr::with_exposed_provenance_mut::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static u8 = unsafe { &*std::ptr::with_exposed_provenance::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static mut u8 = unsafe { &mut *std::ptr::with_exposed_provenance_mut::(a) }; + //~^ WARN transmuting an integer to a pointer + + let _ptr = unsafe { std::ptr::with_exposed_provenance::(42usize) }; + //~^ WARN transmuting an integer to a pointer + let _ptr = unsafe { std::ptr::with_exposed_provenance::(a + a) }; + //~^ WARN transmuting an integer to a pointer +} + +unsafe fn should_not_lint(a: usize) { + let _ptr = unsafe { std::mem::transmute::(0usize) }; // linted by other lints + let _ptr = unsafe { std::mem::transmute::(a) }; // inner type is a ZST + let _ptr = unsafe { std::mem::transmute::(a) }; // omit fn-ptr for now +} + +fn main() {} diff --git a/tests/ui/lint/int_to_ptr.rs b/tests/ui/lint/int_to_ptr.rs new file mode 100644 index 00000000000..7f60da47b85 --- /dev/null +++ b/tests/ui/lint/int_to_ptr.rs @@ -0,0 +1,47 @@ +// Checks for the `integer_to_pointer_transmutes` lint + +//@ check-pass +//@ run-rustfix + +#![allow(unused_unsafe)] +#![allow(dead_code)] + +unsafe fn should_lint(a: usize) { + let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + + let _ptr = unsafe { std::mem::transmute::(42usize) }; + //~^ WARN transmuting an integer to a pointer + let _ptr = unsafe { std::mem::transmute::(a + a) }; + //~^ WARN transmuting an integer to a pointer +} + +const unsafe fn should_lintin_const(a: usize) { + let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; + //~^ WARN transmuting an integer to a pointer + + let _ptr = unsafe { std::mem::transmute::(42usize) }; + //~^ WARN transmuting an integer to a pointer + let _ptr = unsafe { std::mem::transmute::(a + a) }; + //~^ WARN transmuting an integer to a pointer +} + +unsafe fn should_not_lint(a: usize) { + let _ptr = unsafe { std::mem::transmute::(0usize) }; // linted by other lints + let _ptr = unsafe { std::mem::transmute::(a) }; // inner type is a ZST + let _ptr = unsafe { std::mem::transmute::(a) }; // omit fn-ptr for now +} + +fn main() {} diff --git a/tests/ui/lint/int_to_ptr.stderr b/tests/ui/lint/int_to_ptr.stderr new file mode 100644 index 00000000000..437f6442397 --- /dev/null +++ b/tests/ui/lint/int_to_ptr.stderr @@ -0,0 +1,171 @@ +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:10:36 + | +LL | let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; + | ----------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + = note: `#[warn(integer_to_ptr_transmutes)]` on by default + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:12:34 + | +LL | let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; + | --------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance_mut::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:14:38 + | +LL | let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; + | ------------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `&*std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:16:42 + | +LL | let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; + | ----------------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `&mut *std::ptr::with_exposed_provenance_mut::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:19:25 + | +LL | let _ptr = unsafe { std::mem::transmute::(42usize) }; + | ----------------------------------------^^^^^^^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:21:25 + | +LL | let _ptr = unsafe { std::mem::transmute::(a + a) }; + | ----------------------------------------^^^^^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:26:36 + | +LL | let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; + | ----------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:28:34 + | +LL | let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; + | --------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance_mut::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:30:38 + | +LL | let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; + | ------------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `&*std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:32:42 + | +LL | let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; + | ----------------------------------------------^^ + | | + | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `&mut *std::ptr::with_exposed_provenance_mut::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:35:25 + | +LL | let _ptr = unsafe { std::mem::transmute::(42usize) }; + | ----------------------------------------^^^^^^^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: transmuting an integer to a pointer creates a pointer without provenance + --> $DIR/int_to_ptr.rs:37:25 + | +LL | let _ptr = unsafe { std::mem::transmute::(a + a) }; + | ----------------------------------------^^^^^^ + | | + | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | + = note: this is dangerous because dereferencing the resulting pointer is undefined behavior + = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance + = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` + = help: for more information about transmute, see + = help: for more information about exposed provenance, see + +warning: 12 warnings emitted + -- cgit 1.4.1-3-g733a5 From f25bf37f1f9a254db517f48ced9553bb990dcd14 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 27 Jul 2025 13:20:46 +0200 Subject: Allow `integer_to_ptr_transmutes` in core --- library/core/src/ptr/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f5c490ca7ce..6fc85a83e17 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -914,6 +914,7 @@ pub const fn dangling() -> *const T { #[must_use] #[stable(feature = "strict_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] +#[allow(integer_to_ptr_transmutes)] // Expected semantics here. pub const fn without_provenance_mut(addr: usize) -> *mut T { // An int-to-pointer transmute currently has exactly the intended semantics: it creates a // pointer without provenance. Note that this is *not* a stable guarantee about transmute -- cgit 1.4.1-3-g733a5 From 3c664785c12a5b32b4489d0791cf8cc77fd1b3f6 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 27 Jul 2025 13:41:10 +0200 Subject: Allow `integer_to_ptr_transmutes` in tests --- src/tools/miri/tests/fail/branchless-select-i128-pointer.rs | 2 ++ src/tools/miri/tests/fail/provenance/provenance_transmute.rs | 2 ++ src/tools/miri/tests/fail/validity/dangling_ref1.rs | 3 +++ src/tools/miri/tests/panic/transmute_fat2.rs | 2 ++ src/tools/miri/tests/pass/binops.rs | 1 + src/tools/miri/tests/pass/too-large-primval-write-problem.rs | 2 ++ tests/ui/binop/binops.rs | 1 + 7 files changed, 13 insertions(+) diff --git a/src/tools/miri/tests/fail/branchless-select-i128-pointer.rs b/src/tools/miri/tests/fail/branchless-select-i128-pointer.rs index 2b861e5447b..7147813c4b6 100644 --- a/src/tools/miri/tests/fail/branchless-select-i128-pointer.rs +++ b/src/tools/miri/tests/fail/branchless-select-i128-pointer.rs @@ -1,3 +1,5 @@ +#![allow(integer_to_ptr_transmutes)] + use std::mem::transmute; #[cfg(target_pointer_width = "32")] diff --git a/src/tools/miri/tests/fail/provenance/provenance_transmute.rs b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs index d72f10530d7..60cb9a7f6bf 100644 --- a/src/tools/miri/tests/fail/provenance/provenance_transmute.rs +++ b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs @@ -1,5 +1,7 @@ //@compile-flags: -Zmiri-permissive-provenance +#![allow(integer_to_ptr_transmutes)] + use std::mem; // This is the example from diff --git a/src/tools/miri/tests/fail/validity/dangling_ref1.rs b/src/tools/miri/tests/fail/validity/dangling_ref1.rs index fc3a9f34463..57ba1117e76 100644 --- a/src/tools/miri/tests/fail/validity/dangling_ref1.rs +++ b/src/tools/miri/tests/fail/validity/dangling_ref1.rs @@ -1,5 +1,8 @@ // Make sure we catch this even without Stacked Borrows //@compile-flags: -Zmiri-disable-stacked-borrows + +#![allow(integer_to_ptr_transmutes)] + use std::mem; fn main() { diff --git a/src/tools/miri/tests/panic/transmute_fat2.rs b/src/tools/miri/tests/panic/transmute_fat2.rs index e695ff2d57b..7441f25d03e 100644 --- a/src/tools/miri/tests/panic/transmute_fat2.rs +++ b/src/tools/miri/tests/panic/transmute_fat2.rs @@ -1,3 +1,5 @@ +#![allow(integer_to_ptr_transmutes)] + fn main() { #[cfg(all(target_endian = "little", target_pointer_width = "64"))] let bad = unsafe { std::mem::transmute::(42) }; diff --git a/src/tools/miri/tests/pass/binops.rs b/src/tools/miri/tests/pass/binops.rs index 0aff7acb29d..fcbe6c85b7b 100644 --- a/src/tools/miri/tests/pass/binops.rs +++ b/src/tools/miri/tests/pass/binops.rs @@ -32,6 +32,7 @@ fn test_bool() { assert_eq!(true ^ true, false); } +#[allow(integer_to_ptr_transmutes)] fn test_ptr() { unsafe { let p1: *const u8 = ::std::mem::transmute(0_usize); diff --git a/src/tools/miri/tests/pass/too-large-primval-write-problem.rs b/src/tools/miri/tests/pass/too-large-primval-write-problem.rs index f4c418bd78a..00882b7ecca 100644 --- a/src/tools/miri/tests/pass/too-large-primval-write-problem.rs +++ b/src/tools/miri/tests/pass/too-large-primval-write-problem.rs @@ -7,6 +7,8 @@ // // This is just intended as a regression test to make sure we don't reintroduce this problem. +#![allow(integer_to_ptr_transmutes)] + #[cfg(target_pointer_width = "32")] fn main() { use std::mem::transmute; diff --git a/tests/ui/binop/binops.rs b/tests/ui/binop/binops.rs index 7142190a45b..702e9a61345 100644 --- a/tests/ui/binop/binops.rs +++ b/tests/ui/binop/binops.rs @@ -35,6 +35,7 @@ fn test_bool() { assert_eq!(true ^ true, false); } +#[allow(integer_to_ptr_transmutes)] fn test_ptr() { unsafe { let p1: *const u8 = ::std::mem::transmute(0_usize); -- cgit 1.4.1-3-g733a5 From 02a67cc4a535fdb226a57a3d8d3ef32d68fd347c Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 27 Jul 2025 21:21:49 +0200 Subject: Adjust clippy lints for rustc `integer_to_ptr_transmutes` lint --- .../src/transmute/useless_transmute.rs | 12 +----- src/tools/clippy/tests/ui/transmute.rs | 3 +- src/tools/clippy/tests/ui/transmute.stderr | 46 ++++++++-------------- .../ui/transmutes_expressible_as_ptr_casts.fixed | 3 -- .../ui/transmutes_expressible_as_ptr_casts.rs | 3 -- .../ui/transmutes_expressible_as_ptr_casts.stderr | 32 ++++++--------- 6 files changed, 32 insertions(+), 67 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs index ec5fb2793f9..b898920baef 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs @@ -49,17 +49,7 @@ pub(super) fn check<'tcx>( true }, (ty::Int(_) | ty::Uint(_), ty::RawPtr(_, _)) => { - span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from an integer to a pointer", - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified); - } - }, - ); + // Handled by the upstream rustc `integer_to_ptr_transmutes` lint true }, _ => false, diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index e968e7a5924..e7099104f94 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -4,6 +4,7 @@ dead_code, clippy::borrow_as_ptr, unnecessary_transmutes, + integer_to_ptr_transmutes, clippy::needless_lifetimes, clippy::missing_transmute_annotations )] @@ -60,12 +61,10 @@ fn useless() { //~^ useless_transmute let _: *const usize = std::mem::transmute(5_isize); - //~^ useless_transmute let _ = std::ptr::dangling::(); let _: *const usize = std::mem::transmute(1 + 1usize); - //~^ useless_transmute let _ = (1 + 1_usize) as *const usize; } diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 79528ec06f1..9478db09481 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:33:27 + --> tests/ui/transmute.rs:34:27 | LL | let _: *const T = core::mem::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` @@ -8,61 +8,49 @@ LL | let _: *const T = core::mem::transmute(t); = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:36:25 + --> tests/ui/transmute.rs:37:25 | LL | let _: *mut T = core::mem::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:39:27 + --> tests/ui/transmute.rs:40:27 | LL | let _: *const U = core::mem::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:47:27 + --> tests/ui/transmute.rs:48:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:50:27 + --> tests/ui/transmute.rs:51:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:53:27 + --> tests/ui/transmute.rs:54:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:56:27 + --> tests/ui/transmute.rs:57:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> tests/ui/transmute.rs:59:27 + --> tests/ui/transmute.rs:60:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ -error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:62:31 - | -LL | let _: *const usize = std::mem::transmute(5_isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` - -error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:67:31 - | -LL | let _: *const usize = std::mem::transmute(1 + 1usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` - error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:99:24 + --> tests/ui/transmute.rs:98:24 | LL | let _: Usize = core::mem::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +59,25 @@ LL | let _: Usize = core::mem::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:102:24 + --> tests/ui/transmute.rs:101:24 | LL | let _: Usize = core::mem::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> tests/ui/transmute.rs:105:31 + --> tests/ui/transmute.rs:104:31 | LL | let _: *const Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> tests/ui/transmute.rs:108:29 + --> tests/ui/transmute.rs:107:29 | LL | let _: *mut Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> tests/ui/transmute.rs:115:28 + --> tests/ui/transmute.rs:114:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -98,7 +86,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:122:28 + --> tests/ui/transmute.rs:121:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -107,16 +95,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:125:32 + --> tests/ui/transmute.rs:124:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:128:30 + --> tests/ui/transmute.rs:127:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 18 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed index e7ad2a1cbbc..02f67f79e2b 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -13,9 +13,6 @@ fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. - // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 }; - //~^ useless_transmute let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs index 42a81777a82..c5e156405eb 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -13,9 +13,6 @@ fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. - // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; - //~^ useless_transmute let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 7746f087cc7..f39a64d57eb 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,14 +1,5 @@ -error: transmute from an integer to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:17:39 - | -LL | let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` - | - = note: `-D clippy::useless-transmute` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` - error: transmute from a pointer to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:22:38 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:19:38 | LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +13,7 @@ LL + let _ptr_i8_transmute = unsafe { ptr_i32.cast::() }; | error: transmute from a pointer to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:29:46 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:26:46 | LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +25,7 @@ LL + let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] }; | error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:36:50 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:33:50 | LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` @@ -43,40 +34,43 @@ LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, us = help: to override `-D warnings` add `#[allow(clippy::transmutes_expressible_as_ptr_casts)]` error: transmute from a reference to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:43:41 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:40:41 | LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` + | + = note: `-D clippy::useless-transmute` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:52:41 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:49:41 | LL | let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:57:49 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:54:49 | LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from `*const u32` to `usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:61:36 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:58:36 | LL | let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&1u32 as *const u32 as usize` error: transmute from a reference to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:73:14 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:70:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` error: transmute from `fn()` to `*const u8` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:92:28 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:89:28 | LL | let _x: u8 = unsafe { *std::mem::transmute::(f) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(f as *const u8)` -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 1da4959e54eaee2b3770301fe4902b65f7b97e9f Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 16 Aug 2025 11:17:19 +0200 Subject: Prefer verbose suggestions for `integer_to_ptr_transmutes` lint --- compiler/rustc_lint/src/lints.rs | 6 ++- tests/ui/lint/int_to_ptr.stderr | 108 ++++++++++++++++++++++++++------------- 2 files changed, 76 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5c91b36133b..426500dda4a 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1561,7 +1561,8 @@ pub(crate) struct IntegerToPtrTransmutes<'tcx> { pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { #[multipart_suggestion( lint_suggestion_with_exposed_provenance, - applicability = "machine-applicable" + applicability = "machine-applicable", + style = "verbose" )] ToPtr { dst: Ty<'tcx>, @@ -1571,7 +1572,8 @@ pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> { }, #[multipart_suggestion( lint_suggestion_with_exposed_provenance, - applicability = "machine-applicable" + applicability = "machine-applicable", + style = "verbose" )] ToRef { dst: Ty<'tcx>, diff --git a/tests/ui/lint/int_to_ptr.stderr b/tests/ui/lint/int_to_ptr.stderr index 437f6442397..4035bda8fb2 100644 --- a/tests/ui/lint/int_to_ptr.stderr +++ b/tests/ui/lint/int_to_ptr.stderr @@ -2,9 +2,7 @@ warning: transmuting an integer to a pointer creates a pointer without provenanc --> $DIR/int_to_ptr.rs:10:36 | LL | let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; - | ----------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance @@ -12,160 +10,198 @@ LL | let _ptr: *const u8 = unsafe { std::mem::transmute::( = help: for more information about transmute, see = help: for more information about exposed provenance, see = note: `#[warn(integer_to_ptr_transmutes)]` on by default +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ptr: *const u8 = unsafe { std::ptr::with_exposed_provenance::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:12:34 | LL | let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; - | --------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance_mut::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance + | +LL - let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ptr: *mut u8 = unsafe { std::ptr::with_exposed_provenance_mut::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:14:38 | LL | let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; - | ------------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `&*std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ref: &'static u8 = unsafe { &*std::ptr::with_exposed_provenance::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:16:42 | LL | let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; - | ----------------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `&mut *std::ptr::with_exposed_provenance_mut::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance + | +LL - let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ref: &'static mut u8 = unsafe { &mut *std::ptr::with_exposed_provenance_mut::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:19:25 | LL | let _ptr = unsafe { std::mem::transmute::(42usize) }; - | ----------------------------------------^^^^^^^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ptr = unsafe { std::mem::transmute::(42usize) }; +LL + let _ptr = unsafe { std::ptr::with_exposed_provenance::(42usize) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:21:25 | LL | let _ptr = unsafe { std::mem::transmute::(a + a) }; - | ----------------------------------------^^^^^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ptr = unsafe { std::mem::transmute::(a + a) }; +LL + let _ptr = unsafe { std::ptr::with_exposed_provenance::(a + a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:26:36 | LL | let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; - | ----------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ptr: *const u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ptr: *const u8 = unsafe { std::ptr::with_exposed_provenance::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:28:34 | LL | let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; - | --------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance_mut::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance + | +LL - let _ptr: *mut u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ptr: *mut u8 = unsafe { std::ptr::with_exposed_provenance_mut::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:30:38 | LL | let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; - | ------------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `&*std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ref: &'static u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ref: &'static u8 = unsafe { &*std::ptr::with_exposed_provenance::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:32:42 | LL | let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; - | ----------------------------------------------^^ - | | - | help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance: `&mut *std::ptr::with_exposed_provenance_mut::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance_mut` instead to use a previously exposed provenance + | +LL - let _ref: &'static mut u8 = unsafe { std::mem::transmute::(a) }; +LL + let _ref: &'static mut u8 = unsafe { &mut *std::ptr::with_exposed_provenance_mut::(a) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:35:25 | LL | let _ptr = unsafe { std::mem::transmute::(42usize) }; - | ----------------------------------------^^^^^^^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ptr = unsafe { std::mem::transmute::(42usize) }; +LL + let _ptr = unsafe { std::ptr::with_exposed_provenance::(42usize) }; + | warning: transmuting an integer to a pointer creates a pointer without provenance --> $DIR/int_to_ptr.rs:37:25 | LL | let _ptr = unsafe { std::mem::transmute::(a + a) }; - | ----------------------------------------^^^^^^ - | | - | help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance: `std::ptr::with_exposed_provenance::(` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this is dangerous because dereferencing the resulting pointer is undefined behavior = note: exposed provenance semantics can be used to create a pointer based on some previously exposed provenance = help: if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` = help: for more information about transmute, see = help: for more information about exposed provenance, see +help: use `std::ptr::with_exposed_provenance` instead to use a previously exposed provenance + | +LL - let _ptr = unsafe { std::mem::transmute::(a + a) }; +LL + let _ptr = unsafe { std::ptr::with_exposed_provenance::(a + a) }; + | warning: 12 warnings emitted -- cgit 1.4.1-3-g733a5 From e4557f0ea4f7e015b3108dc509bce562f64528bc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 23 Aug 2025 20:54:02 +0000 Subject: Use unnamed lifetime spans as primary spans for MISMATCHED_LIFETIME_SYNTAXES --- compiler/rustc_lint/src/lifetime_syntax.rs | 8 +- src/tools/clippy/tests/ui/ptr_arg.stderr | 4 +- .../type-dependent/issue-71348.full.stderr | 4 +- .../rpit-assoc-pair-with-lifetime.stderr | 4 +- .../example-from-issue48686.stderr | 4 +- .../missing-lifetime-kind.stderr | 16 +-- .../not-tied-to-crate.stderr | 8 +- .../static.stderr | 16 +-- tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs | 6 +- .../lifetimes/mismatched-lifetime-syntaxes.stderr | 153 ++++++++++----------- .../elision/ignore-non-reference-lifetimes.stderr | 8 +- tests/ui/self/self_lifetime-async.stderr | 8 +- tests/ui/self/self_lifetime.stderr | 8 +- 13 files changed, 122 insertions(+), 125 deletions(-) diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs index 464f4fc34b9..413525eb6e5 100644 --- a/compiler/rustc_lint/src/lifetime_syntax.rs +++ b/compiler/rustc_lint/src/lifetime_syntax.rs @@ -214,9 +214,9 @@ impl LifetimeSyntaxCategories> { } } - pub fn flatten(&self) -> impl Iterator { - let Self { hidden, elided, named } = self; - [hidden.iter(), elided.iter(), named.iter()].into_iter().flatten() + pub fn iter_unnamed(&self) -> impl Iterator { + let Self { hidden, elided, named: _ } = self; + [hidden.iter(), elided.iter()].into_iter().flatten() } } @@ -495,7 +495,7 @@ fn emit_mismatch_diagnostic<'tcx>( cx.emit_span_lint( MISMATCHED_LIFETIME_SYNTAXES, - inputs.flatten().copied().collect::>(), + inputs.iter_unnamed().chain(outputs.iter_unnamed()).copied().collect::>(), lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions }, ); } diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 87235057349..f32e83d8b81 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -268,10 +268,10 @@ LL | fn barbar(_x: &mut Vec, y: &mut String) { | ^^^^^^^^^^^ help: change this to: `&mut str` error: eliding a lifetime that's named elsewhere is confusing - --> tests/ui/ptr_arg.rs:314:36 + --> tests/ui/ptr_arg.rs:314:56 | LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str { - | ^^ ^^ ---- the same lifetime is elided here + | -- -- ^^^^ the same lifetime is elided here | | | | | the lifetime is named here | the lifetime is named here diff --git a/tests/ui/const-generics/type-dependent/issue-71348.full.stderr b/tests/ui/const-generics/type-dependent/issue-71348.full.stderr index 32fa46b92b3..299ae680093 100644 --- a/tests/ui/const-generics/type-dependent/issue-71348.full.stderr +++ b/tests/ui/const-generics/type-dependent/issue-71348.full.stderr @@ -1,8 +1,8 @@ warning: hiding a lifetime that's named elsewhere is confusing - --> $DIR/issue-71348.rs:18:40 + --> $DIR/issue-71348.rs:18:56 | LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Target - | ^^ -- ------------------------ the same lifetime is hidden here + | -- -- ^^^^^^^^^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | | | the same lifetime is named here | the lifetime is named here diff --git a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr index 3651226e0c3..7a9254bac60 100644 --- a/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr +++ b/tests/ui/impl-trait/rpit-assoc-pair-with-lifetime.stderr @@ -1,8 +1,8 @@ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/rpit-assoc-pair-with-lifetime.rs:3:31 + --> $DIR/rpit-assoc-pair-with-lifetime.rs:3:82 | LL | pub fn iter<'a>(v: Vec<(u32, &'a u32)>) -> impl DoubleEndedIterator { - | ^^ the lifetime is named here ---- the same lifetime is elided here + | -- the lifetime is named here ^^^^ the same lifetime is elided here | = help: the same lifetime is referred to in inconsistent ways, making the signature confusing = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr index 5a7a5a6ebf9..2b7d6a6da27 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/example-from-issue48686.stderr @@ -1,8 +1,8 @@ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/example-from-issue48686.rs:6:21 + --> $DIR/example-from-issue48686.rs:6:50 | LL | pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 { - | ^^^^^^^ ------- the same lifetime is elided here + | ------- ^^^^^^^ the same lifetime is elided here | | | the lifetime is named here | diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr index af56a0a0ea5..c4d6e78d787 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/missing-lifetime-kind.stderr @@ -1,8 +1,8 @@ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/missing-lifetime-kind.rs:3:22 + --> $DIR/missing-lifetime-kind.rs:3:32 | LL | fn ampersand<'a>(x: &'a u8) -> &u8 { - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -18,10 +18,10 @@ LL | fn ampersand<'a>(x: &'a u8) -> &'a u8 { | ++ error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/missing-lifetime-kind.rs:10:21 + --> $DIR/missing-lifetime-kind.rs:10:31 | LL | fn brackets<'a>(x: &'a u8) -> Brackets { - | ^^ -------- the same lifetime is hidden here + | -- ^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -32,10 +32,10 @@ LL | fn brackets<'a>(x: &'a u8) -> Brackets<'a> { | ++++ error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/missing-lifetime-kind.rs:17:18 + --> $DIR/missing-lifetime-kind.rs:17:28 | LL | fn comma<'a>(x: &'a u8) -> Comma { - | ^^ --------- the same lifetime is hidden here + | -- ^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -46,10 +46,10 @@ LL | fn comma<'a>(x: &'a u8) -> Comma<'a, u8> { | +++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/missing-lifetime-kind.rs:22:23 + --> $DIR/missing-lifetime-kind.rs:22:34 | LL | fn underscore<'a>(x: &'a u8) -> &'_ u8 { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr index cf0a29678fa..28de809faab 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/not-tied-to-crate.stderr @@ -1,8 +1,8 @@ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/not-tied-to-crate.rs:8:16 + --> $DIR/not-tied-to-crate.rs:8:31 | LL | fn bar(x: &'static u8) -> &u8 { - | ^^^^^^^ --- the same lifetime is elided here + | ------- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -18,10 +18,10 @@ LL | fn bar(x: &'static u8) -> &'static u8 { | +++++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/not-tied-to-crate.rs:14:16 + --> $DIR/not-tied-to-crate.rs:14:31 | LL | fn baz(x: &'static u8) -> &u8 { - | ^^^^^^^ --- the same lifetime is elided here + | ------- ^^^ the same lifetime is elided here | | | the lifetime is named here | diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr index d60bec6f7e4..5f21a2877a7 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes-details/static.stderr @@ -1,8 +1,8 @@ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/static.rs:16:18 + --> $DIR/static.rs:16:33 | LL | fn ampersand(x: &'static u8) -> &u8 { - | ^^^^^^^ --- the same lifetime is elided here + | ------- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -18,10 +18,10 @@ LL | fn ampersand(x: &'static u8) -> &'static u8 { | +++++++ error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/static.rs:23:17 + --> $DIR/static.rs:23:32 | LL | fn brackets(x: &'static u8) -> Brackets { - | ^^^^^^^ -------- the same lifetime is hidden here + | ------- ^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -32,10 +32,10 @@ LL | fn brackets(x: &'static u8) -> Brackets<'static> { | +++++++++ error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/static.rs:30:14 + --> $DIR/static.rs:30:29 | LL | fn comma(x: &'static u8) -> Comma { - | ^^^^^^^ --------- the same lifetime is hidden here + | ------- ^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -46,10 +46,10 @@ LL | fn comma(x: &'static u8) -> Comma<'static, u8> { | ++++++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/static.rs:35:19 + --> $DIR/static.rs:35:35 | LL | fn underscore(x: &'static u8) -> &'_ u8 { - | ^^^^^^^ -- the same lifetime is elided here + | ------- ^^ the same lifetime is elided here | | | the lifetime is named here | diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs index f6260c47202..f404c4163a9 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.rs @@ -36,8 +36,8 @@ fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> Contains fn explicit_bound_path_to_explicit_anonymous_path<'a>( v: ContainsLifetime<'a>, - //~^ ERROR eliding a lifetime that's named elsewhere is confusing ) -> ContainsLifetime<'_> { + //~^ ERROR eliding a lifetime that's named elsewhere is confusing v } @@ -188,8 +188,8 @@ mod impl_trait { fn explicit_bound_path_to_impl_trait_precise_capture<'a>( v: ContainsLifetime<'a>, - //~^ ERROR eliding a lifetime that's named elsewhere is confusing ) -> impl FnOnce() + use<'_> { + //~^ ERROR eliding a lifetime that's named elsewhere is confusing move || _ = v } } @@ -208,8 +208,8 @@ mod dyn_trait { fn explicit_bound_path_to_dyn_trait_bound<'a>( v: ContainsLifetime<'a>, - //~^ ERROR hiding a lifetime that's named elsewhere is confusing ) -> Box + '_> { + //~^ ERROR hiding a lifetime that's named elsewhere is confusing Box::new(iter::once(v)) } } diff --git a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr index 20b7561c594..89768fc764a 100644 --- a/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr +++ b/tests/ui/lifetimes/mismatched-lifetime-syntaxes.stderr @@ -1,8 +1,8 @@ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:10:47 + --> $DIR/mismatched-lifetime-syntaxes.rs:10:57 | LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &u8 { - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -18,10 +18,10 @@ LL | fn explicit_bound_ref_to_implicit_ref<'a>(v: &'a u8) -> &'a u8 { | ++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:15:57 + --> $DIR/mismatched-lifetime-syntaxes.rs:15:68 | LL | fn explicit_bound_ref_to_explicit_anonymous_ref<'a>(v: &'a u8) -> &'_ u8 { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -36,7 +36,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:22:48 | LL | fn implicit_path_to_explicit_anonymous_path(v: ContainsLifetime) -> ContainsLifetime<'_> { - | ^^^^^^^^^^^^^^^^ -- the same lifetime is elided here + | ^^^^^^^^^^^^^^^^ ^^ the same lifetime is elided here | | | the lifetime is hidden here | @@ -50,7 +50,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:27:65 | LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> ContainsLifetime { - | ^^ ---------------- the same lifetime is hidden here + | ^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -61,10 +61,10 @@ LL | fn explicit_anonymous_path_to_implicit_path(v: ContainsLifetime<'_>) -> Con | ++++ error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:32:65 + --> $DIR/mismatched-lifetime-syntaxes.rs:32:73 | LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime { - | ^^ ---------------- the same lifetime is hidden here + | -- ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -75,13 +75,12 @@ LL | fn explicit_bound_path_to_implicit_path<'a>(v: ContainsLifetime<'a>) -> Con | ++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:38:25 + --> $DIR/mismatched-lifetime-syntaxes.rs:39:23 | LL | v: ContainsLifetime<'a>, - | ^^ the lifetime is named here -LL | + | -- the lifetime is named here LL | ) -> ContainsLifetime<'_> { - | -- the same lifetime is elided here + | ^^ the same lifetime is elided here | = help: the same lifetime is referred to in inconsistent ways, making the signature confusing help: consistently use `'a` @@ -94,7 +93,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:46:37 | LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime { - | ^^^ ---------------- the same lifetime is hidden here + | ^^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -108,7 +107,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:51:48 | LL | fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime { - | ^^ ---------------- the same lifetime is hidden here + | ^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -119,10 +118,10 @@ LL | fn explicit_anonymous_ref_to_implicit_path(v: &'_ u8) -> ContainsLifetime<' | ++++ error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:56:48 + --> $DIR/mismatched-lifetime-syntaxes.rs:56:58 | LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime { - | ^^ ---------------- the same lifetime is hidden here + | -- ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -133,10 +132,10 @@ LL | fn explicit_bound_ref_to_implicit_path<'a>(v: &'a u8) -> ContainsLifetime<' | ++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:61:58 + --> $DIR/mismatched-lifetime-syntaxes.rs:61:85 | LL | fn explicit_bound_ref_to_explicit_anonymous_path<'a>(v: &'a u8) -> ContainsLifetime<'_> { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -151,7 +150,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:68:37 | LL | fn implicit_path_to_implicit_ref(v: ContainsLifetime) -> &u8 { - | ^^^^^^^^^^^^^^^^ --- the same lifetime is elided here + | ^^^^^^^^^^^^^^^^ ^^^ the same lifetime is elided here | | | the lifetime is hidden here | @@ -165,7 +164,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:73:47 | LL | fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime) -> &'_ u8 { - | ^^^^^^^^^^^^^^^^ -- the same lifetime is elided here + | ^^^^^^^^^^^^^^^^ ^^ the same lifetime is elided here | | | the lifetime is hidden here | @@ -176,10 +175,10 @@ LL | fn implicit_path_to_explicit_anonymous_ref(v: ContainsLifetime<'_>) -> &'_ | ++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:78:64 + --> $DIR/mismatched-lifetime-syntaxes.rs:78:72 | LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &u8 { - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -190,10 +189,10 @@ LL | fn explicit_bound_path_to_implicit_ref<'a>(v: ContainsLifetime<'a>) -> &'a | ++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:83:74 + --> $DIR/mismatched-lifetime-syntaxes.rs:83:83 | LL | fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a>) -> &'_ u8 { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -205,10 +204,10 @@ LL + fn explicit_bound_path_to_explicit_anonymous_ref<'a>(v: ContainsLifetime<'a | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:89:55 + --> $DIR/mismatched-lifetime-syntaxes.rs:89:67 | LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &u8 { - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -219,10 +218,10 @@ LL | fn method_explicit_bound_ref_to_implicit_ref<'a>(&'a self) -> &'a u8 { | ++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:94:65 + --> $DIR/mismatched-lifetime-syntaxes.rs:94:78 | LL | fn method_explicit_bound_ref_to_explicit_anonymous_ref<'a>(&'a self) -> &'_ u8 { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -237,7 +236,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:101:56 | LL | fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> ContainsLifetime { - | ^^ ---------------- the same lifetime is hidden here + | ^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -248,10 +247,10 @@ LL | fn method_explicit_anonymous_ref_to_implicit_path(&'_ self) -> Contains | ++++ error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:106:56 + --> $DIR/mismatched-lifetime-syntaxes.rs:106:68 | LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> ContainsLifetime { - | ^^ ---------------- the same lifetime is hidden here + | -- ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -262,10 +261,10 @@ LL | fn method_explicit_bound_ref_to_implicit_path<'a>(&'a self) -> Contains | ++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:111:66 + --> $DIR/mismatched-lifetime-syntaxes.rs:111:95 | LL | fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) -> ContainsLifetime<'_> { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -277,10 +276,10 @@ LL + fn method_explicit_bound_ref_to_explicit_anonymous_path<'a>(&'a self) - | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:126:39 + --> $DIR/mismatched-lifetime-syntaxes.rs:126:54 | LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &u8 { - | ^^^^^^^ --- the same lifetime is elided here + | ------- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -291,10 +290,10 @@ LL | fn static_ref_to_implicit_ref(v: &'static u8) -> &'static u8 { | +++++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:131:49 + --> $DIR/mismatched-lifetime-syntaxes.rs:131:65 | LL | fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'_ u8 { - | ^^^^^^^ -- the same lifetime is elided here + | ------- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -306,10 +305,10 @@ LL + fn static_ref_to_explicit_anonymous_ref(v: &'static u8) -> &'static u8 | error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:136:40 + --> $DIR/mismatched-lifetime-syntaxes.rs:136:55 | LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime { - | ^^^^^^^ ---------------- the same lifetime is hidden here + | ------- ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -320,10 +319,10 @@ LL | fn static_ref_to_implicit_path(v: &'static u8) -> ContainsLifetime<'sta | +++++++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:141:50 + --> $DIR/mismatched-lifetime-syntaxes.rs:141:82 | LL | fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLifetime<'_> { - | ^^^^^^^ -- the same lifetime is elided here + | ------- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -335,10 +334,10 @@ LL + fn static_ref_to_explicit_anonymous_path(v: &'static u8) -> ContainsLif | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:147:40 + --> $DIR/mismatched-lifetime-syntaxes.rs:147:57 | LL | fn static_ref_to_implicit_ref(&'static self) -> &u8 { - | ^^^^^^^ --- the same lifetime is elided here + | ------- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -349,10 +348,10 @@ LL | fn static_ref_to_implicit_ref(&'static self) -> &'static u8 { | +++++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:152:50 + --> $DIR/mismatched-lifetime-syntaxes.rs:152:68 | LL | fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'_ u8 { - | ^^^^^^^ -- the same lifetime is elided here + | ------- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -364,10 +363,10 @@ LL + fn static_ref_to_explicit_anonymous_ref(&'static self) -> &'static | error: hiding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:157:41 + --> $DIR/mismatched-lifetime-syntaxes.rs:157:58 | LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime { - | ^^^^^^^ ---------------- the same lifetime is hidden here + | ------- ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is named here | @@ -378,10 +377,10 @@ LL | fn static_ref_to_implicit_path(&'static self) -> ContainsLifetime<' | +++++++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:162:51 + --> $DIR/mismatched-lifetime-syntaxes.rs:162:85 | LL | fn static_ref_to_explicit_anonymous_path(&'static self) -> ContainsLifetime<'_> { - | ^^^^^^^ -- the same lifetime is elided here + | ------- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -393,10 +392,10 @@ LL + fn static_ref_to_explicit_anonymous_path(&'static self) -> Contains | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:174:55 + --> $DIR/mismatched-lifetime-syntaxes.rs:174:81 | LL | fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce() + '_ { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -408,10 +407,10 @@ LL + fn explicit_bound_ref_to_impl_trait_bound<'a>(v: &'a u8) -> impl FnOnce | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:179:65 + --> $DIR/mismatched-lifetime-syntaxes.rs:179:95 | LL | fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> impl FnOnce() + use<'_> { - | ^^ the lifetime is named here -- the same lifetime is elided here + | -- the lifetime is named here ^^ the same lifetime is elided here | = help: the same lifetime is referred to in inconsistent ways, making the signature confusing help: consistently use `'a` @@ -421,10 +420,10 @@ LL + fn explicit_bound_ref_to_impl_trait_precise_capture<'a>(v: &'a u8) -> i | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:184:72 + --> $DIR/mismatched-lifetime-syntaxes.rs:184:96 | LL | fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) -> impl FnOnce() + '_ { - | ^^ -- the same lifetime is elided here + | -- ^^ the same lifetime is elided here | | | the lifetime is named here | @@ -436,13 +435,12 @@ LL + fn explicit_bound_path_to_impl_trait_bound<'a>(v: ContainsLifetime<'a>) | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:190:29 + --> $DIR/mismatched-lifetime-syntaxes.rs:191:30 | LL | v: ContainsLifetime<'a>, - | ^^ the lifetime is named here -LL | + | -- the lifetime is named here LL | ) -> impl FnOnce() + use<'_> { - | -- the same lifetime is elided here + | ^^ the same lifetime is elided here | = help: the same lifetime is referred to in inconsistent ways, making the signature confusing help: consistently use `'a` @@ -452,10 +450,10 @@ LL + ) -> impl FnOnce() + use<'a> { | error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:204:54 + --> $DIR/mismatched-lifetime-syntaxes.rs:204:88 | LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box + '_> { - | ^^ the lifetime is named here --- the same lifetime is elided here + | -- the lifetime is named here ^^^ the same lifetime is elided here | = help: the same lifetime is referred to in inconsistent ways, making the signature confusing help: consistently use `'a` @@ -464,13 +462,12 @@ LL | fn explicit_bound_ref_to_dyn_trait_bound<'a>(v: &'a u8) -> Box $DIR/mismatched-lifetime-syntaxes.rs:210:29 + --> $DIR/mismatched-lifetime-syntaxes.rs:211:34 | LL | v: ContainsLifetime<'a>, - | ^^ the lifetime is named here -LL | + | -- the lifetime is named here LL | ) -> Box + '_> { - | ---------------- the same lifetime is hidden here + | ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | = help: the same lifetime is referred to in inconsistent ways, making the signature confusing help: consistently use `'a` @@ -479,10 +476,10 @@ LL | ) -> Box> + '_> { | ++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:222:33 + --> $DIR/mismatched-lifetime-syntaxes.rs:222:52 | LL | fn multiple_inputs<'a>(v: (&'a u8, &'a u8)) -> &u8 { - | ^^ ^^ --- the same lifetime is elided here + | -- -- ^^^ the same lifetime is elided here | | | | | the lifetime is named here | the lifetime is named here @@ -494,10 +491,10 @@ LL | fn multiple_inputs<'a>(v: (&'a u8, &'a u8)) -> &'a u8 { | ++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:227:33 + --> $DIR/mismatched-lifetime-syntaxes.rs:227:44 | LL | fn multiple_outputs<'a>(v: &'a u8) -> (&u8, &u8) { - | ^^ --- --- the same lifetime is elided here + | -- ^^^ ^^^ the same lifetime is elided here | | | | | the same lifetime is elided here | the lifetime is named here @@ -509,10 +506,10 @@ LL | fn multiple_outputs<'a>(v: &'a u8) -> (&'a u8, &'a u8) { | ++ ++ error: hiding or eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:232:53 + --> $DIR/mismatched-lifetime-syntaxes.rs:232:62 | LL | fn all_three_categories<'a>(v: ContainsLifetime<'a>) -> (&u8, ContainsLifetime) { - | ^^ --- ---------------- the same lifetime is hidden here + | -- ^^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | | | the same lifetime is elided here | the lifetime is named here @@ -524,10 +521,10 @@ LL | fn all_three_categories<'a>(v: ContainsLifetime<'a>) -> (&'a u8, Contai | ++ ++++ error: eliding a lifetime that's named elsewhere is confusing - --> $DIR/mismatched-lifetime-syntaxes.rs:237:38 + --> $DIR/mismatched-lifetime-syntaxes.rs:237:49 | LL | fn explicit_bound_output<'a>(v: &'a u8) -> (&u8, &'a u8, ContainsLifetime<'a>) { - | ^^ --- -- -- the same lifetime is named here + | -- ^^^ -- -- the same lifetime is named here | | | | | | | the same lifetime is named here | | the same lifetime is elided here @@ -543,7 +540,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:250:45 | LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime; - | ^^^ ---------------- the same lifetime is hidden here + | ^^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -557,7 +554,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:253:49 | LL | fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime; - | ^^^^^ ---------------- the same lifetime is hidden here + | ^^^^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -571,7 +568,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:258:45 | LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime { - | ^^^ ---------------- the same lifetime is hidden here + | ^^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -585,7 +582,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:263:49 | LL | fn method_implicit_ref_to_implicit_path(&self) -> ContainsLifetime { - | ^^^^^ ---------------- the same lifetime is hidden here + | ^^^^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | @@ -599,7 +596,7 @@ error: hiding a lifetime that's elided elsewhere is confusing --> $DIR/mismatched-lifetime-syntaxes.rs:277:45 | LL | fn implicit_ref_to_implicit_path(v: &u8) -> ContainsLifetime; - | ^^^ ---------------- the same lifetime is hidden here + | ^^^ ^^^^^^^^^^^^^^^^ the same lifetime is hidden here | | | the lifetime is elided here | diff --git a/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr b/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr index 7108fa1a290..1a5c87114d4 100644 --- a/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr +++ b/tests/ui/self/elision/ignore-non-reference-lifetimes.stderr @@ -1,8 +1,8 @@ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/ignore-non-reference-lifetimes.rs:6:30 + --> $DIR/ignore-non-reference-lifetimes.rs:6:41 | LL | fn a<'a>(self: Self, a: &'a str) -> &str { - | ^^ ---- the same lifetime is elided here + | -- ^^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -14,10 +14,10 @@ LL | fn a<'a>(self: Self, a: &'a str) -> &'a str { | ++ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/ignore-non-reference-lifetimes.rs:10:33 + --> $DIR/ignore-non-reference-lifetimes.rs:10:44 | LL | fn b<'a>(self: Foo<'b>, a: &'a str) -> &str { - | ^^ ---- the same lifetime is elided here + | -- ^^^^ the same lifetime is elided here | | | the lifetime is named here | diff --git a/tests/ui/self/self_lifetime-async.stderr b/tests/ui/self/self_lifetime-async.stderr index 43dc96abdc2..78cc610fd04 100644 --- a/tests/ui/self/self_lifetime-async.stderr +++ b/tests/ui/self/self_lifetime-async.stderr @@ -1,8 +1,8 @@ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/self_lifetime-async.rs:6:29 + --> $DIR/self_lifetime-async.rs:6:44 | LL | async fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -14,10 +14,10 @@ LL | async fn foo<'b>(self: &'b Foo<'a>) -> &'b () { self.0 } | ++ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/self_lifetime-async.rs:12:42 + --> $DIR/self_lifetime-async.rs:12:52 | LL | async fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | diff --git a/tests/ui/self/self_lifetime.stderr b/tests/ui/self/self_lifetime.stderr index 4f9b2fcd2ad..84f63454633 100644 --- a/tests/ui/self/self_lifetime.stderr +++ b/tests/ui/self/self_lifetime.stderr @@ -1,8 +1,8 @@ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/self_lifetime.rs:7:23 + --> $DIR/self_lifetime.rs:7:38 | LL | fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | @@ -14,10 +14,10 @@ LL | fn foo<'b>(self: &'b Foo<'a>) -> &'b () { self.0 } | ++ warning: eliding a lifetime that's named elsewhere is confusing - --> $DIR/self_lifetime.rs:13:36 + --> $DIR/self_lifetime.rs:13:46 | LL | fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } - | ^^ --- the same lifetime is elided here + | -- ^^^ the same lifetime is elided here | | | the lifetime is named here | -- cgit 1.4.1-3-g733a5 From 3bf61444616fc0b9de1e09031f40be0943823fc5 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Mon, 11 Aug 2025 11:46:30 +0200 Subject: Allow errors to be emitted as fatal during attribute parsing --- compiler/rustc_attr_parsing/src/context.rs | 40 ++++++++++++++++++++-------- compiler/rustc_attr_parsing/src/interface.rs | 2 +- compiler/rustc_attr_parsing/src/parser.rs | 23 +++++++++------- compiler/rustc_errors/src/diagnostic.rs | 23 ++++++++++++++++ 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index bb701df6053..f30edbfaaa1 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -5,7 +5,7 @@ use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; -use rustc_errors::Diagnostic; +use rustc_errors::{Diag, Diagnostic, Level}; use rustc_feature::AttributeTemplate; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; @@ -263,11 +263,7 @@ impl Stage for Early { sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { - if self.emit_errors.should_emit() { - sess.dcx().emit_err(diag) - } else { - sess.dcx().create_err(diag).delay_as_bug() - } + self.should_emit().emit_err_or_delay(sess.dcx().create_err(diag)) } fn should_emit(&self) -> ShouldEmit { @@ -333,7 +329,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { /// must be delayed until after HIR is built. This method will take care of the details of /// that. pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) { - if !self.stage.should_emit().should_emit() { + if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { return; } let id = self.target_id; @@ -651,8 +647,13 @@ pub enum OmitDoc { Skip, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum ShouldEmit { + /// The operations will emit errors, and lints, and errors are fatal. + /// + /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`. + /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible. + EarlyFatal, /// The operation will emit errors and lints. /// This is usually what you need. ErrorsAndLints, @@ -662,10 +663,27 @@ pub enum ShouldEmit { } impl ShouldEmit { - pub fn should_emit(&self) -> bool { + pub(crate) fn emit_err_or_delay(&self, diag: Diag<'_>) -> ErrorGuaranteed { + match self { + ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(), + ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(), + ShouldEmit::ErrorsAndLints => diag.emit(), + ShouldEmit::Nothing => diag.delay_as_bug(), + } + } + + pub(crate) fn maybe_emit_err(&self, diag: Diag<'_>) { match self { - ShouldEmit::ErrorsAndLints => true, - ShouldEmit::Nothing => false, + ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => { + diag.emit(); + } + ShouldEmit::EarlyFatal => { + diag.upgrade_to_fatal().emit(); + } + ShouldEmit::ErrorsAndLints => { + diag.emit(); + } + ShouldEmit::Nothing => {} } } } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f652d39a708..b4222e41b71 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -252,7 +252,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { (accept.accept_fn)(&mut cx, args); - if self.stage.should_emit().should_emit() { + if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) { self.check_target( path.get_attribute_path(), attr.span, diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 364f8819d13..3ba188938c6 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -328,15 +328,16 @@ fn expr_to_lit( match res { Ok(lit) => { if token_lit.suffix.is_some() { - psess - .dcx() - .create_err(SuffixedLiteralInAttribute { span: lit.span }) - .emit_unless_delay(!should_emit.should_emit()); + should_emit.emit_err_or_delay( + psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); None } else { - if should_emit.should_emit() && !lit.kind.is_unsuffixed() { + if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there - psess.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span }); + should_emit.maybe_emit_err( + psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); } Some(lit) @@ -366,7 +367,7 @@ fn expr_to_lit( err.downgrade_to_delayed_bug(); } - err.emit_unless_delay(!should_emit.should_emit()); + should_emit.emit_err_or_delay(err); None } } @@ -397,9 +398,11 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { } }; - if self.should_emit.should_emit() && !lit.kind.is_unsuffixed() { + if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there - self.parser.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span }); + self.should_emit.maybe_emit_err( + self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); } Ok(lit) @@ -539,7 +542,7 @@ impl<'a> MetaItemListParser<'a> { ) { Ok(s) => Some(s), Err(e) => { - e.emit_unless_delay(!should_emit.should_emit()); + should_emit.emit_err_or_delay(e); None } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 43ce886975c..96a4ed3218f 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -577,6 +577,29 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self.level = Level::DelayedBug; } + /// Make emitting this diagnostic fatal + /// + /// Changes the level of this diagnostic to Fatal, and importantly also changes the emission guarantee. + /// This is sound for errors that would otherwise be printed, but now simply exit the process instead. + /// This function still gives an emission guarantee, the guarantee is now just that it exits fatally. + /// For delayed bugs this is different, since those are buffered. If we upgrade one to fatal, another + /// might now be ignored. + #[rustc_lint_diagnostics] + #[track_caller] + pub fn upgrade_to_fatal(mut self) -> Diag<'a, FatalAbort> { + assert!( + matches!(self.level, Level::Error), + "upgrade_to_fatal: cannot upgrade {:?} to Fatal: not an error", + self.level + ); + self.level = Level::Fatal; + + // Take is okay since we immediately rewrap it in another diagnostic. + // i.e. we do emit it despite defusing the original diagnostic's drop bomb. + let diag = self.diag.take(); + Diag { dcx: self.dcx, diag, _marker: PhantomData } + } + with_fn! { with_span_label, /// Appends a labeled span to the diagnostic. /// -- cgit 1.4.1-3-g733a5 From 4b35cde904c1015df42b2c63244c1db1ed51fff9 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Mon, 11 Aug 2025 11:46:30 +0200 Subject: Support lints in early attribute parsing --- Cargo.lock | 1 - compiler/rustc_attr_parsing/src/interface.rs | 32 ++++++++--- compiler/rustc_attr_parsing/src/lints.rs | 6 +-- .../rustc_attr_parsing/src/session_diagnostics.rs | 6 +-- compiler/rustc_errors/Cargo.toml | 1 - compiler/rustc_errors/src/lib.rs | 6 +-- compiler/rustc_middle/src/ty/context.rs | 2 + compiler/rustc_session/src/session.rs | 17 +++++- .../lint/unused/unused_attributes-must_use.fixed | 2 + tests/ui/lint/unused/unused_attributes-must_use.rs | 2 + .../lint/unused/unused_attributes-must_use.stderr | 63 ++++++++++++---------- 11 files changed, 93 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89392631127..af206e87c45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3791,7 +3791,6 @@ dependencies = [ "rustc_error_messages", "rustc_fluent_macro", "rustc_hashes", - "rustc_hir_id", "rustc_index", "rustc_lexer", "rustc_lint_defs", diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index b4222e41b71..60523c2877c 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -50,6 +50,27 @@ impl<'sess> AttributeParser<'sess, Early> { target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, + ) -> Option { + Self::parse_limited_should_emit( + sess, + attrs, + sym, + target_span, + target_node_id, + features, + ShouldEmit::Nothing, + ) + } + + /// Usually you want `parse_limited`, which defaults to no errors. + pub fn parse_limited_should_emit( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: Symbol, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + should_emit: ShouldEmit, ) -> Option { let mut parsed = Self::parse_limited_all( sess, @@ -59,7 +80,7 @@ impl<'sess> AttributeParser<'sess, Early> { target_span, target_node_id, features, - ShouldEmit::Nothing, + should_emit, ); assert!(parsed.len() <= 1); parsed.pop() @@ -84,9 +105,8 @@ impl<'sess> AttributeParser<'sess, Early> { target, OmitDoc::Skip, std::convert::identity, - |_lint| { - // FIXME: Can't emit lints here for now - // This branch can be hit when an attribute produces a warning during early parsing (such as attributes on macro calls) + |lint| { + crate::lints::emit_attribute_lint(&lint, sess); }, ) } @@ -121,8 +141,8 @@ impl<'sess> AttributeParser<'sess, Early> { cx: &mut parser, target_span, target_id: target_node_id, - emit_lint: &mut |_lint| { - panic!("can't emit lints here for now (nothing uses this atm)"); + emit_lint: &mut |lint| { + crate::lints::emit_attribute_lint(&lint, sess); }, }, attr_span: attr.span, diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index 7030f28f23c..069478e7f0c 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -1,13 +1,13 @@ use std::borrow::Cow; use rustc_errors::{DiagArgValue, LintEmitter}; +use rustc_hir::Target; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; -use rustc_hir::{HirId, Target}; use rustc_span::sym; use crate::session_diagnostics; -pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) { +pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) { let AttributeLint { id, span, kind } = lint; match kind { @@ -51,7 +51,7 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi *id, *span, session_diagnostics::InvalidTargetLint { - name, + name: name.clone(), target: target.plural_name(), applied: DiagArgValue::StrListSepByAnd( applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 2192e8f8f83..72bee0ddfbf 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -484,9 +484,9 @@ pub(crate) struct EmptyAttributeList { #[diag(attr_parsing_invalid_target_lint)] #[warning] #[help] -pub(crate) struct InvalidTargetLint<'a> { - pub name: &'a AttrPath, - pub target: &'a str, +pub(crate) struct InvalidTargetLint { + pub name: AttrPath, + pub target: &'static str, pub applied: DiagArgValue, pub only: &'static str, #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index f37b6fb748f..7912b8e6098 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -14,7 +14,6 @@ rustc_error_codes = { path = "../rustc_error_codes" } rustc_error_messages = { path = "../rustc_error_messages" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } -rustc_hir_id = { path = "../rustc_hir_id" } rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 38c5716348f..71fc54f0d33 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -61,7 +61,6 @@ pub use rustc_error_messages::{ fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display, }; use rustc_hashes::Hash128; -use rustc_hir_id::HirId; pub use rustc_lint_defs::{Applicability, listify, pluralize}; use rustc_lint_defs::{Lint, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; @@ -110,13 +109,14 @@ rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); /// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`. /// Always the `TyCtxt`. pub trait LintEmitter: Copy { + type Id: Copy; #[track_caller] fn emit_node_span_lint( self, lint: &'static Lint, - hir_id: HirId, + hir_id: Self::Id, span: impl Into, - decorator: impl for<'a> LintDiagnostic<'a, ()>, + decorator: impl for<'a> LintDiagnostic<'a, ()> + DynSend + 'static, ); } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c215e44a965..4990a85466e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1421,6 +1421,8 @@ pub struct TyCtxt<'tcx> { } impl<'tcx> LintEmitter for TyCtxt<'tcx> { + type Id = HirId; + fn emit_node_span_lint( self, lint: &'static Lint, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index bb7ffa2a85d..96767d05439 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -7,6 +7,7 @@ use std::sync::atomic::AtomicBool; use std::{env, fmt, io}; use rand::{RngCore, rng}; +use rustc_ast::NodeId; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; @@ -22,7 +23,7 @@ use rustc_errors::timings::TimingSectionHandler; use rustc_errors::translation::Translator; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, - TerminalUrl, fallback_fluent_bundle, + LintEmitter, TerminalUrl, fallback_fluent_bundle, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -223,6 +224,20 @@ pub struct Session { pub invocation_temp: Option, } +impl LintEmitter for &'_ Session { + type Id = NodeId; + + fn emit_node_span_lint( + self, + lint: &'static rustc_lint_defs::Lint, + node_id: Self::Id, + span: impl Into, + decorator: impl for<'a> rustc_errors::LintDiagnostic<'a, ()> + DynSend + 'static, + ) { + self.psess.buffer_lint(lint, span, node_id, decorator); + } +} + #[derive(Clone, Copy)] pub enum CodegenUnits { /// Specified by the user. In this case we try fairly hard to produce the diff --git a/tests/ui/lint/unused/unused_attributes-must_use.fixed b/tests/ui/lint/unused/unused_attributes-must_use.fixed index 2e800cbff3f..6bc63c8ee5b 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.fixed +++ b/tests/ui/lint/unused/unused_attributes-must_use.fixed @@ -66,6 +66,8 @@ extern "Rust" { } //~ ERROR unused attribute +//~^ ERROR `#[must_use]` attribute cannot be used on macro calls +//~| WARN this was previously accepted by the compiler but is being phased out global_asm!(""); //~ ERROR attribute cannot be used on diff --git a/tests/ui/lint/unused/unused_attributes-must_use.rs b/tests/ui/lint/unused/unused_attributes-must_use.rs index c41c6c1d706..3c0d76e619f 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.rs +++ b/tests/ui/lint/unused/unused_attributes-must_use.rs @@ -66,6 +66,8 @@ extern "Rust" { } #[must_use] //~ ERROR unused attribute +//~^ ERROR `#[must_use]` attribute cannot be used on macro calls +//~| WARN this was previously accepted by the compiler but is being phased out global_asm!(""); #[must_use] //~ ERROR attribute cannot be used on diff --git a/tests/ui/lint/unused/unused_attributes-must_use.stderr b/tests/ui/lint/unused/unused_attributes-must_use.stderr index 03baea3a004..231d799057c 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.stderr +++ b/tests/ui/lint/unused/unused_attributes-must_use.stderr @@ -1,20 +1,29 @@ -error: unused attribute `must_use` +error: `#[must_use]` attribute cannot be used on macro calls --> $DIR/unused_attributes-must_use.rs:68:1 | LL | #[must_use] | ^^^^^^^^^^^ | -note: the built-in attribute `must_use` will be ignored, since it's applied to the macro invocation `global_asm` - --> $DIR/unused_attributes-must_use.rs:69:1 - | -LL | global_asm!(""); - | ^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[must_use]` can be applied to functions, data types, unions, and traits note: the lint level is defined here --> $DIR/unused_attributes-must_use.rs:4:9 | LL | #![deny(unused_attributes, unused_must_use)] | ^^^^^^^^^^^^^^^^^ +error: unused attribute `must_use` + --> $DIR/unused_attributes-must_use.rs:68:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + | +note: the built-in attribute `must_use` will be ignored, since it's applied to the macro invocation `global_asm` + --> $DIR/unused_attributes-must_use.rs:71:1 + | +LL | global_asm!(""); + | ^^^^^^^^^^ + error: `#[must_use]` attribute cannot be used on extern crates --> $DIR/unused_attributes-must_use.rs:7:1 | @@ -88,7 +97,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on type aliases - --> $DIR/unused_attributes-must_use.rs:71:1 + --> $DIR/unused_attributes-must_use.rs:73:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -97,7 +106,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on function params - --> $DIR/unused_attributes-must_use.rs:75:8 + --> $DIR/unused_attributes-must_use.rs:77:8 | LL | fn qux<#[must_use] T>(_: T) {} | ^^^^^^^^^^^ @@ -106,7 +115,7 @@ LL | fn qux<#[must_use] T>(_: T) {} = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on associated consts - --> $DIR/unused_attributes-must_use.rs:80:5 + --> $DIR/unused_attributes-must_use.rs:82:5 | LL | #[must_use] | ^^^^^^^^^^^ @@ -115,7 +124,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on associated types - --> $DIR/unused_attributes-must_use.rs:83:5 + --> $DIR/unused_attributes-must_use.rs:85:5 | LL | #[must_use] | ^^^^^^^^^^^ @@ -124,7 +133,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on trait impl blocks - --> $DIR/unused_attributes-must_use.rs:93:1 + --> $DIR/unused_attributes-must_use.rs:95:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -133,7 +142,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on trait methods in impl blocks - --> $DIR/unused_attributes-must_use.rs:98:5 + --> $DIR/unused_attributes-must_use.rs:100:5 | LL | #[must_use] | ^^^^^^^^^^^ @@ -142,7 +151,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to data types, functions, unions, required trait methods, provided trait methods, inherent methods, foreign functions, and traits error: `#[must_use]` attribute cannot be used on trait aliases - --> $DIR/unused_attributes-must_use.rs:105:1 + --> $DIR/unused_attributes-must_use.rs:107:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -151,7 +160,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on macro defs - --> $DIR/unused_attributes-must_use.rs:109:1 + --> $DIR/unused_attributes-must_use.rs:111:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -160,7 +169,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on statements - --> $DIR/unused_attributes-must_use.rs:118:5 + --> $DIR/unused_attributes-must_use.rs:120:5 | LL | #[must_use] | ^^^^^^^^^^^ @@ -169,7 +178,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on closures - --> $DIR/unused_attributes-must_use.rs:123:13 + --> $DIR/unused_attributes-must_use.rs:125:13 | LL | let x = #[must_use] | ^^^^^^^^^^^ @@ -178,7 +187,7 @@ LL | let x = #[must_use] = help: `#[must_use]` can be applied to methods, data types, functions, unions, foreign functions, and traits error: `#[must_use]` attribute cannot be used on match arms - --> $DIR/unused_attributes-must_use.rs:146:9 + --> $DIR/unused_attributes-must_use.rs:148:9 | LL | #[must_use] | ^^^^^^^^^^^ @@ -187,7 +196,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on struct fields - --> $DIR/unused_attributes-must_use.rs:155:28 + --> $DIR/unused_attributes-must_use.rs:157:28 | LL | let s = PatternField { #[must_use] foo: 123 }; | ^^^^^^^^^^^ @@ -196,7 +205,7 @@ LL | let s = PatternField { #[must_use] foo: 123 }; = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: `#[must_use]` attribute cannot be used on pattern fields - --> $DIR/unused_attributes-must_use.rs:157:24 + --> $DIR/unused_attributes-must_use.rs:159:24 | LL | let PatternField { #[must_use] foo } = s; | ^^^^^^^^^^^ @@ -205,7 +214,7 @@ LL | let PatternField { #[must_use] foo } = s; = help: `#[must_use]` can be applied to functions, data types, unions, and traits error: unused `X` that must be used - --> $DIR/unused_attributes-must_use.rs:128:5 + --> $DIR/unused_attributes-must_use.rs:130:5 | LL | X; | ^ @@ -221,7 +230,7 @@ LL | let _ = X; | +++++++ error: unused `Y` that must be used - --> $DIR/unused_attributes-must_use.rs:129:5 + --> $DIR/unused_attributes-must_use.rs:131:5 | LL | Y::Z; | ^^^^ @@ -232,7 +241,7 @@ LL | let _ = Y::Z; | +++++++ error: unused `U` that must be used - --> $DIR/unused_attributes-must_use.rs:130:5 + --> $DIR/unused_attributes-must_use.rs:132:5 | LL | U { unit: () }; | ^^^^^^^^^^^^^^ @@ -243,7 +252,7 @@ LL | let _ = U { unit: () }; | +++++++ error: unused return value of `U::method` that must be used - --> $DIR/unused_attributes-must_use.rs:131:5 + --> $DIR/unused_attributes-must_use.rs:133:5 | LL | U::method(); | ^^^^^^^^^^^ @@ -254,7 +263,7 @@ LL | let _ = U::method(); | +++++++ error: unused return value of `foo` that must be used - --> $DIR/unused_attributes-must_use.rs:132:5 + --> $DIR/unused_attributes-must_use.rs:134:5 | LL | foo(); | ^^^^^ @@ -265,7 +274,7 @@ LL | let _ = foo(); | +++++++ error: unused return value of `foreign_foo` that must be used - --> $DIR/unused_attributes-must_use.rs:135:9 + --> $DIR/unused_attributes-must_use.rs:137:9 | LL | foreign_foo(); | ^^^^^^^^^^^^^ @@ -276,7 +285,7 @@ LL | let _ = foreign_foo(); | +++++++ error: unused return value of `Use::get_four` that must be used - --> $DIR/unused_attributes-must_use.rs:143:5 + --> $DIR/unused_attributes-must_use.rs:145:5 | LL | ().get_four(); | ^^^^^^^^^^^^^ @@ -286,5 +295,5 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = ().get_four(); | +++++++ -error: aborting due to 29 previous errors +error: aborting due to 30 previous errors -- cgit 1.4.1-3-g733a5 From 59ceb02d65f13a20d29422f4d923ecde429d4c0c Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Mon, 11 Aug 2025 11:46:30 +0200 Subject: Port crate name to the new attribute system --- .../src/attributes/crate_level.rs | 33 ++++++ compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/attributes/util.rs | 6 +- compiler/rustc_attr_parsing/src/context.rs | 8 +- compiler/rustc_attr_parsing/src/lib.rs | 4 +- compiler/rustc_hir/src/attrs/data_structures.rs | 3 + compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 6 + compiler/rustc_interface/src/passes.rs | 28 ++++- compiler/rustc_interface/src/util.rs | 12 +- compiler/rustc_lint/src/nonstandard_style.rs | 52 ++++----- compiler/rustc_passes/src/check_attr.rs | 46 ++++++-- tests/ui/attributes/crate-name-empty.stderr | 4 +- tests/ui/attributes/crate-name-macro-call.rs | 2 +- tests/ui/attributes/crate-name-macro-call.stderr | 8 +- tests/ui/attributes/crate-name-mismatch.stderr | 4 +- tests/ui/attributes/malformed-attrs.stderr | 14 +-- ...rint-crate-name-request-malformed-crate-name.rs | 2 +- ...-crate-name-request-malformed-crate-name.stderr | 8 +- ...ile-names-request-malformed-crate-name-1.stderr | 5 +- ...nt-file-names-request-malformed-crate-name-2.rs | 2 +- ...ile-names-request-malformed-crate-name-2.stderr | 8 +- ...rint-file-names-request-malformed-crate-name.rs | 2 +- ...-file-names-request-malformed-crate-name.stderr | 8 +- tests/ui/lint/unused/unused-attr-duplicate.rs | 8 +- tests/ui/lint/unused/unused-attr-duplicate.stderr | 130 ++++++++++++--------- 25 files changed, 248 insertions(+), 156 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/crate_level.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs new file mode 100644 index 00000000000..9175d7479e1 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -0,0 +1,33 @@ +use super::prelude::*; + +pub(crate) struct CrateNameParser; + +impl SingleAttributeParser for CrateNameParser { + const PATH: &[Symbol] = &[sym::crate_name]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); + + // FIXME: crate name is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(n) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + + let Some(name) = n.value_as_str() else { + cx.expected_string_literal(n.value_span, Some(n.value_as_lit())); + return None; + }; + + Some(AttributeKind::CrateName { + name, + name_span: n.value_span, + attr_span: cx.attr_span, + style: cx.attr_style, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index b98678041d7..9dad9c893f0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -35,6 +35,7 @@ pub(crate) mod cfg; pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; +pub(crate) mod crate_level; pub(crate) mod deprecation; pub(crate) mod dummy; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index ef9701cb7cb..77e8c32e59d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -1,5 +1,5 @@ use rustc_ast::LitKind; -use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name}; +use rustc_ast::attr::AttributeExt; use rustc_feature::is_builtin_attr_name; use rustc_hir::RustcVersion; use rustc_span::{Symbol, sym}; @@ -27,10 +27,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } -pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { - first_attr_value_str_by_name(attrs, sym::crate_name) -} - pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( attrs: impl Iterator, symbol: Symbol, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f30edbfaaa1..128ecd67d2a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -24,6 +24,7 @@ use crate::attributes::codegen_attrs::{ UsedParser, }; use crate::attributes::confusables::ConfusablesParser; +use crate::attributes::crate_level::CrateNameParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -166,6 +167,7 @@ attribute_parsers!( // tidy-alphabetical-start Single, + Single, Single, Single, Single, @@ -310,7 +312,9 @@ pub struct AcceptContext<'f, 'sess, S: Stage> { /// The span of the attribute currently being parsed pub(crate) attr_span: Span, + /// Whether it is an inner or outer attribute pub(crate) attr_style: AttrStyle, + /// The expected structure of the attribute. /// /// Used in reporting errors to give a hint to users what the attribute *should* look like. @@ -683,7 +687,9 @@ impl ShouldEmit { ShouldEmit::ErrorsAndLints => { diag.emit(); } - ShouldEmit::Nothing => {} + ShouldEmit::Nothing => { + diag.cancel(); + } } } } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 969c24a4f89..4dd908cdc40 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -106,9 +106,7 @@ pub mod validate_attr; pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr}; pub use attributes::cfg_old::*; -pub use attributes::util::{ - find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, -}; +pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; pub use interface::AttributeParser; pub use lints::emit_attribute_lint; diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 1e73bb6f5fd..09da5772d23 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -367,6 +367,9 @@ pub enum AttributeKind { /// Represents `#[coverage(..)]`. Coverage(Span, CoverageAttrKind), + /// Represents `#[crate_name = ...]` + CrateName { name: Symbol, name_span: Span, attr_span: Span, style: AttrStyle }, + /// Represents `#[custom_mir]`. CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 77c6faa7acd..e5329c104bb 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -7,6 +7,11 @@ pub enum EncodeCrossCrate { } impl AttributeKind { + /// Whether this attribute should be encoded in metadata files. + /// + /// If this is "Yes", then another crate can do `tcx.get_all_attrs(did)` for a did in this crate, and get the attribute. + /// When this is No, the attribute is filtered out while encoding and other crate won't be able to observe it. + /// This can be unexpectedly good for performance, so unless necessary for cross-crate compilation, prefer No. pub fn encode_cross_crate(&self) -> EncodeCrossCrate { use AttributeKind::*; use EncodeCrossCrate::*; @@ -31,6 +36,7 @@ impl AttributeKind { ConstTrait(..) => No, Coroutine(..) => No, Coverage(..) => No, + CrateName { .. } => No, CustomMir(_, _, _) => Yes, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 90f7ae76387..bc5ef04079e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -6,7 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; use rustc_ast as ast; -use rustc_attr_parsing::validate_attr; +use rustc_attr_parsing::{AttributeParser, ShouldEmit, validate_attr}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; @@ -16,6 +16,7 @@ use rustc_errors::timings::TimingSection; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_incremental::setup_dep_graph; @@ -1244,8 +1245,7 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol // in all code paths that require the crate name very early on, namely before // macro expansion. - let attr_crate_name = - validate_and_find_value_str_builtin_attr(sym::crate_name, sess, krate_attrs); + let attr_crate_name = parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal); let validate = |name, span| { rustc_session::output::validate_crate_name(sess, name, span); @@ -1283,6 +1283,28 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol sym::rust_out } +pub(crate) fn parse_crate_name( + sess: &Session, + attrs: &[ast::Attribute], + emit_errors: ShouldEmit, +) -> Option<(Symbol, Span)> { + let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = + AttributeParser::parse_limited_should_emit( + sess, + &attrs, + sym::crate_name, + DUMMY_SP, + rustc_ast::node_id::CRATE_NODE_ID, + None, + emit_errors, + )? + else { + unreachable!("crate_name is the only attr we could've parsed here"); + }; + + Some((name, name_span)) +} + fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { // We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`]) // because that would require expanding this while in the middle of expansion, which needs to diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 9a432a60819..04006f3e446 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, OnceLock}; use std::{env, thread}; use rustc_ast as ast; -use rustc_attr_parsing::validate_attr; +use rustc_attr_parsing::{ShouldEmit, validate_attr}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; @@ -24,6 +24,7 @@ use rustc_target::spec::Target; use tracing::info; use crate::errors; +use crate::passes::parse_crate_name; /// Function pointer type that constructs a new CodegenBackend. type MakeBackendFn = fn() -> Box; @@ -520,11 +521,10 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu sess.dcx().emit_fatal(errors::MultipleOutputTypesToStdout); } - let crate_name = sess - .opts - .crate_name - .clone() - .or_else(|| rustc_attr_parsing::find_crate_name(attrs).map(|n| n.to_string())); + let crate_name = + sess.opts.crate_name.clone().or_else(|| { + parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string()) + }); match sess.io.output_file { None => { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 8fafaa33d0c..65075cfecfa 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -5,7 +5,7 @@ use rustc_hir::attrs::{AttributeKind, ReprAttr}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor}; -use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; +use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; use rustc_middle::hir::nested_filter::All; use rustc_middle::ty; use rustc_session::config::CrateType; @@ -343,35 +343,27 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - ast::attr::find_by_name(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), sym::crate_name).and_then( - |attr| { - if let Attribute::Unparsed(n) = attr - && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } = - n.as_ref() - && let ast::LitKind::Str(name, ..) = lit.kind - { - // Discard the double quotes surrounding the literal. - let sp = cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .ok() - .and_then(|snippet| { - let left = snippet.find('"')?; - let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; - - Some( - lit.span - .with_lo(lit.span.lo() + BytePos(left as u32 + 1)) - .with_hi(lit.span.hi() - BytePos(right as u32)), - ) - }) - .unwrap_or(lit.span); - - Some(Ident::new(name, sp)) - } else { - None - } + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::CrateName{name, name_span,..} => (name, name_span)).map( + |(&name, &span)| { + // Discard the double quotes surrounding the literal. + let sp = cx + .sess() + .source_map() + .span_to_snippet(span) + .ok() + .and_then(|snippet| { + let left = snippet.find('"')?; + let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; + + Some( + span + .with_lo(span.lo() + BytePos(left as u32 + 1)) + .with_hi(span.hi() - BytePos(right as u32)), + ) + }) + .unwrap_or(span); + + Ident::new(name, sp) }, ) }; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c610ce4fc85..7aef60b7b91 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -250,7 +250,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ShouldPanic { .. } | AttributeKind::Coroutine(..) | AttributeKind::Linkage(..) - | AttributeKind::MustUse { .. }, + | AttributeKind::MustUse { .. } + | AttributeKind::CrateName { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -367,22 +368,49 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); if hir_id != CRATE_HIR_ID { - if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = - attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) - { - match style { - Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint( + match attr { + // FIXME(jdonszelmann) move to attribute parsing when validation gets better there + &Attribute::Parsed(AttributeKind::CrateName { + attr_span: span, style, .. + }) => match style { + ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + span, errors::OuterCrateLevelAttr, ), - Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + span, errors::InnerCrateLevelAttr, ), + }, + Attribute::Parsed(_) => { /* not crate-level */ } + Attribute::Unparsed(attr) => { + // FIXME(jdonszelmann): remove once all crate-level attrs are parsed and caught by + // the above + if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = + attr.path + .segments + .first() + .and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) + { + match attr.style { + ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr, + ), + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + } + } } } } diff --git a/tests/ui/attributes/crate-name-empty.stderr b/tests/ui/attributes/crate-name-empty.stderr index 509a42d05f7..31d9dd0e948 100644 --- a/tests/ui/attributes/crate-name-empty.stderr +++ b/tests/ui/attributes/crate-name-empty.stderr @@ -1,8 +1,8 @@ error: crate name must not be empty - --> $DIR/crate-name-empty.rs:3:1 + --> $DIR/crate-name-empty.rs:3:17 | LL | #![crate_name = ""] - | ^^^^^^^^^^^^^^^^^^^ + | ^^ error: aborting due to 1 previous error diff --git a/tests/ui/attributes/crate-name-macro-call.rs b/tests/ui/attributes/crate-name-macro-call.rs index 1aae2e506e2..61076349cc3 100644 --- a/tests/ui/attributes/crate-name-macro-call.rs +++ b/tests/ui/attributes/crate-name-macro-call.rs @@ -1,6 +1,6 @@ // issue: rust-lang/rust#122001 // Ensure we reject macro calls inside `#![crate_name]` as their result wouldn't get honored anyway. -#![crate_name = concat!("my", "crate")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("my", "crate")] //~ ERROR attribute value must be a literal fn main() {} diff --git a/tests/ui/attributes/crate-name-macro-call.stderr b/tests/ui/attributes/crate-name-macro-call.stderr index 56827aa11a4..e988b8461d7 100644 --- a/tests/ui/attributes/crate-name-macro-call.stderr +++ b/tests/ui/attributes/crate-name-macro-call.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/crate-name-macro-call.rs:4:1 +error: attribute value must be a literal + --> $DIR/crate-name-macro-call.rs:4:17 | LL | #![crate_name = concat!("my", "crate")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/attributes/crate-name-mismatch.stderr b/tests/ui/attributes/crate-name-mismatch.stderr index 4021fbe7c18..6416ad2d41a 100644 --- a/tests/ui/attributes/crate-name-mismatch.stderr +++ b/tests/ui/attributes/crate-name-mismatch.stderr @@ -1,8 +1,8 @@ error: `--crate-name` and `#[crate_name]` are required to match, but `foo` != `bar` - --> $DIR/crate-name-mismatch.rs:5:1 + --> $DIR/crate-name-mismatch.rs:5:17 | LL | #![crate_name = "bar"] - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 11e01ac29be..69f9a18c179 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -41,14 +41,6 @@ LL | #![windows_subsystem = "console"] LL | #![windows_subsystem = "windows"] | +++++++++++ -error: malformed `crate_name` attribute input - --> $DIR/malformed-attrs.rs:74:1 - | -LL | #[crate_name] - | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` - | - = note: for more information, visit - error: malformed `instruction_set` attribute input --> $DIR/malformed-attrs.rs:106:1 | @@ -478,6 +470,12 @@ LL | #[used()] | = help: `#[used]` can only be applied to statics +error[E0539]: malformed `crate_name` attribute input + --> $DIR/malformed-attrs.rs:74:1 + | +LL | #[crate_name] + | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` + error[E0539]: malformed `target_feature` attribute input --> $DIR/malformed-attrs.rs:79:1 | diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs index c7f3c2c5403..62f1f6cd09c 100644 --- a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs +++ b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs @@ -2,4 +2,4 @@ // See also . //@ compile-flags: --print=crate-name -#![crate_name = concat!("wrapped")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("wrapped")] //~ ERROR attribute value must be a literal diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr index b773f7c97aa..fab38dae78b 100644 --- a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr +++ b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/print-crate-name-request-malformed-crate-name.rs:5:1 +error: attribute value must be a literal + --> $DIR/print-crate-name-request-malformed-crate-name.rs:5:17 | LL | #![crate_name = concat!("wrapped")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr index 64e38ed8533..d3e60948e4c 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr @@ -1,10 +1,9 @@ -error: malformed `crate_name` attribute input +error[E0539]: malformed `crate_name` attribute input --> $DIR/print-file-names-request-malformed-crate-name-1.rs:4:1 | LL | #![crate_name] | ^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs index 13c9d1e0027..1ac1208ee44 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs @@ -4,4 +4,4 @@ //@ compile-flags: --print=file-names #![crate_name = "this_one_is_okay"] -#![crate_name = concat!("this_one_is_not")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("this_one_is_not")] //~ ERROR attribute value must be a literal diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr index e9a5b58e4f9..1571521ff17 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/print-file-names-request-malformed-crate-name-2.rs:7:1 +error: attribute value must be a literal + --> $DIR/print-file-names-request-malformed-crate-name-2.rs:7:17 | LL | #![crate_name = concat!("this_one_is_not")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs index 5fe8bd7945f..6441017c665 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs @@ -2,4 +2,4 @@ // See also . //@ compile-flags: --print=file-names -#![crate_name = concat!("wrapped")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("wrapped")] //~ ERROR attribute value must be a literal diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr index e63525c6a5d..155e2e5a3fa 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/print-file-names-request-malformed-crate-name.rs:5:1 +error: attribute value must be a literal + --> $DIR/print-file-names-request-malformed-crate-name.rs:5:17 | LL | #![crate_name = concat!("wrapped")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused/unused-attr-duplicate.rs b/tests/ui/lint/unused/unused-attr-duplicate.rs index cfa6c2b4228..a334c49788c 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.rs +++ b/tests/ui/lint/unused/unused-attr-duplicate.rs @@ -11,8 +11,12 @@ // - no_main: extra setup #![deny(unused_attributes)] #![crate_name = "unused_attr_duplicate"] -#![crate_name = "unused_attr_duplicate2"] //~ ERROR unused attribute -//~^ WARN this was previously accepted +#![crate_name = "unused_attr_duplicate2"] +//~^ ERROR unused attribute +//~| WARN this was previously accepted +//~| ERROR unused attribute +//~| WARN this was previously accepted +// FIXME(jdonszelmann) this error is given twice now. I'll look at this in the future #![recursion_limit = "128"] #![recursion_limit = "256"] //~ ERROR unused attribute //~^ WARN this was previously accepted diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 6c44e884ba5..203211d0d56 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -1,14 +1,15 @@ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:33:1 + --> $DIR/unused-attr-duplicate.rs:14:1 | -LL | #[no_link] - | ^^^^^^^^^^ help: remove this attribute +LL | #![crate_name = "unused_attr_duplicate2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:32:1 + --> $DIR/unused-attr-duplicate.rs:13:1 | -LL | #[no_link] - | ^^^^^^^^^^ +LL | #![crate_name = "unused_attr_duplicate"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! note: the lint level is defined here --> $DIR/unused-attr-duplicate.rs:12:9 | @@ -16,291 +17,304 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:14:1 + --> $DIR/unused-attr-duplicate.rs:37:1 | -LL | #![crate_name = "unused_attr_duplicate2"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute +LL | #[no_link] + | ^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:13:1 + --> $DIR/unused-attr-duplicate.rs:36:1 | -LL | #![crate_name = "unused_attr_duplicate"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +LL | #[no_link] + | ^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:17:1 + --> $DIR/unused-attr-duplicate.rs:21:1 | LL | #![recursion_limit = "256"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:16:1 + --> $DIR/unused-attr-duplicate.rs:20:1 | LL | #![recursion_limit = "128"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:20:1 + --> $DIR/unused-attr-duplicate.rs:24:1 | LL | #![type_length_limit = "1"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:19:1 + --> $DIR/unused-attr-duplicate.rs:23:1 | LL | #![type_length_limit = "1048576"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:23:1 + --> $DIR/unused-attr-duplicate.rs:27:1 | LL | #![no_std] | ^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:22:1 + --> $DIR/unused-attr-duplicate.rs:26:1 | LL | #![no_std] | ^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:27:1 + --> $DIR/unused-attr-duplicate.rs:31:1 | LL | #![windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:26:1 + --> $DIR/unused-attr-duplicate.rs:30:1 | LL | #![windows_subsystem = "console"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:30:1 + --> $DIR/unused-attr-duplicate.rs:34:1 | LL | #![no_builtins] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:29:1 + --> $DIR/unused-attr-duplicate.rs:33:1 | LL | #![no_builtins] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:40:5 + --> $DIR/unused-attr-duplicate.rs:44:5 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:39:5 + --> $DIR/unused-attr-duplicate.rs:43:5 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:37:1 + --> $DIR/unused-attr-duplicate.rs:41:1 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:36:1 + --> $DIR/unused-attr-duplicate.rs:40:1 | LL | #[macro_use] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:47:1 + --> $DIR/unused-attr-duplicate.rs:51:1 | LL | #[path = "bar.rs"] | ^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:46:1 + --> $DIR/unused-attr-duplicate.rs:50:1 | LL | #[path = "auxiliary/lint_unused_extern_crate.rs"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:53:1 + --> $DIR/unused-attr-duplicate.rs:57:1 | LL | #[ignore = "some text"] | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:52:1 + --> $DIR/unused-attr-duplicate.rs:56:1 | LL | #[ignore] | ^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:55:1 + --> $DIR/unused-attr-duplicate.rs:59:1 | LL | #[should_panic(expected = "values don't match")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:54:1 + --> $DIR/unused-attr-duplicate.rs:58:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:60:1 + --> $DIR/unused-attr-duplicate.rs:64:1 | LL | #[must_use = "some message"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:59:1 + --> $DIR/unused-attr-duplicate.rs:63:1 | LL | #[must_use] | ^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:66:1 + --> $DIR/unused-attr-duplicate.rs:70:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:65:1 + --> $DIR/unused-attr-duplicate.rs:69:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:72:1 + --> $DIR/unused-attr-duplicate.rs:76:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:71:1 + --> $DIR/unused-attr-duplicate.rs:75:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:76:1 + --> $DIR/unused-attr-duplicate.rs:80:1 | LL | #[inline(never)] | ^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:75:1 + --> $DIR/unused-attr-duplicate.rs:79:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:79:1 + --> $DIR/unused-attr-duplicate.rs:83:1 | LL | #[cold] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:78:1 + --> $DIR/unused-attr-duplicate.rs:82:1 | LL | #[cold] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:81:1 + --> $DIR/unused-attr-duplicate.rs:85:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:80:1 + --> $DIR/unused-attr-duplicate.rs:84:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:88:5 + --> $DIR/unused-attr-duplicate.rs:92:5 | LL | #[link_name = "this_does_not_exist"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:90:5 + --> $DIR/unused-attr-duplicate.rs:94:5 | LL | #[link_name = "rust_dbg_extern_identity_u32"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:94:1 + --> $DIR/unused-attr-duplicate.rs:98:1 | LL | #[export_name = "exported_symbol_name"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:96:1 + --> $DIR/unused-attr-duplicate.rs:100:1 | LL | #[export_name = "exported_symbol_name2"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:100:1 + --> $DIR/unused-attr-duplicate.rs:104:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:99:1 + --> $DIR/unused-attr-duplicate.rs:103:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:104:1 + --> $DIR/unused-attr-duplicate.rs:108:1 | LL | #[used] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:103:1 + --> $DIR/unused-attr-duplicate.rs:107:1 | LL | #[used] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:107:1 + --> $DIR/unused-attr-duplicate.rs:111:1 | LL | #[link_section = ".text"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:110:1 + --> $DIR/unused-attr-duplicate.rs:114:1 | LL | #[link_section = ".bss"] | ^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:25:1 + --> $DIR/unused-attr-duplicate.rs:14:1 + | +LL | #![crate_name = "unused_attr_duplicate2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:13:1 + | +LL | #![crate_name = "unused_attr_duplicate"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:29:1 | LL | #![no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:24:1 + --> $DIR/unused-attr-duplicate.rs:28:1 | LL | #![no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From 48a4e2d2dde8d68e1d00d3eac07b2c6155f3239d Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Sat, 23 Aug 2025 19:42:14 +0200 Subject: fix ICE on stable related to attrs on macros --- compiler/rustc_attr_parsing/src/context.rs | 21 ++------------------ compiler/rustc_attr_parsing/src/parser.rs | 20 +++++++++---------- .../concat-in-crate-deprecated-issue-137687.rs | 6 ++++++ .../unused/concat-in-crate-name-issue-137687.rs | 9 +++++++++ .../concat-in-crate-name-issue-137687.stderr | 23 ++++++++++++++++++++++ tests/ui/resolve/path-attr-in-const-block.rs | 1 + tests/ui/resolve/path-attr-in-const-block.stderr | 8 +++++++- 7 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 tests/ui/lint/unused/concat-in-crate-deprecated-issue-137687.rs create mode 100644 tests/ui/lint/unused/concat-in-crate-name-issue-137687.rs create mode 100644 tests/ui/lint/unused/concat-in-crate-name-issue-137687.stderr diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 128ecd67d2a..d4b9cfe00ad 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -265,7 +265,7 @@ impl Stage for Early { sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { - self.should_emit().emit_err_or_delay(sess.dcx().create_err(diag)) + self.should_emit().emit_err(sess.dcx().create_err(diag)) } fn should_emit(&self) -> ShouldEmit { @@ -667,7 +667,7 @@ pub enum ShouldEmit { } impl ShouldEmit { - pub(crate) fn emit_err_or_delay(&self, diag: Diag<'_>) -> ErrorGuaranteed { + pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed { match self { ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(), ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(), @@ -675,21 +675,4 @@ impl ShouldEmit { ShouldEmit::Nothing => diag.delay_as_bug(), } } - - pub(crate) fn maybe_emit_err(&self, diag: Diag<'_>) { - match self { - ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => { - diag.emit(); - } - ShouldEmit::EarlyFatal => { - diag.upgrade_to_fatal().emit(); - } - ShouldEmit::ErrorsAndLints => { - diag.emit(); - } - ShouldEmit::Nothing => { - diag.cancel(); - } - } - } } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 3ba188938c6..6d3cf684296 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -328,14 +328,14 @@ fn expr_to_lit( match res { Ok(lit) => { if token_lit.suffix.is_some() { - should_emit.emit_err_or_delay( + should_emit.emit_err( psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), ); None } else { if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there - should_emit.maybe_emit_err( + should_emit.emit_err( psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), ); } @@ -355,6 +355,10 @@ fn expr_to_lit( } } } else { + if matches!(should_emit, ShouldEmit::Nothing) { + return None; + } + // Example cases: // - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`. // - `#[foo = include_str!("nonexistent-file.rs")]`: @@ -362,12 +366,8 @@ fn expr_to_lit( // the error because an earlier error will have already // been reported. let msg = "attribute value must be a literal"; - let mut err = psess.dcx().struct_span_err(span, msg); - if let ExprKind::Err(_) = expr.kind { - err.downgrade_to_delayed_bug(); - } - - should_emit.emit_err_or_delay(err); + let err = psess.dcx().struct_span_err(span, msg); + should_emit.emit_err(err); None } } @@ -400,7 +400,7 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there - self.should_emit.maybe_emit_err( + self.should_emit.emit_err( self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), ); } @@ -542,7 +542,7 @@ impl<'a> MetaItemListParser<'a> { ) { Ok(s) => Some(s), Err(e) => { - should_emit.emit_err_or_delay(e); + should_emit.emit_err(e); None } } diff --git a/tests/ui/lint/unused/concat-in-crate-deprecated-issue-137687.rs b/tests/ui/lint/unused/concat-in-crate-deprecated-issue-137687.rs new file mode 100644 index 00000000000..22dd55f4421 --- /dev/null +++ b/tests/ui/lint/unused/concat-in-crate-deprecated-issue-137687.rs @@ -0,0 +1,6 @@ +//@ check-pass +#[deprecated = concat !()] +macro_rules! a { + () => {}; +} +fn main() {} diff --git a/tests/ui/lint/unused/concat-in-crate-name-issue-137687.rs b/tests/ui/lint/unused/concat-in-crate-name-issue-137687.rs new file mode 100644 index 00000000000..37fbf93ffa1 --- /dev/null +++ b/tests/ui/lint/unused/concat-in-crate-name-issue-137687.rs @@ -0,0 +1,9 @@ +#![deny(unused)] + +#[crate_name = concat !()] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo] +macro_rules! a { + //~^ ERROR unused macro definition + () => {}; +} +fn main() {} diff --git a/tests/ui/lint/unused/concat-in-crate-name-issue-137687.stderr b/tests/ui/lint/unused/concat-in-crate-name-issue-137687.stderr new file mode 100644 index 00000000000..4ffb55d493a --- /dev/null +++ b/tests/ui/lint/unused/concat-in-crate-name-issue-137687.stderr @@ -0,0 +1,23 @@ +error: unused macro definition: `a` + --> $DIR/concat-in-crate-name-issue-137687.rs:5:14 + | +LL | macro_rules! a { + | ^ + | +note: the lint level is defined here + --> $DIR/concat-in-crate-name-issue-137687.rs:1:9 + | +LL | #![deny(unused)] + | ^^^^^^ + = note: `#[deny(unused_macros)]` implied by `#[deny(unused)]` + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` + --> $DIR/concat-in-crate-name-issue-137687.rs:3:1 + | +LL | #[crate_name = concat !()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/resolve/path-attr-in-const-block.rs b/tests/ui/resolve/path-attr-in-const-block.rs index 076511d26d6..0ca356b1ddf 100644 --- a/tests/ui/resolve/path-attr-in-const-block.rs +++ b/tests/ui/resolve/path-attr-in-const-block.rs @@ -5,5 +5,6 @@ fn main() { const { #![path = foo!()] //~^ ERROR: cannot find macro `foo` in this scope + //~| ERROR: attribute value must be a literal } } diff --git a/tests/ui/resolve/path-attr-in-const-block.stderr b/tests/ui/resolve/path-attr-in-const-block.stderr index 8f9e58157c8..19d2745577b 100644 --- a/tests/ui/resolve/path-attr-in-const-block.stderr +++ b/tests/ui/resolve/path-attr-in-const-block.stderr @@ -4,5 +4,11 @@ error: cannot find macro `foo` in this scope LL | #![path = foo!()] | ^^^ -error: aborting due to 1 previous error +error: attribute value must be a literal + --> $DIR/path-attr-in-const-block.rs:6:19 + | +LL | #![path = foo!()] + | ^^^^^^ + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From b8313dfb04a12b45483f2a8a7527d8ba1c63ae03 Mon Sep 17 00:00:00 2001 From: Karol Zwolak Date: Sat, 23 Aug 2025 11:05:05 +0200 Subject: rustdoc: render attributes in Field and Variants sections --- src/librustdoc/html/render/print_item.rs | 24 +++++++++++++++++++++--- src/librustdoc/html/templates/item_union.html | 2 +- tests/rustdoc/attributes.rs | 4 ++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 2618ec272ca..b86a2c94697 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1460,6 +1460,13 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { ) } + fn print_field_attrs(&self, field: &'a clean::Item) -> impl Display { + fmt::from_fn(move |w| { + render_attributes_in_code(w, field, "", self.cx); + Ok(()) + }) + } + fn document_field(&self, field: &'a clean::Item) -> impl Display { document(self.cx, field, Some(self.it), HeadingOffset::H3) } @@ -1770,6 +1777,7 @@ fn item_variants( ) .maybe_display() )?; + render_attributes_in_code(w, variant, "", cx); if let clean::VariantItem(ref var) = variant.kind && let clean::VariantKind::CLike = var.kind { @@ -1843,7 +1851,12 @@ fn item_variants( "
\ \ §\ - {f}: {t}\ + " + )?; + render_attributes_in_code(w, field, "", cx); + write!( + w, + "{f}: {t}\ \ {doc}\
", @@ -2079,10 +2092,15 @@ fn item_fields( w, "\ §\ - {field_name}: {ty}\ + ", + item_type = ItemType::StructField, + )?; + render_attributes_in_code(w, field, "", cx); + write!( + w, + "{field_name}: {ty}\ \ {doc}", - item_type = ItemType::StructField, ty = ty.print(cx), doc = document(cx, field, Some(it), HeadingOffset::H3), )?; diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html index 5dba43ca255..171e079ed13 100644 --- a/src/librustdoc/html/templates/item_union.html +++ b/src/librustdoc/html/templates/item_union.html @@ -12,7 +12,7 @@ {% let name = field.name.expect("union field name") %} {# #} § {# #} - {{ name }}: {{+ self.print_ty(ty)|safe }} {# #} + {{+ self.print_field_attrs(field)|safe }}{{ name }}: {{+ self.print_ty(ty)|safe }} {# #} {% if let Some(stability_class) = self.stability_field(field) %} diff --git a/tests/rustdoc/attributes.rs b/tests/rustdoc/attributes.rs index db5f89ed46e..33e4e31bec6 100644 --- a/tests/rustdoc/attributes.rs +++ b/tests/rustdoc/attributes.rs @@ -28,12 +28,15 @@ macro_rules! macro_rule { #[unsafe(link_section = "enum")] pub enum Enum { //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "a")]' + //@ has - '//*[@class="variants"]//*[@class="code-attribute"]' '#[unsafe(link_section = "a")]' #[unsafe(link_section = "a")] A, //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "quz")]' + //@ has - '//*[@class="variants"]//*[@class="code-attribute"]' '#[unsafe(link_section = "quz")]' #[unsafe(link_section = "quz")] Quz { //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "b")]' + //@ has - '//*[@class="variants"]//*[@class="code-attribute"]' '#[unsafe(link_section = "b")]' #[unsafe(link_section = "b")] b: (), }, @@ -66,6 +69,7 @@ pub union Union { #[unsafe(link_section = "struct")] pub struct Struct { //@ has 'foo/struct.Struct.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "x")]' + //@ has - '//*[@id="structfield.x"]//*[@class="code-attribute"]' '#[unsafe(link_section = "x")]' #[unsafe(link_section = "x")] pub x: u32, y: f32, -- cgit 1.4.1-3-g733a5 From c2b9a8afa9674f473ac78bc0fad76b18c78b7667 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 11 Aug 2025 16:57:40 +0000 Subject: Make sure to treat only param where clauses as inherent --- compiler/rustc_hir_typeck/src/method/probe.rs | 27 +++++++++++++ .../methods/rigid-alias-bound-is-not-inherent-2.rs | 17 ++++++++ .../rigid-alias-bound-is-not-inherent-2.stderr | 17 ++++++++ .../methods/rigid-alias-bound-is-not-inherent-3.rs | 26 ++++++++++++ .../rigid-alias-bound-is-not-inherent-3.stderr | 30 ++++++++++++++ ...igid-alias-bound-is-not-inherent.current.stderr | 30 ++++++++++++++ .../rigid-alias-bound-is-not-inherent.next.stderr | 30 ++++++++++++++ .../methods/rigid-alias-bound-is-not-inherent.rs | 46 ++++++++++++++++++++++ 8 files changed, 223 insertions(+) create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent.rs diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3ca3b56b87e..f5c3ebd8375 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -909,6 +909,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug_assert_matches!(param_ty.kind(), ty::Param(_)); let tcx = self.tcx; + + // We use `DeepRejectCtxt` here which may return false positive on where clauses + // with alias self types. We need to later on reject these as inherent candidates + // in `consider_probe`. let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { @@ -1945,6 +1949,29 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ); (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); + + if matches!(probe.kind, WhereClauseCandidate(_)) { + // `WhereClauseCandidate` requires that the self type is a param, + // because it has special behavior with candidate preference as an + // inherent pick. + match ocx.structurally_normalize_ty( + cause, + self.param_env, + trait_ref.self_ty(), + ) { + Ok(ty) => { + if !matches!(ty.kind(), ty::Param(_)) { + debug!("--> not a param ty: {xform_self_ty:?}"); + return ProbeResult::NoMatch; + } + } + Err(errors) => { + debug!("--> cannot relate self-types {:?}", errors); + return ProbeResult::NoMatch; + } + } + } + xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); match ocx.relate(cause, self.param_env, self.variance(), self_ty, xform_self_ty) { diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs new file mode 100644 index 00000000000..8363ec1b3fb --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs @@ -0,0 +1,17 @@ +// Regression test for . + +mod module { + pub trait Trait { + fn method(&self); + } +} + +// Note that we do not import Trait +use std::ops::Deref; + +fn foo(x: impl Deref) { + x.method(); + //~^ ERROR no method named `method` found for type parameter +} + +fn main() {} diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr new file mode 100644 index 00000000000..433cab9cf9e --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr @@ -0,0 +1,17 @@ +error[E0599]: no method named `method` found for type parameter `impl Deref` in the current scope + --> $DIR/rigid-alias-bound-is-not-inherent-2.rs:13:7 + | +LL | fn foo(x: impl Deref) { + | --------------------------------- method `method` not found for this type parameter +LL | x.method(); + | ^^^^^^ method not found in `impl Deref` + | + = help: items from traits can only be used if the trait is in scope +help: trait `Trait` which provides `method` is implemented but not in scope; perhaps you want to import it + | +LL + use module::Trait; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs new file mode 100644 index 00000000000..bb316eed34d --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs @@ -0,0 +1,26 @@ +use std::ops::Deref; + +trait Trait1 { + fn call_me(&self) {} +} + +impl Trait1 for T {} + +trait Trait2 { + fn call_me(&self) {} +} + +impl Trait2 for T {} + +pub fn foo(x: T) +where + T: Deref, + U: Trait1, +{ + // This should be ambiguous. The fact that there's an inherent where-bound + // candidate for `U` should not impact the candidates for `T` + x.call_me(); + //~^ ERROR multiple applicable items in scope +} + +fn main() {} diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr new file mode 100644 index 00000000000..466ad4d2abc --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr @@ -0,0 +1,30 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/rigid-alias-bound-is-not-inherent-3.rs:22:7 + | +LL | x.call_me(); + | ^^^^^^^ multiple `call_me` found + | +note: candidate #1 is defined in an impl of the trait `Trait1` for the type `T` + --> $DIR/rigid-alias-bound-is-not-inherent-3.rs:4:5 + | +LL | fn call_me(&self) {} + | ^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `Trait2` for the type `T` + --> $DIR/rigid-alias-bound-is-not-inherent-3.rs:10:5 + | +LL | fn call_me(&self) {} + | ^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - x.call_me(); +LL + Trait1::call_me(&x); + | +help: disambiguate the method for candidate #2 + | +LL - x.call_me(); +LL + Trait2::call_me(&x); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr new file mode 100644 index 00000000000..4652bf5e3c5 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr @@ -0,0 +1,30 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/rigid-alias-bound-is-not-inherent.rs:42:7 + | +LL | x.method(); + | ^^^^^^ multiple `method` found + | +note: candidate #1 is defined in the trait `Trait1` + --> $DIR/rigid-alias-bound-is-not-inherent.rs:21:5 + | +LL | fn method(&self) { + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `Trait2` for the type `T` + --> $DIR/rigid-alias-bound-is-not-inherent.rs:27:5 + | +LL | fn method(&self) { + | ^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - x.method(); +LL + Trait1::method(&x); + | +help: disambiguate the method for candidate #2 + | +LL - x.method(); +LL + Trait2::method(&x); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr new file mode 100644 index 00000000000..afacb3a7d52 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr @@ -0,0 +1,30 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/rigid-alias-bound-is-not-inherent.rs:42:7 + | +LL | x.method(); + | ^^^^^^ multiple `method` found + | +note: candidate #1 is defined in the trait `Trait1` + --> $DIR/rigid-alias-bound-is-not-inherent.rs:21:5 + | +LL | fn method(&self) { + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `Trait2` + --> $DIR/rigid-alias-bound-is-not-inherent.rs:27:5 + | +LL | fn method(&self) { + | ^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - x.method(); +LL + Trait1::method(&x); + | +help: disambiguate the method for candidate #2 + | +LL - x.method(); +LL + Trait2::method(&x); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.rs b/tests/ui/methods/rigid-alias-bound-is-not-inherent.rs new file mode 100644 index 00000000000..3dd63df3f39 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.rs @@ -0,0 +1,46 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// See the code below. +// +// We were using `DeepRejectCtxt` to ensure that `assemble_inherent_candidates_from_param` +// did not rely on the param-env being eagerly normalized. Since aliases unify with all +// types, this meant that a rigid param-env candidate like `::Target: Trait1` +// would be registered as a "WhereClauseCandidate", which is treated as inherent. Since +// we evaluate these candidates for all self types in the deref chain, this candidate +// would be satisfied for `::Target`, meaning that it would be preferred over +// an "extension" candidate like `::Target: Trait2` even though it holds. +// This is problematic, since it causes ambiguities to be broken somewhat arbitrarily. +// And as a side-effect, it also caused our computation of "used" traits to be miscalculated +// since inherent candidates don't count as an import usage. + +use std::ops::Deref; + +trait Trait1 { + fn method(&self) { + println!("1"); + } +} + +trait Trait2 { + fn method(&self) { + println!("2"); + } +} +impl Trait2 for T {} + +trait Other {} + +fn foo(x: T) +where + T: Deref, + ::Target: Trait1 + Other, +{ + // Make sure that we don't prefer methods from where clauses for rigid aliases, + // just for params. We could revisit this behavior, but it would be a lang change. + x.method(); + //~^ ERROR multiple applicable items in scope +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 22ef90c8049cf8f07ce42e09f01ca791b829594e Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 25 Aug 2025 04:55:35 +0000 Subject: Prepare for merging from rust-lang/rust This updates the rust-version file to a1dbb443527bd126452875eb5d5860c1d001d761. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3450f18334a..f412399cc8c 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f6d23413c399fb530be362ebcf25a4e788e16137 +a1dbb443527bd126452875eb5d5860c1d001d761 -- cgit 1.4.1-3-g733a5 From baff99c556a22c962c15cfb9f79a0dcb1d467b20 Mon Sep 17 00:00:00 2001 From: Ryan Lee <138088339+minux-lee@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:49:10 +0900 Subject: Remove bug comments from Repeat variant Removed comments related to a bug in Repeat variant. --- compiler/rustc_middle/src/mir/syntax.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 6039a03aa29..3b8def67f92 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1371,12 +1371,7 @@ pub enum Rvalue<'tcx> { /// Creates an array where each element is the value of the operand. /// - /// This is the cause of a bug in the case where the repetition count is zero because the value - /// is not dropped, see [#74836]. - /// /// Corresponds to source code like `[x; 32]`. - /// - /// [#74836]: https://github.com/rust-lang/rust/issues/74836 Repeat(Operand<'tcx>, ty::Const<'tcx>), /// Creates a reference of the indicated kind to the place. -- cgit 1.4.1-3-g733a5