From a9a979839bbdfec48c75d618ab0dce8a953589b8 Mon Sep 17 00:00:00 2001 From: Sundeep KOKKONDA <56015745+sundeep-kokkonda@users.noreply.github.com> Date: Mon, 4 Mar 2024 08:18:54 +0530 Subject: [PATCH 01/12] Removing absolute path in proc-macro With rust 1.75 the absolute build path is embedding into '.rustc' section and which causes reproducibility issues. Detailed issue is here. https://github.com/rust-lang/rust/issues/120825#issuecomment-1964307219 With this change the 'absolute path' changed back to '/rust/$hash' format. --- compiler/rustc_session/src/session.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 09fb6aa5d8f98..6a76d78b33f3d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -898,19 +898,6 @@ impl Session { } pub fn should_prefer_remapped_for_codegen(&self) -> bool { - // bail out, if any of the requested crate types aren't: - // "compiled executables or libraries" - for crate_type in &self.opts.crate_types { - match crate_type { - CrateType::Executable - | CrateType::Dylib - | CrateType::Rlib - | CrateType::Staticlib - | CrateType::Cdylib => continue, - CrateType::ProcMacro => return false, - } - } - let has_split_debuginfo = match self.split_debuginfo() { SplitDebuginfo::Off => false, SplitDebuginfo::Packed => true, From 678e2177dcc089bb5355819d11ee22d7703126d4 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 9 Jul 2023 21:42:23 -0500 Subject: [PATCH 02/12] bootstrap: print test name on failure even with `verbose-tests=false` This makes it much easier to know which test failed without having to wait for compiletest to completely finish running. Before: ``` Testing stage0 compiletest suite=ui mode=ui (x86_64-unknown-linux-gnu) running 15274 tests iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 88/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 176/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 264/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 352/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 440/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 528/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiFFiiiiiii ... ``` After: ``` Testing stage0 compiletest suite=ui mode=ui (x86_64-unknown-linux-gnu) running 15274 tests iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 88/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 176/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 264/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 352/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 440/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii 528/15274 iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii [ui] tests/ui/associated-type-bounds/implied-in-supertrait.rs ... F [ui] tests/ui/associated-type-bounds/return-type-notation/basic.rs#next_with ... F iiiiiiiiiiiii ... ``` This serves a similar use case to the existing RUSTC_TEST_FAIL_FAST, but is on by default and as a result much more discoverable. We should consider unifying RUSTC_TEST_FAIL_FAST with the `--no-fail-fast` flag in the future for consistency and discoverability. --- src/bootstrap/src/utils/render_tests.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index bfbb53f8c8121..cbd01606a895a 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -166,7 +166,7 @@ impl<'a> Renderer<'a> { println!(); } - fn render_test_outcome_terse(&mut self, outcome: Outcome<'_>, _: &TestOutcome) { + fn render_test_outcome_terse(&mut self, outcome: Outcome<'_>, test: &TestOutcome) { if self.terse_tests_in_line != 0 && self.terse_tests_in_line % TERSE_TESTS_PER_LINE == 0 { if let Some(total) = self.tests_count { let total = total.to_string(); @@ -178,7 +178,7 @@ impl<'a> Renderer<'a> { } self.terse_tests_in_line += 1; - self.builder.colored_stdout(|stdout| outcome.write_short(stdout)).unwrap(); + self.builder.colored_stdout(|stdout| outcome.write_short(stdout, &test.name)).unwrap(); let _ = std::io::stdout().flush(); } @@ -300,7 +300,7 @@ enum Outcome<'a> { } impl Outcome<'_> { - fn write_short(&self, writer: &mut dyn WriteColor) -> Result<(), std::io::Error> { + fn write_short(&self, writer: &mut dyn WriteColor, name: &str) -> Result<(), std::io::Error> { match self { Outcome::Ok => { writer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; @@ -311,8 +311,11 @@ impl Outcome<'_> { write!(writer, "b")?; } Outcome::Failed => { + // Put failed tests on their own line and include the test name, so that it's faster + // to see which test failed without having to wait for them all to run. + writeln!(writer)?; writer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; - write!(writer, "F")?; + writeln!(writer, "{name} ... F")?; } Outcome::Ignored { .. } => { writer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?; From 8bfe9dbae20628bfcea390556c40a991984692ec Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 4 Nov 2023 21:40:38 -0400 Subject: [PATCH 03/12] libtest: Print the names of failed tests eagerly Previously, libtest would wait until all tests finished running to print the progress, which made it annoying to run many tests at once (since you don't know which have failed). Change it to print the names as soon as they fail. This also adds a test for the terse output; previously it was untested. --- library/test/src/formatters/terse.rs | 35 ++++++-- tests/ui/test-attrs/terse.rs | 125 +++++++++++++++++++++++++++ tests/ui/test-attrs/terse.run.stdout | 31 +++++++ 3 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 tests/ui/test-attrs/terse.rs create mode 100644 tests/ui/test-attrs/terse.run.stdout diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 2931ca6ead0ac..22c28e4954eff 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -23,6 +23,7 @@ pub(crate) struct TerseFormatter { max_name_len: usize, test_count: usize, + test_column: usize, total_test_count: usize, } @@ -39,6 +40,7 @@ impl TerseFormatter { max_name_len, is_multithreaded, test_count: 0, + test_column: 0, total_test_count: 0, // initialized later, when write_run_start is called } } @@ -47,8 +49,20 @@ impl TerseFormatter { self.write_short_result(".", term::color::GREEN) } - pub fn write_failed(&mut self) -> io::Result<()> { - self.write_short_result("F", term::color::RED) + pub fn write_failed(&mut self, name: &str) -> io::Result<()> { + // Put failed tests on their own line and include the test name, so that it's faster + // to see which test failed without having to wait for them all to run. + + // normally, we write the progress unconditionally, even if the previous line was cut short. + // but if this is the very first column, no short results will have been printed and we'll end up with *only* the progress on the line. + // avoid this. + if self.test_column != 0 { + self.write_progress()?; + } + self.test_count += 1; + self.write_plain(format!("{name} --- "))?; + self.write_pretty("FAILED", term::color::RED)?; + self.write_plain("\n") } pub fn write_ignored(&mut self) -> io::Result<()> { @@ -65,15 +79,22 @@ impl TerseFormatter { color: term::color::Color, ) -> io::Result<()> { self.write_pretty(result, color)?; - if self.test_count % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 { + self.test_count += 1; + self.test_column += 1; + if self.test_column % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 { // We insert a new line regularly in order to flush the // screen when dealing with line-buffered output (e.g., piping to // `stamp` in the rust CI). - let out = format!(" {}/{}\n", self.test_count + 1, self.total_test_count); - self.write_plain(out)?; + self.write_progress()?; } - self.test_count += 1; + Ok(()) + } + + fn write_progress(&mut self) -> io::Result<()> { + let out = format!(" {}/{}\n", self.test_count, self.total_test_count); + self.write_plain(out)?; + self.test_column = 0; Ok(()) } @@ -213,7 +234,7 @@ impl OutputFormatter for TerseFormatter { match *result { TestResult::TrOk => self.write_ok(), TestResult::TrFailed | TestResult::TrFailedMsg(_) | TestResult::TrTimedFail => { - self.write_failed() + self.write_failed(desc.name.as_slice()) } TestResult::TrIgnored => self.write_ignored(), TestResult::TrBench(ref bs) => { diff --git a/tests/ui/test-attrs/terse.rs b/tests/ui/test-attrs/terse.rs new file mode 100644 index 0000000000000..ab9d5cc19bd6c --- /dev/null +++ b/tests/ui/test-attrs/terse.rs @@ -0,0 +1,125 @@ +//@ compile-flags: --test +//@ run-fail +//@ run-flags: --test-threads=1 --quiet +//@ check-run-results +//@ exec-env:RUST_BACKTRACE=0 +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ ignore-emscripten no threads support +//@ needs-unwind + +#[test] +fn abc() { + panic!(); +} + +#[test] +fn foo() { + panic!(); +} + +#[test] +fn foo2() { + panic!(); +} + +// run a whole bunch of tests so we can see what happens when we go over 88 columns +#[test] fn f0() {} +#[test] fn f1() {} +#[test] fn f2() {} +#[test] fn f3() {} +#[test] fn f4() {} +#[test] fn f5() {} +#[test] fn f6() {} +#[test] fn f7() {} +#[test] fn f8() {} +#[test] fn f9() {} +#[test] fn f10() {} +#[test] fn f11() {} +#[test] fn f12() {} +#[test] fn f13() {} +#[test] fn f14() {} +#[test] fn f15() {} +#[test] fn f16() {} +#[test] fn f17() {} +#[test] fn f18() {} +#[test] fn f19() {} +#[test] fn f20() {} +#[test] fn f21() {} +#[test] fn f22() {} +#[test] fn f23() {} +#[test] fn f24() {} +#[test] fn f25() {} +#[test] fn f26() {} +#[test] fn f27() {} +#[test] fn f28() {} +#[test] fn f29() {} +#[test] fn f30() {} +#[test] fn f31() {} +#[test] fn f32() {} +#[test] fn f33() {} +#[test] fn f34() {} +#[test] fn f35() {} +#[test] fn f36() {} +#[test] fn f37() {} +#[test] fn f38() {} +#[test] fn f39() {} +#[test] fn f40() {} +#[test] fn f41() {} +#[test] fn f42() {} +#[test] fn f43() {} +#[test] fn f44() {} +#[test] fn f45() {} +#[test] fn f46() {} +#[test] fn f47() {} +#[test] fn f48() {} +#[test] fn f49() {} +#[test] fn f50() {} +#[test] fn f51() {} +#[test] fn f52() {} +#[test] fn f53() {} +#[test] fn f54() {} +#[test] fn f55() {} +#[test] fn f56() {} +#[test] fn f57() {} +#[test] fn f58() {} +#[test] fn f59() {} +#[test] fn f60() {} +#[test] fn f61() {} +#[test] fn f62() {} +#[test] fn f63() {} +#[test] fn f64() {} +#[test] fn f65() {} +#[test] fn f66() {} +#[test] fn f67() {} +#[test] fn f68() {} +#[test] fn f69() {} +#[test] fn f70() {} +#[test] fn f71() {} +#[test] fn f72() {} +#[test] fn f73() {} +#[test] fn f74() {} +#[test] fn f75() {} +#[test] fn f76() {} +#[test] fn f77() {} +#[test] fn f78() {} +#[test] fn f79() {} +#[test] fn f80() {} +#[test] fn f81() {} +#[test] fn f82() {} +#[test] fn f83() {} +#[test] fn f84() {} +#[test] fn f85() {} +#[test] fn f86() {} +#[test] fn f87() {} +#[test] fn f88() {} +#[test] fn f89() {} +#[test] fn f90() {} +#[test] fn f91() {} +#[test] fn f92() {} +#[test] fn f93() {} +#[test] fn f94() {} +#[test] fn f95() {} +#[test] fn f96() {} +#[test] fn f97() {} +#[test] fn f98() {} +#[test] fn f99() {} diff --git a/tests/ui/test-attrs/terse.run.stdout b/tests/ui/test-attrs/terse.run.stdout new file mode 100644 index 0000000000000..2b361361ae888 --- /dev/null +++ b/tests/ui/test-attrs/terse.run.stdout @@ -0,0 +1,31 @@ + +running 103 tests +abc --- FAILED +....................................................................................... 88/103 +............. 101/103 +foo --- FAILED +foo2 --- FAILED + +failures: + +---- abc stdout ---- +thread 'abc' panicked at $DIR/terse.rs:12:5: +explicit panic +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +---- foo stdout ---- +thread 'foo' panicked at $DIR/terse.rs:17:5: +explicit panic + +---- foo2 stdout ---- +thread 'foo2' panicked at $DIR/terse.rs:22:5: +explicit panic + + +failures: + abc + foo + foo2 + +test result: FAILED. 100 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + From d6023948275b3cef0b8320864595d24ad959c0e3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 21 Feb 2024 14:51:59 +1100 Subject: [PATCH 04/12] Change message type in bug functions. From `impl Into` to `impl Into>`. Because these functions don't produce user-facing output and we don't want their strings to be translated. --- compiler/rustc_abi/src/layout.rs | 7 +++- compiler/rustc_errors/src/lib.rs | 40 +++++++++++-------- compiler/rustc_middle/src/ty/layout.rs | 3 +- compiler/rustc_middle/src/ty/sty.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 5 ++- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 28e148bddb220..1c6d0495ecfa1 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1,6 +1,9 @@ +use std::borrow::{Borrow, Cow}; +use std::cmp; use std::fmt::{self, Write}; +use std::iter; +use std::ops::Bound; use std::ops::Deref; -use std::{borrow::Borrow, cmp, iter, ops::Bound}; use rustc_index::Idx; use tracing::debug; @@ -32,7 +35,7 @@ where pub trait LayoutCalculator { type TargetDataLayoutRef: Borrow; - fn delayed_bug(&self, txt: String); + fn delayed_bug(&self, txt: impl Into>); fn current_data_layout(&self) -> Self::TargetDataLayoutRef; fn scalar_pair( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0a6d66f52dad0..0f5757a2e9ba0 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1076,32 +1076,36 @@ impl DiagCtxt { // Functions beginning with `struct_`/`create_` create a diagnostic. Other // functions create and emit a diagnostic all in one go. impl DiagCtxt { - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn struct_bug(&self, msg: impl Into) -> Diag<'_, BugAbort> { - Diag::new(self, Bug, msg) + pub fn struct_bug(&self, msg: impl Into>) -> Diag<'_, BugAbort> { + Diag::new(self, Bug, msg.into()) } - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn bug(&self, msg: impl Into) -> ! { + pub fn bug(&self, msg: impl Into>) -> ! { self.struct_bug(msg).emit() } - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] pub fn struct_span_bug( &self, span: impl Into, - msg: impl Into, + msg: impl Into>, ) -> Diag<'_, BugAbort> { self.struct_bug(msg).with_span(span) } - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn span_bug(&self, span: impl Into, msg: impl Into) -> ! { - self.struct_span_bug(span, msg).emit() + pub fn span_bug(&self, span: impl Into, msg: impl Into>) -> ! { + self.struct_span_bug(span, msg.into()).emit() } #[track_caller] @@ -1215,24 +1219,28 @@ impl DiagCtxt { } /// Ensures that an error is printed. See `Level::DelayedBug`. - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] - pub fn delayed_bug(&self, msg: impl Into) -> ErrorGuaranteed { - Diag::::new(self, DelayedBug, msg).emit() + pub fn delayed_bug(&self, msg: impl Into>) -> ErrorGuaranteed { + Diag::::new(self, DelayedBug, msg.into()).emit() } /// Ensures that an error is printed. See `Level::DelayedBug`. /// /// Note: this function used to be called `delay_span_bug`. It was renamed /// to match similar functions like `span_err`, `span_warn`, etc. - // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing. + // + // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't + // user-facing. #[track_caller] pub fn span_delayed_bug( &self, sp: impl Into, - msg: impl Into, + msg: impl Into>, ) -> ErrorGuaranteed { - Diag::::new(self, DelayedBug, msg).with_span(sp).emit() + Diag::::new(self, DelayedBug, msg.into()).with_span(sp).emit() } #[rustc_lint_diagnostics] diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 5a0e2aa691d58..ff511ea803adb 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -17,6 +17,7 @@ use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; +use std::borrow::Cow; use std::cmp; use std::fmt; use std::num::NonZero; @@ -268,7 +269,7 @@ pub struct LayoutCx<'tcx, C> { impl<'tcx> LayoutCalculator for LayoutCx<'tcx, TyCtxt<'tcx>> { type TargetDataLayoutRef = &'tcx TargetDataLayout; - fn delayed_bug(&self, txt: String) { + fn delayed_bug(&self, txt: impl Into>) { self.tcx.dcx().delayed_bug(txt); } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 17659ac23307c..cbef0ca11a56f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -13,7 +13,7 @@ use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use crate::ty::{List, ParamEnv}; use hir::def::DefKind; use rustc_data_structures::captures::Captures; -use rustc_errors::{DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagnosticArg, MultiSpan}; +use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagnosticArg, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; @@ -1543,7 +1543,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_error_with_message>( tcx: TyCtxt<'tcx>, span: S, - msg: impl Into, + msg: impl Into>, ) -> Ty<'tcx> { let reported = tcx.dcx().span_delayed_bug(span, msg); Ty::new(tcx, Error(reported)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index be1c8d9094bfb..a1be60180838c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -1,5 +1,6 @@ //! Compute the binary representation of a type +use std::borrow::Cow; use std::fmt; use base_db::salsa::Cycle; @@ -114,8 +115,8 @@ struct LayoutCx<'a> { impl<'a> LayoutCalculator for LayoutCx<'a> { type TargetDataLayoutRef = &'a TargetDataLayout; - fn delayed_bug(&self, txt: String) { - never!("{}", txt); + fn delayed_bug(&self, txt: impl Into>) { + never!("{}", txt.into()); } fn current_data_layout(&self) -> &'a TargetDataLayout { From 6120de99f7be901f94e55c2cdf09b6fb98f93409 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 5 Mar 2024 18:31:32 +0000 Subject: [PATCH 05/12] Fix linting paths with qself in `unused_qualifications` --- compiler/rustc_resolve/src/late.rs | 70 +++++++++++++------------ tests/ui/lint/lint-qualification.fixed | 3 ++ tests/ui/lint/lint-qualification.rs | 3 ++ tests/ui/lint/lint-qualification.stderr | 14 ++++- 4 files changed, 56 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index de6e121c79bbb..36279de83d34b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3953,6 +3953,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Avoid recording definition of `A::B` in `::B::C`. self.r.record_partial_res(node_id, partial_res); self.resolve_elided_lifetimes_in_path(partial_res, path, source, path_span); + self.lint_unused_qualifications(path, ns, finalize); } partial_res @@ -4145,39 +4146,6 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"), }; - if path.iter().all(|seg| !seg.ident.span.from_expansion()) { - let end_pos = - path.iter().position(|seg| seg.has_generic_args).map_or(path.len(), |pos| pos + 1); - let unqualified = - path[..end_pos].iter().enumerate().skip(1).rev().find_map(|(i, seg)| { - // Preserve the current namespace for the final path segment, but use the type - // namespace for all preceding segments - // - // e.g. for `std::env::args` check the `ValueNS` for `args` but the `TypeNS` for - // `std` and `env` - // - // If the final path segment is beyond `end_pos` all the segments to check will - // use the type namespace - let ns = if i + 1 == path.len() { ns } else { TypeNS }; - let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?; - let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?; - - (res == binding.res()).then_some(seg) - }); - - if let Some(unqualified) = unqualified { - self.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNUSED_QUALIFICATIONS, - finalize.node_id, - finalize.path_span, - "unnecessary qualification", - lint::BuiltinLintDiag::UnusedQualifications { - removal_span: finalize.path_span.until(unqualified.ident.span), - }, - ); - } - } - Ok(Some(result)) } @@ -4656,6 +4624,42 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.r.doc_link_traits_in_scope = doc_link_traits_in_scope; } } + + fn lint_unused_qualifications(&mut self, path: &[Segment], ns: Namespace, finalize: Finalize) { + if path.iter().any(|seg| seg.ident.span.from_expansion()) { + return; + } + + let end_pos = + path.iter().position(|seg| seg.has_generic_args).map_or(path.len(), |pos| pos + 1); + let unqualified = path[..end_pos].iter().enumerate().skip(1).rev().find_map(|(i, seg)| { + // Preserve the current namespace for the final path segment, but use the type + // namespace for all preceding segments + // + // e.g. for `std::env::args` check the `ValueNS` for `args` but the `TypeNS` for + // `std` and `env` + // + // If the final path segment is beyond `end_pos` all the segments to check will + // use the type namespace + let ns = if i + 1 == path.len() { ns } else { TypeNS }; + let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?; + let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?; + + (res == binding.res()).then_some(seg) + }); + + if let Some(unqualified) = unqualified { + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNUSED_QUALIFICATIONS, + finalize.node_id, + finalize.path_span, + "unnecessary qualification", + lint::BuiltinLintDiag::UnusedQualifications { + removal_span: path[0].ident.span.until(unqualified.ident.span), + }, + ); + } + } } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of diff --git a/tests/ui/lint/lint-qualification.fixed b/tests/ui/lint/lint-qualification.fixed index ab450f8b7347d..2b1f8b591752a 100644 --- a/tests/ui/lint/lint-qualification.fixed +++ b/tests/ui/lint/lint-qualification.fixed @@ -24,6 +24,9 @@ fn main() { use std::fmt; let _: fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + let _ = ::default(); // issue #121999 + //~^ ERROR: unnecessary qualification + macro_rules! m { ($a:ident, $b:ident) => { $crate::foo::bar(); // issue #37357 ::foo::bar(); // issue #38682 diff --git a/tests/ui/lint/lint-qualification.rs b/tests/ui/lint/lint-qualification.rs index 84a36f509eb9e..002fdbf7724a7 100644 --- a/tests/ui/lint/lint-qualification.rs +++ b/tests/ui/lint/lint-qualification.rs @@ -24,6 +24,9 @@ fn main() { use std::fmt; let _: std::fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + let _ = ::default(); // issue #121999 + //~^ ERROR: unnecessary qualification + macro_rules! m { ($a:ident, $b:ident) => { $crate::foo::bar(); // issue #37357 ::foo::bar(); // issue #38682 diff --git a/tests/ui/lint/lint-qualification.stderr b/tests/ui/lint/lint-qualification.stderr index 45e1525f4bf8c..8dddcf23f7575 100644 --- a/tests/ui/lint/lint-qualification.stderr +++ b/tests/ui/lint/lint-qualification.stderr @@ -87,5 +87,17 @@ LL - let _: std::fmt::Result = Ok(()); LL + let _: fmt::Result = Ok(()); | -error: aborting due to 7 previous errors +error: unnecessary qualification + --> $DIR/lint-qualification.rs:27:13 + | +LL | let _ = ::default(); // issue #121999 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the unnecessary path segments + | +LL - let _ = ::default(); // issue #121999 +LL + let _ = ::default(); // issue #121999 + | + +error: aborting due to 8 previous errors From efe5a40f2b6b1f748a966a7e1da1903ed1b8cb58 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Sun, 25 Feb 2024 00:47:49 -0500 Subject: [PATCH 06/12] remove all-zero GEP This always produces zero offset, regardless of what the struct layout is. Originally, this may have been necessary in order to change the pointer type, but with opaque pointers, it is no longer necessary. --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f7ff36c0467d2..9ae82d4845e16 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -94,8 +94,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } if let OperandValue::Immediate(v) = cg_elem.val { - let zero = bx.const_usize(0); - let start = dest.project_index(bx, zero).llval; + let start = dest.llval; let size = bx.const_usize(dest.layout.size.bytes()); // Use llvm.memset.p0i8.* to initialize all zero arrays From b2c6b12c0ae48d54d91aba9d89b30945ddae2c4c Mon Sep 17 00:00:00 2001 From: Kornel Date: Fri, 1 Mar 2024 13:37:08 +0000 Subject: [PATCH 07/12] Helper function for resolve_path --- compiler/rustc_expand/src/base.rs | 25 +++++++++++-------------- compiler/rustc_span/src/lib.rs | 11 +++++++++++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 550ed078549e2..ec29e7ed079f3 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1223,21 +1223,18 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe // after macro expansion (that is, they are unhygienic). if !path.is_absolute() { let callsite = span.source_callsite(); - let mut result = match sess.source_map().span_to_filename(callsite) { - FileName::Real(name) => name - .into_local_path() - .expect("attempting to resolve a file path in an external file"), - FileName::DocTest(path, _) => path, - other => { - return Err(sess.dcx().create_err(errors::ResolveRelativePath { - span, - path: sess.source_map().filename_for_diagnostics(&other).to_string(), - })); - } + let source_map = sess.source_map(); + let Some(mut base_path) = source_map.span_to_filename(callsite).into_local_path() else { + return Err(sess.dcx().create_err(errors::ResolveRelativePath { + span, + path: source_map + .filename_for_diagnostics(&source_map.span_to_filename(callsite)) + .to_string(), + })); }; - result.pop(); - result.push(path); - Ok(result) + base_path.pop(); + base_path.push(path); + Ok(base_path) } else { Ok(path) } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 616a7ccc7c64f..0c974ef4ca3eb 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -427,6 +427,17 @@ impl FileName { src.hash(&mut hasher); FileName::InlineAsm(hasher.finish()) } + + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. + /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + pub fn into_local_path(self) -> Option { + match self { + FileName::Real(path) => path.into_local_path(), + FileName::DocTest(path, _) => Some(path), + _ => None, + } + } } /// Represents a span. From da8cbe40fe1a444051a98e754e9a33549ef676a5 Mon Sep 17 00:00:00 2001 From: Kornel Date: Fri, 1 Mar 2024 02:49:02 +0000 Subject: [PATCH 08/12] Suggest correct path in include_bytes! --- .../rustc_builtin_macros/src/source_util.rs | 150 ++++++++++++++---- compiler/rustc_expand/src/base.rs | 16 +- tests/ui/include-macros/parent_dir.rs | 10 ++ tests/ui/include-macros/parent_dir.stderr | 50 ++++++ tests/ui/macros/macros-nonfatal-errors.stderr | 4 +- 5 files changed, 197 insertions(+), 33 deletions(-) create mode 100644 tests/ui/include-macros/parent_dir.rs create mode 100644 tests/ui/include-macros/parent_dir.stderr diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 2da9bda19e034..134a6baea6e8b 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -3,18 +3,20 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; +use rustc_data_structures::sync::Lrc; use rustc_expand::base::{ - check_zero_tts, get_single_str_from_tts, parse_expr, resolve_path, DummyResult, ExtCtxt, - MacEager, MacResult, + check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, + resolve_path, DummyResult, ExtCtxt, MacEager, MacResult, }; use rustc_expand::module::DirOwnership; use rustc_parse::new_parser_from_file; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; use rustc_span::{Pos, Span}; - use smallvec::SmallVec; +use std::path::{Path, PathBuf}; use std::rc::Rc; // These macros all relate to the file system; they either return @@ -180,32 +182,22 @@ pub fn expand_include_str( tts: TokenStream, ) -> Box { let sp = cx.with_def_site_ctxt(sp); - let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { - Ok(file) => file, + let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_str!") { + Ok(res) => res, Err(guar) => return DummyResult::any(sp, guar), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, - Err(err) => { - let guar = err.emit(); - return DummyResult::any(sp, guar); - } - }; - match cx.source_map().load_binary_file(&file) { + match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { Ok(bytes) => match std::str::from_utf8(&bytes) { Ok(src) => { let interned_src = Symbol::intern(src); MacEager::expr(cx.expr_str(sp, interned_src)) } Err(_) => { - let guar = cx.dcx().span_err(sp, format!("{} wasn't a utf-8 file", file.display())); + let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file")); DummyResult::any(sp, guar) } }, - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) - } + Err(dummy) => dummy, } } @@ -215,25 +207,123 @@ pub fn expand_include_bytes( tts: TokenStream, ) -> Box { let sp = cx.with_def_site_ctxt(sp); - let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") { - Ok(file) => file, + let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_bytes!") { + Ok(res) => res, Err(guar) => return DummyResult::any(sp, guar), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, + match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { + Ok(bytes) => { + let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); + MacEager::expr(expr) + } + Err(dummy) => dummy, + } +} + +fn load_binary_file( + cx: &mut ExtCtxt<'_>, + original_path: &Path, + macro_span: Span, + path_span: Span, +) -> Result, Box> { + let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) { + Ok(path) => path, Err(err) => { let guar = err.emit(); - return DummyResult::any(sp, guar); + return Err(DummyResult::any(macro_span, guar)); } }; - match cx.source_map().load_binary_file(&file) { - Ok(bytes) => { - let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); - MacEager::expr(expr) + match cx.source_map().load_binary_file(&resolved_path) { + Ok(data) => Ok(data), + Err(io_err) => { + let mut err = cx.dcx().struct_span_err( + macro_span, + format!("couldn't read `{}`: {io_err}", resolved_path.display()), + ); + + if original_path.is_relative() { + let source_map = cx.sess.source_map(); + let new_path = source_map + .span_to_filename(macro_span.source_callsite()) + .into_local_path() + .and_then(|src| find_path_suggestion(source_map, src.parent()?, original_path)) + .and_then(|path| path.into_os_string().into_string().ok()); + + if let Some(new_path) = new_path { + err.span_suggestion( + path_span, + "there is a file with the same name in a different directory", + format!("\"{}\"", new_path.escape_debug()), + rustc_lint_defs::Applicability::MachineApplicable, + ); + } + } + let guar = err.emit(); + Err(DummyResult::any(macro_span, guar)) } - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) + } +} + +fn find_path_suggestion( + source_map: &SourceMap, + base_dir: &Path, + wanted_path: &Path, +) -> Option { + // Fix paths that assume they're relative to cargo manifest dir + let mut base_c = base_dir.components(); + let mut wanted_c = wanted_path.components(); + let mut without_base = None; + while let Some(wanted_next) = wanted_c.next() { + if wanted_c.as_path().file_name().is_none() { + break; + } + // base_dir may be absolute + while let Some(base_next) = base_c.next() { + if base_next == wanted_next { + without_base = Some(wanted_c.as_path()); + break; + } + } + } + let root_absolute = without_base.into_iter().map(PathBuf::from); + + let base_dir_components = base_dir.components().count(); + // Avoid going all the way to the root dir + let max_parent_components = if base_dir.is_relative() { + base_dir_components + 1 + } else { + base_dir_components.saturating_sub(1) + }; + + // Try with additional leading ../ + let mut prefix = PathBuf::new(); + let add = std::iter::from_fn(|| { + prefix.push(".."); + Some(prefix.join(wanted_path)) + }) + .take(max_parent_components.min(3)); + + // Try without leading directories + let mut trimmed_path = wanted_path; + let remove = std::iter::from_fn(|| { + let mut components = trimmed_path.components(); + let removed = components.next()?; + trimmed_path = components.as_path(); + let _ = trimmed_path.file_name()?; // ensure there is a file name left + Some([ + Some(trimmed_path.to_path_buf()), + (removed != std::path::Component::ParentDir) + .then(|| Path::new("..").join(trimmed_path)), + ]) + }) + .flatten() + .flatten() + .take(4); + + for new_path in root_absolute.chain(add).chain(remove) { + if source_map.file_exists(&base_dir.join(&new_path)) { + return Some(new_path); } } + None } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ec29e7ed079f3..bc66517e4d6dd 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1331,6 +1331,15 @@ pub fn get_single_str_from_tts( tts: TokenStream, name: &str, ) -> Result { + get_single_str_spanned_from_tts(cx, span, tts, name).map(|(s, _)| s) +} + +pub fn get_single_str_spanned_from_tts( + cx: &mut ExtCtxt<'_>, + span: Span, + tts: TokenStream, + name: &str, +) -> Result<(Symbol, Span), ErrorGuaranteed> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); @@ -1342,7 +1351,12 @@ pub fn get_single_str_from_tts( if p.token != token::Eof { cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); } - expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s) + expr_to_spanned_string(cx, ret, "argument must be a string literal") + .map_err(|err| match err { + Ok((err, _)) => err.emit(), + Err(guar) => guar, + }) + .map(|(symbol, _style, span)| (symbol, span)) } /// Extracts comma-separated expressions from `tts`. diff --git a/tests/ui/include-macros/parent_dir.rs b/tests/ui/include-macros/parent_dir.rs new file mode 100644 index 0000000000000..4933ec4373c61 --- /dev/null +++ b/tests/ui/include-macros/parent_dir.rs @@ -0,0 +1,10 @@ +fn main() { + let _ = include_str!("include-macros/file.txt"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_str!("hello.rs"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_bytes!("../../data.bin"); //~ ERROR couldn't read + //~^HELP different directory + let _ = include_str!("tests/ui/include-macros/file.txt"); //~ ERROR couldn't read + //~^HELP different directory +} diff --git a/tests/ui/include-macros/parent_dir.stderr b/tests/ui/include-macros/parent_dir.stderr new file mode 100644 index 0000000000000..3ec4c34763e4f --- /dev/null +++ b/tests/ui/include-macros/parent_dir.stderr @@ -0,0 +1,50 @@ +error: couldn't read `$DIR/include-macros/file.txt`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:2:13 + | +LL | let _ = include_str!("include-macros/file.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("file.txt"); + | ~~~~~~~~~~ + +error: couldn't read `$DIR/hello.rs`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:4:13 + | +LL | let _ = include_str!("hello.rs"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("../hello.rs"); + | ~~~~~~~~~~~~~ + +error: couldn't read `$DIR/../../data.bin`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:6:13 + | +LL | let _ = include_bytes!("../../data.bin"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_bytes!("data.bin"); + | ~~~~~~~~~~ + +error: couldn't read `$DIR/tests/ui/include-macros/file.txt`: No such file or directory (os error 2) + --> $DIR/parent_dir.rs:8:13 + | +LL | let _ = include_str!("tests/ui/include-macros/file.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) +help: there is a file with the same name in a different directory + | +LL | let _ = include_str!("file.txt"); + | ~~~~~~~~~~ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index ca373ea6cd9e4..2b3cff9891e1e 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -200,7 +200,7 @@ error: argument must be a string literal LL | include_str!(invalid); | ^^^^^^^ -error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) +error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: No such file or directory (os error 2) --> $DIR/macros-nonfatal-errors.rs:113:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); @@ -214,7 +214,7 @@ error: argument must be a string literal LL | include_bytes!(invalid); | ^^^^^^^ -error: couldn't read $DIR/i'd be quite surprised if a file with this name existed: $FILE_NOT_FOUND_MSG (os error 2) +error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: No such file or directory (os error 2) --> $DIR/macros-nonfatal-errors.rs:115:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); From b7d58eef4b9e6bd1aa454c3100815fb5d5481a7b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 20 Feb 2024 14:12:50 +1100 Subject: [PATCH 09/12] Rewrite the `untranslatable_diagnostic` lint. Currently it only checks calls to functions marked with `#[rustc_lint_diagnostics]`. This commit changes it to check calls to any function with an `impl Into<{D,Subd}iagMessage>` parameter. This greatly improves its coverage and doesn't rely on people remembering to add `#[rustc_lint_diagnostics]`. The commit also adds `#[allow(rustc::untranslatable_diagnostic)`] attributes to places that need it that are caught by the improved lint. These places that might be easy to convert to translatable diagnostics. Finally, it also: - Expands and corrects some comments. - Does some minor formatting improvements. - Adds missing `DecorateLint` cases to `tests/ui-fulldeps/internal-lints/diagnostics.rs`. --- compiler/rustc_ast_lowering/src/asm.rs | 1 + compiler/rustc_ast_lowering/src/expr.rs | 1 + compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_passes/src/feature_gate.rs | 4 + compiler/rustc_attr/src/builtin.rs | 3 + .../rustc_borrowck/src/diagnostics/mod.rs | 1 + .../src/diagnostics/region_errors.rs | 4 + .../src/transform/check_consts/ops.rs | 4 + compiler/rustc_driver_impl/src/args.rs | 1 + compiler/rustc_driver_impl/src/lib.rs | 1 + compiler/rustc_expand/src/base.rs | 2 + compiler/rustc_expand/src/config.rs | 4 +- compiler/rustc_expand/src/expand.rs | 3 + compiler/rustc_expand/src/lib.rs | 1 + .../rustc_expand/src/proc_macro_server.rs | 3 + compiler/rustc_expand/src/tests.rs | 2 +- compiler/rustc_feature/src/builtin_attrs.rs | 4 +- compiler/rustc_interface/src/interface.rs | 1 + compiler/rustc_interface/src/util.rs | 3 + compiler/rustc_lint/src/context.rs | 7 +- compiler/rustc_lint/src/internal.rs | 165 +++++++++++++----- compiler/rustc_lint/src/let_underscore.rs | 1 + compiler/rustc_lint/src/levels.rs | 1 + compiler/rustc_metadata/src/creader.rs | 1 + compiler/rustc_metadata/src/native_libs.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 2 + compiler/rustc_passes/src/check_const.rs | 1 + compiler/rustc_passes/src/entry.rs | 1 + compiler/rustc_session/src/config.rs | 6 +- compiler/rustc_session/src/options.rs | 1 + compiler/rustc_session/src/search_paths.rs | 1 + compiler/rustc_session/src/session.rs | 1 + src/tools/rustfmt/src/parse/session.rs | 1 + .../ui-fulldeps/internal-lints/diagnostics.rs | 33 +++- .../internal-lints/diagnostics.stderr | 14 +- 35 files changed, 221 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index fd717e82d26df..7fd419f62e459 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -22,6 +22,7 @@ use std::collections::hash_map::Entry; use std::fmt::Write; impl<'a, 'hir> LoweringContext<'a, 'hir> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn lower_inline_asm( &mut self, sp: Span, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0b1cb12408003..ddeea4c56b12f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1495,6 +1495,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> { let is_async_gen = match self.coroutine_kind { Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => false, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 087d240b0d50f..37ee9a5140a06 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2272,6 +2272,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.expr_block(block) } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen { match c.value.kind { ExprKind::Underscore => { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a28fcb0077948..d9e3f028697e0 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -17,6 +17,7 @@ use crate::errors; macro_rules! gate { ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{ if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit(); } }}; @@ -34,6 +35,7 @@ macro_rules! gate { macro_rules! gate_alt { ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr) => {{ if !$has_feature && !$span.allows_unstable($name) { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable feature_err(&$visitor.sess, $name, $span, $explain).emit(); } }}; @@ -73,6 +75,7 @@ struct PostExpansionVisitor<'a> { } impl<'a> PostExpansionVisitor<'a> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_abi(&self, abi: ast::StrLit, constness: ast::Const) { let ast::StrLit { symbol_unescaped, span, .. } = abi; @@ -579,6 +582,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { if let Ok(snippet) = sm.span_to_snippet(span) && snippet == "!" { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental") .emit(); } else { diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index eb25b910c7baf..814104ec78c49 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -516,6 +516,7 @@ pub struct Condition { } /// Tests if a cfg-pattern matches the cfg set +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn cfg_matches( cfg: &ast::MetaItem, sess: &Session, @@ -566,6 +567,7 @@ fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Feat } } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { let (cfg, feature, has_feature) = gated_cfg; if !has_feature(features) && !cfg_span.allows_unstable(*feature) { @@ -592,6 +594,7 @@ fn parse_version(s: Symbol) -> Option { /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn eval_condition( cfg: &ast::MetaItem, sess: &Session, diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 53e8ac121bbce..65d530dd92771 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1008,6 +1008,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.borrow_spans(span, borrow.reserve_location) } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn explain_captures( &mut self, err: &mut Diag<'_>, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3765dfe5db53c..65eda5a714296 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -201,6 +201,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // For generic associated types (GATs) which implied 'static requirement // from higher-ranked trait bounds (HRTB). Try to locate span of the trait // and the span which bounded to the trait for adding 'static lifetime suggestion + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_static_lifetime_for_gat_from_hrtb( &self, diag: &mut Diag<'_>, @@ -822,6 +823,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// LL | fn iter_values_anon(&self) -> impl Iterator + 'a { /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// ``` + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn add_static_impl_trait_suggestion( &self, diag: &mut Diag<'_>, @@ -972,6 +974,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty); } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #[instrument(skip(self, err), level = "debug")] fn suggest_constrain_dyn_trait_in_impl( &self, @@ -1034,6 +1037,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag); } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_move_on_borrowing_closure(&self, diag: &mut Diag<'_>) { let map = self.infcx.tcx.hir(); let body_id = map.body_owned_by(self.mir_def_id()); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 1107b894ab3ee..15720f25c5cff 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -62,6 +62,7 @@ impl<'tcx> NonConstOp<'tcx> for FloatingPointOp { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { feature_err( &ccx.tcx.sess, @@ -556,6 +557,7 @@ impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { Status::Unstable(sym::const_mut_refs) } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { feature_err( &ccx.tcx.sess, @@ -589,6 +591,7 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { let mut err = feature_err( &ccx.tcx.sess, @@ -632,6 +635,7 @@ pub mod ty { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { feature_err( &ccx.tcx.sess, diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index 5dfd37a6da4de..8b6fb5fd660f3 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -97,6 +97,7 @@ impl Expander { /// **Note:** This function doesn't interpret argument 0 in any special way. /// If this function is intended to be used with command line arguments, /// `argv[0]` must be removed prior to calling it manually. +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec { let mut expander = Expander::default(); for arg in at_args { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index ab1e37e4ac221..ebe3a4ffd045a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -4,6 +4,7 @@ //! //! This API is completely unstable and subject to change. +#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 550ed078549e2..6a1ef318a8c1e 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1483,6 +1483,8 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) -> bool { }; if crate_matches { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] sess.psess.buffer_lint_with_diagnostic( PROC_MACRO_BACK_COMPAT, item.ident.span, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 921fea143124a..c95d7cdeb73ab 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -239,6 +239,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn expand_cfg_attr(&self, attr: &Attribute, recursive: bool) -> Vec { let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(attr, &self.sess.psess) @@ -273,6 +274,7 @@ impl<'a> StripUnconfigured<'a> { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn expand_cfg_attr_item( &self, attr: &Attribute, @@ -371,6 +373,7 @@ impl<'a> StripUnconfigured<'a> { } /// If attributes are not allowed on expressions, emit an error for `attr` + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #[instrument(level = "trace", skip(self))] pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { if self.features.is_some_and(|features| !features.stmt_expr_attributes) @@ -384,7 +387,6 @@ impl<'a> StripUnconfigured<'a> { ); if attr.is_doc_comment() { - #[allow(rustc::untranslatable_diagnostic)] err.help("`///` is for documentation comments. For a plain comment, use `//`."); } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 9d6daf1f47fbe..8e5a0eb336bfe 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -784,6 +784,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }) } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) { let kind = match item { Annotatable::Item(_) @@ -826,6 +827,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn visit_item(&mut self, item: &'ast ast::Item) { match &item.kind { ItemKind::Mod(_, mod_kind) @@ -1690,6 +1692,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Detect use of feature-gated or invalid attributes on macro invocations // since they will not be detected after macro expansion. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { let features = self.cx.ecfg.features; let mut attrs = attrs.iter().peekable(); diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 0e73abc9ed854..e550f7242c338 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -5,6 +5,7 @@ #![feature(associated_type_defaults)] #![feature(if_let_guard)] #![feature(let_chains)] +#![feature(lint_reasons)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] #![feature(proc_macro_diagnostic)] diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 32a083a72f013..ddc685c9d07da 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -517,6 +517,9 @@ impl server::FreeFunctions for Rustc<'_, '_> { Diag::new(&self.psess().dcx, diagnostic.level.to_internal(), message); diag.span(MultiSpan::from_spans(diagnostic.spans)); for child in diagnostic.children { + // This message comes from another diagnostic, and we are just reconstructing the + // diagnostic, so there's no need for translation. + #[allow(rustc::untranslatable_diagnostic)] diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans)); } diag.emit(); diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 8c47b7594535c..a3510dc9bff4d 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -178,6 +178,7 @@ impl Write for Shared { } } +#[allow(rustc::untranslatable_diagnostic)] // no translation needed for tests fn test_harness(file_text: &str, span_labels: Vec, expected_output: &str) { create_default_session_globals_then(|| { let (handler, source_map, output) = create_test_handler(); @@ -192,7 +193,6 @@ fn test_harness(file_text: &str, span_labels: Vec, expected_output: & println!("text: {:?}", source_map.span_to_snippet(span)); } - #[allow(rustc::untranslatable_diagnostic)] handler.span_err(msp, "foo"); assert!( diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index de7ea84ffa551..0780e9bdce4c7 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -695,8 +695,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::potential_query_instability` lint to warn methods which // might not be stable during incremental compilation. rustc_attr!(rustc_lint_query_instability, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), - // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints - // to assist in changes to diagnostic APIs. + // Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic + // APIs. Any function with this attribute will be checked by that lint. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` // types (as well as any others in future). diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 3a2bbed19beff..820626173a879 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -318,6 +318,7 @@ pub struct Config { // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { trace!("run_compiler"); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index bf3beb677aa6d..0d50200133cb2 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -160,6 +160,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( }) } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn { match unsafe { load_symbol_from_dylib::(path, "__rustc_codegen_backend") } { Ok(backend_sym) => backend_sym, @@ -227,6 +228,7 @@ fn get_rustc_path_inner(bin_path: &str) -> Option { }) } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn get_codegen_sysroot( early_dcx: &EarlyDiagCtxt, maybe_sysroot: &Option, @@ -319,6 +321,7 @@ fn get_codegen_sysroot( } } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn check_attr_crate_type( sess: &Session, attrs: &[ast::Attribute], diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index f043a312c719c..89cdde11726ab 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -621,12 +621,13 @@ pub trait LintContext { /// Note that this function should only be called for [`LintExpectationId`]s /// retrieved from the current lint pass. Buffered or manually created ids can /// cause ICEs. - #[rustc_lint_diagnostics] fn fulfill_expectation(&self, expectation: LintExpectationId) { // We need to make sure that submitted expectation ids are correctly fulfilled suppressed // and stored between compilation sessions. To not manually do these steps, we simply create - // a dummy diagnostic and emit is as usual, which will be suppressed and stored like a normal - // expected lint diagnostic. + // a dummy diagnostic and emit it as usual, which will be suppressed and stored like a + // normal expected lint diagnostic. + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] self.sess() .dcx() .struct_expect( diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index fdcd0192e6a74..3a8c8e79b4f28 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -10,7 +10,7 @@ use rustc_ast as ast; use rustc_hir::def::Res; use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath}; use rustc_hir::{BinOp, BinOpKind, HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::symbol::{kw, sym, Symbol}; @@ -338,10 +338,11 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { } declare_tool_lint! { - /// The `untranslatable_diagnostic` lint detects diagnostics created - /// without using translatable Fluent strings. + /// The `untranslatable_diagnostic` lint detects messages passed to functions with `impl + /// Into<{D,Subd}iagMessage` parameters without using translatable Fluent strings. /// - /// More details on translatable diagnostics can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/translation.html). + /// More details on translatable diagnostics can be found + /// [here](https://rustc-dev-guide.rust-lang.org/diagnostics/translation.html). pub rustc::UNTRANSLATABLE_DIAGNOSTIC, Deny, "prevent creation of diagnostics which cannot be translated", @@ -349,11 +350,13 @@ declare_tool_lint! { } declare_tool_lint! { - /// The `diagnostic_outside_of_impl` lint detects diagnostics created manually, - /// and inside an `IntoDiagnostic`/`AddToDiagnostic` implementation, - /// or a `#[derive(Diagnostic)]`/`#[derive(Subdiagnostic)]` expansion. + /// The `diagnostic_outside_of_impl` lint detects calls to functions annotated with + /// `#[rustc_lint_diagnostics]` that are outside an `IntoDiagnostic`, `AddToDiagnostic`, or + /// `DecorateLint` impl, or a `#[derive(Diagnostic)]`, `#[derive(Subdiagnostic)]`, + /// `#[derive(DecorateLint)]` expansion. /// - /// More details on diagnostics implementations can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html). + /// More details on diagnostics implementations can be found + /// [here](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html). pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL, Deny, "prevent creation of diagnostics outside of `IntoDiagnostic`/`AddToDiagnostic` impls", @@ -364,54 +367,130 @@ declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE impl LateLintPass<'_> for Diagnostics { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return }; - debug!(?span, ?def_id, ?args); - let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, args) + // Only check function calls and method calls. + let (span, def_id, fn_gen_args, call_tys) = match expr.kind { + ExprKind::Call(callee, args) => { + match cx.typeck_results().node_type(callee.hir_id).kind() { + &ty::FnDef(def_id, fn_gen_args) => { + let call_tys: Vec<_> = + args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect(); + (callee.span, def_id, fn_gen_args, call_tys) + } + _ => return, // occurs for fns passed as args + } + } + ExprKind::MethodCall(segment, _recv, args, _span) => { + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + let fn_gen_args = cx.typeck_results().node_args(expr.hir_id); + let mut call_tys: Vec<_> = + args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect(); + call_tys.insert(0, cx.tcx.types.self_param); // dummy inserted for `self` + (segment.ident.span, def_id, fn_gen_args, call_tys) + } + _ => return, + }; + + // Is the callee marked with `#[rustc_lint_diagnostics]`? + let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, fn_gen_args) .ok() .flatten() .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)); - if !has_attr { - return; - } - let mut found_parent_with_attr = false; - let mut found_impl = false; - for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { - if let Some(owner_did) = hir_id.as_owner() { - found_parent_with_attr = found_parent_with_attr - || cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics); + // Closure: is the type `{D,Subd}iagMessage`? + let is_diag_message = |ty: MiddleTy<'_>| { + if let Some(adt_def) = ty.ty_adt_def() + && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did()) + && matches!(name, sym::DiagMessage | sym::SubdiagMessage) + { + true + } else { + false } + }; - debug!(?parent); - if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent - && let Impl { of_trait: Some(of_trait), .. } = impl_ - && let Some(def_id) = of_trait.trait_def_id() - && let Some(name) = cx.tcx.get_diagnostic_name(def_id) - && matches!(name, sym::IntoDiagnostic | sym::AddToDiagnostic | sym::DecorateLint) - { - found_impl = true; - break; + // Does the callee have a `impl Into<{D,Subd}iagMessage>` parameter? (There should be at + // most one.) + let mut impl_into_diagnostic_message_param = None; + let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates; + for (i, ¶m_ty) in fn_sig.inputs().iter().enumerate() { + if let ty::Param(p) = param_ty.kind() { + // It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`. + for pred in predicates.iter() { + if let Some(trait_pred) = pred.as_trait_clause() + && let trait_ref = trait_pred.skip_binder().trait_ref + && trait_ref.self_ty() == param_ty // correct predicate for the param? + && cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id) + && let ty1 = trait_ref.args.type_at(1) + && is_diag_message(ty1) + { + if impl_into_diagnostic_message_param.is_some() { + cx.tcx.dcx().span_bug( + span, + "can't handle multiple `impl Into<{D,Sub}iagMessage>` params", + ); + } + impl_into_diagnostic_message_param = Some((i, p.name)); + } + } } } - debug!(?found_impl); - if !found_parent_with_attr && !found_impl { - cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl); + + // Is the callee interesting? + if !has_attr && impl_into_diagnostic_message_param.is_none() { + return; } - let mut found_diagnostic_message = false; - for ty in args.types() { - debug!(?ty); - if let Some(adt_def) = ty.ty_adt_def() - && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did()) - && matches!(name, sym::DiagMessage | sym::SubdiagMessage) + // Is the parent method marked with `#[rustc_lint_diagnostics]`? + let mut parent_has_attr = false; + for (hir_id, _parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + if let Some(owner_did) = hir_id.as_owner() + && cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics) { - found_diagnostic_message = true; + parent_has_attr = true; break; } } - debug!(?found_diagnostic_message); - if !found_parent_with_attr && !found_diagnostic_message { - cx.emit_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag); + + // Calls to `#[rustc_lint_diagnostics]`-marked functions should only occur: + // - inside an impl of `IntoDiagnostic`, `AddToDiagnostic`, or `DecorateLint`, or + // - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`. + // + // Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint. + if has_attr && !parent_has_attr { + let mut is_inside_appropriate_impl = false; + for (_hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + debug!(?parent); + if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent + && let Impl { of_trait: Some(of_trait), .. } = impl_ + && let Some(def_id) = of_trait.trait_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) + && matches!( + name, + sym::IntoDiagnostic | sym::AddToDiagnostic | sym::DecorateLint + ) + { + is_inside_appropriate_impl = true; + break; + } + } + debug!(?is_inside_appropriate_impl); + if !is_inside_appropriate_impl { + cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl); + } + } + + // Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg + // with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an + // `UNTRANSLATABLE_DIAGNOSTIC` lint. + if let Some((param_i, param_i_p_name)) = impl_into_diagnostic_message_param { + // Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`? + let arg_ty = call_tys[param_i]; + let is_translatable = is_diag_message(arg_ty) + || matches!(arg_ty.kind(), ty::Param(p) if p.name == param_i_p_name); + if !is_translatable { + cx.emit_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag); + } } } } @@ -425,7 +504,7 @@ declare_tool_lint! { report_in_external_macro: true } -declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]); +declare_lint_pass!(BadOptAccess => [BAD_OPT_ACCESS]); impl LateLintPass<'_> for BadOptAccess { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index c552b9832553a..de642f373b5c4 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -104,6 +104,7 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { return; diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 906cd1d1283f8..2f08cd53b75a1 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -724,6 +724,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option) { let sess = self.sess; for (attr_index, attr) in attrs.iter().enumerate() { diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 8b48570fbbadd..c0cd6b830dcbe 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -947,6 +947,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } } + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn report_unused_deps(&mut self, krate: &ast::Crate) { // Make a point span rather than covering the whole file let span = krate.spans.inner_span.shrink_to_lo(); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index cdcc586b09e70..baa2e1ff60215 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -93,6 +93,7 @@ struct Collector<'tcx> { } impl<'tcx> Collector<'tcx> { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn process_module(&mut self, module: &ForeignModule) { let ForeignModule { def_id, abi, ref foreign_items } = *module; let def_id = def_id.expect_local(); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 04c0cf7436e2b..f523db5bddaac 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1074,6 +1074,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// of one item. Read the documentation of [`check_doc_inline`] for more information. /// /// [`check_doc_inline`]: Self::check_doc_inline + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_doc_attrs( &self, attr: &Attribute, @@ -1756,6 +1757,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if the `#[repr]` attributes on `item` are valid. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_repr( &self, attrs: &[Attribute], diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 02792491f5ed0..30a65d69c4ef8 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -77,6 +77,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { } /// Emits an error when an unsupported expression is found in a const context. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn const_check_violated(&self, expr: NonConstExpr, span: Span) { let Self { tcx, def_id, const_kind } = *self; diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 97c70e327f0fe..5246389248e9f 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -114,6 +114,7 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { } } +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { if let Some((def_id, _)) = visitor.start_fn { Some((def_id.to_def_id(), EntryFnType::Start)) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index dadbcff6f68cd..1a59f877f1f6a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1,6 +1,8 @@ //! Contains infrastructure for configuring the compiler, including parsing //! command-line options. +#![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable + pub use crate::options::*; use crate::errors::FileWriteFail; @@ -2468,9 +2470,7 @@ pub fn parse_externs( )); let adjusted_name = name.replace('-', "_"); if is_ascii_ident(&adjusted_name) { - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME error.help(format!( "consider replacing the dashes with underscores: `{adjusted_name}`" )); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 42c9d5e10eba1..3e593167b597b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -320,6 +320,7 @@ macro_rules! redirect_field { type OptionSetter = fn(&mut O, v: Option<&str>) -> bool; type OptionDescrs = &'static [(&'static str, OptionSetter, &'static str, &'static str)]; +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_options( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index 9b913c76998e0..32d5e430717bd 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -61,6 +61,7 @@ impl SearchPath { (PathKind::All, path) }; if path.is_empty() { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable early_dcx.early_fatal("empty search path given via `-L`"); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 4f4d8fabb7261..5811ce48eabf8 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1022,6 +1022,7 @@ fn default_emitter( // JUSTIFICATION: literally session construction #[allow(rustc::bad_opt_access)] +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn build_session( early_dcx: EarlyDiagCtxt, sopts: config::Options, diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 11af986051351..cf8120e9b0987 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -383,6 +383,7 @@ mod tests { } fn build_diagnostic(level: DiagnosticLevel, span: Option) -> DiagInner { + #[allow(rustc::untranslatable_diagnostic)] // no translation needed for empty string let mut diag = DiagInner::new(level, ""); diag.messages.clear(); if let Some(span) = span { diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.rs b/tests/ui-fulldeps/internal-lints/diagnostics.rs index 6a978dabe628f..f73fdc0882ff9 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.rs +++ b/tests/ui-fulldeps/internal-lints/diagnostics.rs @@ -14,8 +14,8 @@ extern crate rustc_session; extern crate rustc_span; use rustc_errors::{ - AddToDiagnostic, Diag, EmissionGuarantee, DiagCtxt, IntoDiagnostic, Level, - SubdiagMessageOp, + AddToDiagnostic, DecorateLint, Diag, DiagCtxt, DiagInner, DiagMessage, EmissionGuarantee, + IntoDiagnostic, Level, SubdiagMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -78,6 +78,31 @@ impl AddToDiagnostic for TranslatableInAddToDiagnostic { } } +pub struct UntranslatableInDecorateLint; + +impl<'a> DecorateLint<'a, ()> for UntranslatableInDecorateLint { + fn decorate_lint<'b, >(self, diag: &'b mut Diag<'a, ()>) { + diag.note("untranslatable diagnostic"); + //~^ ERROR diagnostics should be created using translatable messages + } + + fn msg(&self) -> DiagMessage { + unreachable!(); + } +} + +pub struct TranslatableInDecorateLint; + +impl<'a> DecorateLint<'a, ()> for TranslatableInDecorateLint { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.note(crate::fluent_generated::no_crate_note); + } + + fn msg(&self) -> DiagMessage { + unreachable!(); + } +} + pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) { let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); //~^ ERROR diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls @@ -87,9 +112,11 @@ pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) { //~^^ ERROR diagnostics should be created using translatable messages } -// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted. +// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted for +// `diagnostic_outside_of_impl`. #[rustc_lint_diagnostics] pub fn skipped_because_of_annotation<'a>(dcx: &'a DiagCtxt) { + #[allow(rustc::untranslatable_diagnostic)] let _diag = dcx.struct_err("untranslatable diagnostic"); // okay! } diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.stderr b/tests/ui-fulldeps/internal-lints/diagnostics.stderr index 5ddfdf32149aa..b8fbee1ff009c 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/tests/ui-fulldeps/internal-lints/diagnostics.stderr @@ -16,8 +16,14 @@ error: diagnostics should be created using translatable messages LL | diag.note("untranslatable diagnostic"); | ^^^^ +error: diagnostics should be created using translatable messages + --> $DIR/diagnostics.rs:85:14 + | +LL | diag.note("untranslatable diagnostic"); + | ^^^^ + error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:82:21 + --> $DIR/diagnostics.rs:107:21 | LL | let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); | ^^^^^^^^^^ @@ -29,16 +35,16 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:85:21 + --> $DIR/diagnostics.rs:110:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:85:21 + --> $DIR/diagnostics.rs:110:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors From 3591e77b3566772023b7e35e96224106243ef214 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 29 Feb 2024 08:53:46 +1100 Subject: [PATCH 10/12] Add missing `#[rustc_lint_diagnostics]` attributes. Prior to the previous commit, `#[rust_lint_diagnostics]` attributes could only be used on methods with an `impl Into<{D,Subd}iagMessage>` parameter. But there are many other nearby diagnostic methods (e.g. `Diag::span`) that don't take such a parameter and should have the attribute. This commit adds the missing attribute to these `Diag` methods. This requires adding some missing `#[allow(rustc::diagnostic_outside_of_impl)]` markers at call sites to these methods. --- .../rustc_borrowck/src/diagnostics/mod.rs | 5 +++ .../src/diagnostics/region_errors.rs | 9 ++++-- compiler/rustc_errors/src/diagnostic.rs | 31 +++++++++++++++++++ compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_session/src/parse.rs | 1 + compiler/rustc_session/src/session.rs | 1 + 6 files changed, 45 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 65d530dd92771..560928d394145 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -76,6 +76,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// LL | for (key, value) in dict { /// | ^^^^ /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME pub(super) fn add_moved_or_invoked_closure_note( &self, location: Location, @@ -585,6 +586,7 @@ impl UseSpans<'_> { } /// Add a span label to the arguments of the closure, if it exists. + #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn args_subdiag( self, dcx: &rustc_errors::DiagCtxt, @@ -598,6 +600,7 @@ impl UseSpans<'_> { /// Add a span label to the use of the captured variable, if it exists. /// only adds label to the `path_span` + #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_path_only_subdiag( self, dcx: &rustc_errors::DiagCtxt, @@ -635,6 +638,7 @@ impl UseSpans<'_> { } /// Add a subdiagnostic to the use of the captured variable, if it exists. + #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_subdiag( self, dcx: &rustc_errors::DiagCtxt, @@ -1008,6 +1012,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.borrow_spans(span, borrow.reserve_location) } + #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn explain_captures( &mut self, diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 65eda5a714296..c06bf94a6fd5f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -201,6 +201,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // For generic associated types (GATs) which implied 'static requirement // from higher-ranked trait bounds (HRTB). Try to locate span of the trait // and the span which bounded to the trait for adding 'static lifetime suggestion + #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_static_lifetime_for_gat_from_hrtb( &self, @@ -255,9 +256,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { hrtb_bounds.iter().for_each(|bound| { let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; }; - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] diag.span_note( *trait_span, "due to current limitations in the borrow checker, this implies a `'static` lifetime" @@ -581,6 +579,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// executing... /// = note: ...therefore, returned references to captured variables will escape the closure /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME fn report_fnmut_error( &self, errci: &ErrorConstraintInfo<'tcx>, @@ -762,6 +761,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it /// | is returning data with lifetime `'b` /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'tcx> { let ErrorConstraintInfo { fr, @@ -823,6 +823,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// LL | fn iter_values_anon(&self) -> impl Iterator + 'a { /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// ``` + #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn add_static_impl_trait_suggestion( &self, @@ -974,6 +975,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty); } + #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #[instrument(skip(self, err), level = "debug")] fn suggest_constrain_dyn_trait_in_impl( @@ -1037,6 +1039,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag); } + #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_move_on_borrowing_closure(&self, diag: &mut Diag<'_>) { let map = self.infcx.tcx.hir(); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5390dda9b21ad..c186d5b284fc4 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -598,6 +598,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// /// In the meantime, though, callsites are required to deal with the "bug" /// locally in whichever way makes the most sense. + #[rustc_lint_diagnostics] #[track_caller] pub fn downgrade_to_delayed_bug(&mut self) { assert!( @@ -631,6 +632,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span_labels, /// Labels all the given spans with the provided label. /// See [`Self::span_label()`] for more information. + #[rustc_lint_diagnostics] pub fn span_labels(&mut self, spans: impl IntoIterator, label: &str) -> &mut Self { for span in spans { self.span_label(span, label.to_string()); @@ -638,6 +640,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } + #[rustc_lint_diagnostics] pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self { let before = self.span.clone(); self.span(after); @@ -653,6 +656,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + #[rustc_lint_diagnostics] pub fn note_expected_found( &mut self, expected_label: &dyn fmt::Display, @@ -663,6 +667,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") } + #[rustc_lint_diagnostics] pub fn note_expected_found_extra( &mut self, expected_label: &dyn fmt::Display, @@ -705,6 +710,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + #[rustc_lint_diagnostics] pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ StringPart::normal(format!("`{name}` from trait: `")), @@ -722,12 +728,14 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } + #[rustc_lint_diagnostics] fn highlighted_note(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } /// This is like [`Diag::note()`], but it's only printed once. + #[rustc_lint_diagnostics] pub fn note_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceNote, msg, MultiSpan::new()); self @@ -748,6 +756,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Prints the span with a note above it. /// This is like [`Diag::note_once()`], but it gets its own span. + #[rustc_lint_diagnostics] pub fn span_note_once>( &mut self, sp: S, @@ -786,12 +795,14 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } /// This is like [`Diag::help()`], but it's only printed once. + #[rustc_lint_diagnostics] pub fn help_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceHelp, msg, MultiSpan::new()); self } /// Add a help message attached to this diagnostic with a customizable highlighted message. + #[rustc_lint_diagnostics] pub fn highlighted_help(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Help, msg, MultiSpan::new()); self @@ -812,12 +823,14 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Disallow attaching suggestions this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods /// (before and after the call to `disable_suggestions`) will be ignored. + #[rustc_lint_diagnostics] pub fn disable_suggestions(&mut self) -> &mut Self { self.suggestions = Err(SuggestionsDisabled); self } /// Helper for pushing to `self.suggestions`, if available (not disable). + #[rustc_lint_diagnostics] fn push_suggestion(&mut self, suggestion: CodeSuggestion) { for subst in &suggestion.substitutions { for part in &subst.parts { @@ -838,6 +851,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_multipart_suggestion, /// Show a suggestion that has multiple parts to it. /// In other words, multiple changes need to be applied as part of this suggestion. + #[rustc_lint_diagnostics] pub fn multipart_suggestion( &mut self, msg: impl Into, @@ -854,6 +868,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. /// In other words, multiple changes need to be applied as part of this suggestion. + #[rustc_lint_diagnostics] pub fn multipart_suggestion_verbose( &mut self, msg: impl Into, @@ -869,6 +884,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } /// [`Diag::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. + #[rustc_lint_diagnostics] pub fn multipart_suggestion_with_style( &mut self, msg: impl Into, @@ -911,6 +927,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// be from the message, showing the span label inline would be visually unpleasant /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't /// improve understandability. + #[rustc_lint_diagnostics] pub fn tool_only_multipart_suggestion( &mut self, msg: impl Into, @@ -943,6 +960,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// * may contain a name of a function, variable, or type, but not whole expressions /// /// See `CodeSuggestion` for more information. + #[rustc_lint_diagnostics] pub fn span_suggestion( &mut self, sp: Span, @@ -961,6 +979,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } /// [`Diag::span_suggestion()`] but you can set the [`SuggestionStyle`]. + #[rustc_lint_diagnostics] pub fn span_suggestion_with_style( &mut self, sp: Span, @@ -986,6 +1005,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span_suggestion_verbose, /// Always show the suggested change. + #[rustc_lint_diagnostics] pub fn span_suggestion_verbose( &mut self, sp: Span, @@ -1006,6 +1026,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span_suggestions, /// Prints out a message with multiple suggested edits of the code. /// See also [`Diag::span_suggestion()`]. + #[rustc_lint_diagnostics] pub fn span_suggestions( &mut self, sp: Span, @@ -1022,6 +1043,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { ) } } + #[rustc_lint_diagnostics] pub fn span_suggestions_with_style( &mut self, sp: Span, @@ -1052,6 +1074,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// Prints out a message with multiple suggested edits of the code, where each edit consists of /// multiple parts. /// See also [`Diag::multipart_suggestion()`]. + #[rustc_lint_diagnostics] pub fn multipart_suggestions( &mut self, msg: impl Into, @@ -1098,6 +1121,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// inline, it will only show the message and not the suggestion. /// /// See `CodeSuggestion` for more information. + #[rustc_lint_diagnostics] pub fn span_suggestion_short( &mut self, sp: Span, @@ -1121,6 +1145,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// be from the message, showing the span label inline would be visually unpleasant /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't /// improve understandability. + #[rustc_lint_diagnostics] pub fn span_suggestion_hidden( &mut self, sp: Span, @@ -1165,6 +1190,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of /// interpolated variables). + #[rustc_lint_diagnostics] pub fn subdiagnostic( &mut self, dcx: &crate::DiagCtxt, @@ -1180,6 +1206,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_span, /// Add a span. + #[rustc_lint_diagnostics] pub fn span(&mut self, sp: impl Into) -> &mut Self { self.span = sp.into(); if let Some(span) = self.span.primary_span() { @@ -1188,6 +1215,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } } + #[rustc_lint_diagnostics] pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self { self.is_lint = Some(IsLint { name, has_future_breakage }); self @@ -1195,6 +1223,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_code, /// Add an error code. + #[rustc_lint_diagnostics] pub fn code(&mut self, code: ErrCode) -> &mut Self { self.code = Some(code); self @@ -1202,6 +1231,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_primary_message, /// Add a primary message. + #[rustc_lint_diagnostics] pub fn primary_message(&mut self, msg: impl Into) -> &mut Self { self.messages[0] = (msg.into(), Style::NoStyle); self @@ -1209,6 +1239,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { with_fn! { with_arg, /// Add an argument. + #[rustc_lint_diagnostics] pub fn arg( &mut self, name: impl Into, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f523db5bddaac..a99db9cb8cc58 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2330,6 +2330,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id); if let Some(hir_sig) = hir_sig { + #[allow(rustc::diagnostic_outside_of_impl)] // FIXME match terr { TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => { if let Some(ty) = hir_sig.decl.inputs.get(idx) { diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 25b5422f3225f..942c5ba8b9aa4 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -168,6 +168,7 @@ pub fn add_feature_diagnostics( /// This variant allows you to control whether it is a library or language feature. /// Almost always, you want to use this for a language feature. If so, prefer /// `add_feature_diagnostics`. +#[allow(rustc::diagnostic_outside_of_impl)] // FIXME pub fn add_feature_diagnostics_for_issue( err: &mut Diag<'_, G>, sess: &Session, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5811ce48eabf8..12654f5b648d5 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -312,6 +312,7 @@ impl Session { ) -> Diag<'a> { let mut err = self.dcx().create_err(err); if err.code.is_none() { + #[allow(rustc::diagnostic_outside_of_impl)] err.code(E0658); } add_feature_diagnostics(&mut err, self, feature); From 9f287dd7b32a77f277720907c76c5de466202ad3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 25 Oct 2023 14:57:25 +1100 Subject: [PATCH 11/12] Change the documented implicit value of `-C instrument-coverage` to `=yes` --- compiler/rustc_interface/src/tests.rs | 2 +- compiler/rustc_session/src/config.rs | 12 ++++----- compiler/rustc_session/src/options.rs | 23 ++++++++-------- compiler/rustc_session/src/session.rs | 2 +- src/doc/rustc/src/instrument-coverage.md | 27 ++++++++++++++++--- .../instrument-coverage/bad-value.bad.stderr | 2 +- .../bad-value.blank.stderr | 2 +- 7 files changed, 44 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5e1fc248b801c..4f15d1c1d3a0b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -590,7 +590,7 @@ fn test_codegen_options_tracking_hash() { tracked!(force_frame_pointers, Some(false)); tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); - tracked!(instrument_coverage, InstrumentCoverage::All); + tracked!(instrument_coverage, InstrumentCoverage::Yes); tracked!(link_dead_code, Some(true)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index dadbcff6f68cd..10243ec3a340e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -146,8 +146,10 @@ pub enum LtoCli { /// unless the function has type parameters. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum InstrumentCoverage { - /// Default `-C instrument-coverage` or `-C instrument-coverage=statement` - All, + /// `-C instrument-coverage=no` (or `off`, `false` etc.) + No, + /// `-C instrument-coverage` or `-C instrument-coverage=yes` + Yes, /// Additionally, instrument branches and output branch coverage. /// `-Zunstable-options -C instrument-coverage=branch` Branch, @@ -155,8 +157,6 @@ pub enum InstrumentCoverage { ExceptUnusedGenerics, /// `-Zunstable-options -C instrument-coverage=except-unused-functions` ExceptUnusedFunctions, - /// `-C instrument-coverage=off` (or `no`, etc.) - Off, } /// Settings for `-Z instrument-xray` flag. @@ -2722,7 +2722,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M // This is what prevents them from being used on stable compilers. match cg.instrument_coverage { // Stable values: - InstrumentCoverage::All | InstrumentCoverage::Off => {} + InstrumentCoverage::Yes | InstrumentCoverage::No => {} // Unstable values: InstrumentCoverage::Branch | InstrumentCoverage::ExceptUnusedFunctions @@ -2736,7 +2736,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } } - if cg.instrument_coverage != InstrumentCoverage::Off { + if cg.instrument_coverage != InstrumentCoverage::No { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_dcx.early_fatal( "option `-C instrument-coverage` is not compatible with either `-C profile-use` \ diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 55e1556cdf8d6..139bc8059d924 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -394,8 +394,7 @@ mod desc { pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; - pub const parse_instrument_coverage: &str = - "`all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off`"; + pub const parse_instrument_coverage: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions`"; pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -918,18 +917,18 @@ mod parse { if v.is_some() { let mut bool_arg = false; if parse_bool(&mut bool_arg, v) { - *slot = if bool_arg { InstrumentCoverage::All } else { InstrumentCoverage::Off }; + *slot = if bool_arg { InstrumentCoverage::Yes } else { InstrumentCoverage::No }; return true; } } let Some(v) = v else { - *slot = InstrumentCoverage::All; + *slot = InstrumentCoverage::Yes; return true; }; *slot = match v { - "all" => InstrumentCoverage::All, + "all" => InstrumentCoverage::Yes, "branch" => InstrumentCoverage::Branch, "except-unused-generics" | "except_unused_generics" => { InstrumentCoverage::ExceptUnusedGenerics @@ -937,7 +936,7 @@ mod parse { "except-unused-functions" | "except_unused_functions" => { InstrumentCoverage::ExceptUnusedFunctions } - "off" | "no" | "n" | "false" | "0" => InstrumentCoverage::Off, + "0" => InstrumentCoverage::No, _ => return false, }; true @@ -1444,15 +1443,15 @@ options! { inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] - instrument_coverage: InstrumentCoverage = (InstrumentCoverage::Off, parse_instrument_coverage, [TRACKED], + instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ implies `-C symbol-mangling-version=v0`. Optional values are: - `=all` (implicit value) - `=branch` - `=except-unused-generics` - `=except-unused-functions` - `=off` (default)"), + `=no` `=n` `=off` `=false` (default) + `=yes` `=y` `=on` `=true` (implicit value) + `=branch` (unstable) + `=except-unused-generics` (unstable) + `=except-unused-functions` (unstable)"), link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 4f4d8fabb7261..71235f5b17140 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -352,7 +352,7 @@ impl Session { } pub fn instrument_coverage(&self) -> bool { - self.opts.cg.instrument_coverage() != InstrumentCoverage::Off + self.opts.cg.instrument_coverage() != InstrumentCoverage::No } pub fn instrument_coverage_branch(&self) -> bool { diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 5b76631833108..2f93252eddcd7 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -331,10 +331,29 @@ $ llvm-cov report \ ## `-C instrument-coverage=` -- `-C instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-C instrument-coverage`, with no value.) -- `-C instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-C instrument-coverage` option.) -- `-Zunstable-options -C instrument-coverage=except-unused-generics`: Instrument all functions except unused generics. -- `-Zunstable-options -C instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions. +- `-C instrument-coverage=no` (or `n`/`off`/`false`): + Don't enable coverage instrumentation. No functions will be instrumented for coverage. + - This is the same as not using the `-C instrument-coverage` flag at all. +- `-C instrument-coverage=yes` (or `y`/`on`/`true`): + Enable coverage instrumentation with the default behaviour. + Currently this instruments all functions, including unused functions and unused generics. + - This is the same as `-C instrument-coverage` with no value. + +### Other values + +- `-C instrument-coverage=all`: + Currently an alias for `yes`, but may behave differently in the future if + more fine-grained coverage options are added. + Using this value is currently not recommended. + +### Unstable values + +- `-Z unstable-options -C instrument-coverage=branch`: + Placeholder for potential branch coverage support in the future. +- `-Z unstable-options -C instrument-coverage=except-unused-generics`: + Instrument all functions except unused generics. +- `-Z unstable-options -C instrument-coverage=except-unused-functions`: + Instrument only used (called) functions and instantiated generic functions. ## Other references diff --git a/tests/ui/instrument-coverage/bad-value.bad.stderr b/tests/ui/instrument-coverage/bad-value.bad.stderr index b867d169dae5b..0411a1f98a3ee 100644 --- a/tests/ui/instrument-coverage/bad-value.bad.stderr +++ b/tests/ui/instrument-coverage/bad-value.bad.stderr @@ -1,2 +1,2 @@ -error: incorrect value `bad-value` for codegen option `instrument-coverage` - `all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off` was expected +error: incorrect value `bad-value` for codegen option `instrument-coverage` - either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions` was expected diff --git a/tests/ui/instrument-coverage/bad-value.blank.stderr b/tests/ui/instrument-coverage/bad-value.blank.stderr index e7122fb61cdfc..b3a8e7cf94784 100644 --- a/tests/ui/instrument-coverage/bad-value.blank.stderr +++ b/tests/ui/instrument-coverage/bad-value.blank.stderr @@ -1,2 +1,2 @@ -error: incorrect value `` for codegen option `instrument-coverage` - `all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off` was expected +error: incorrect value `` for codegen option `instrument-coverage` - either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions` was expected From 73030143818b99f0180a259b161cacd175b7d775 Mon Sep 17 00:00:00 2001 From: bohan Date: Sat, 17 Feb 2024 00:55:35 +0800 Subject: [PATCH 12/12] avoid overlapping privacy suggestion for single nested imports --- compiler/rustc_resolve/src/diagnostics.rs | 62 ++++++++++++------- compiler/rustc_resolve/src/ident.rs | 3 +- compiler/rustc_resolve/src/imports.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 2 + tests/ui/imports/append-import-suggestion.rs | 16 +++++ .../imports/append-import-suggestion.stderr | 21 +++++++ 6 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 tests/ui/imports/append-import-suggestion.rs create mode 100644 tests/ui/imports/append-import-suggestion.stderr diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 833ebfa03e5fc..0bb2a69ae9926 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1736,7 +1736,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'a>) { - let PrivacyError { ident, binding, outermost_res, parent_scope, dedup_span } = + let PrivacyError { ident, binding, outermost_res, parent_scope, single_nested, dedup_span } = *privacy_error; let res = binding.res(); @@ -1775,7 +1775,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import_suggestions, Instead::Yes, FoundUse::Yes, - DiagMode::Import, + DiagMode::Import { append: single_nested }, vec![], "", ); @@ -2701,7 +2701,11 @@ pub(crate) enum DiagMode { /// The binding is part of a pattern Pattern, /// The binding is part of a use statement - Import, + Import { + /// `true` mean add the tips afterward for case `use a::{b,c}`, + /// rather than replacing within. + append: bool, + }, } pub(crate) fn import_candidates( @@ -2726,6 +2730,8 @@ pub(crate) fn import_candidates( ); } +type PathString<'a> = (String, &'a str, Option, &'a Option, bool); + /// When an entity with a given name is not available in scope, we search for /// entities with that name in all crates. This method allows outputting the /// results of this search in a programmer-friendly way. If any entities are @@ -2746,10 +2752,8 @@ fn show_candidates( return false; } - let mut accessible_path_strings: Vec<(String, &str, Option, &Option, bool)> = - Vec::new(); - let mut inaccessible_path_strings: Vec<(String, &str, Option, &Option, bool)> = - Vec::new(); + let mut accessible_path_strings: Vec> = Vec::new(); + let mut inaccessible_path_strings: Vec> = Vec::new(); candidates.iter().for_each(|c| { if c.accessible { @@ -2811,6 +2815,15 @@ fn show_candidates( err.note(note.clone()); } + let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { + msg.push(':'); + + for candidate in accessible_path_strings { + msg.push('\n'); + msg.push_str(&candidate.0); + } + }; + if let Some(span) = use_placement_span { let (add_use, trailing) = match mode { DiagMode::Pattern => { @@ -2822,7 +2835,7 @@ fn show_candidates( ); return true; } - DiagMode::Import => ("", ""), + DiagMode::Import { .. } => ("", ""), DiagMode::Normal => ("use ", ";\n"), }; for candidate in &mut accessible_path_strings { @@ -2839,13 +2852,22 @@ fn show_candidates( format!("{add_use}{}{append}{trailing}{additional_newline}", &candidate.0); } - err.span_suggestions_with_style( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, - ); + match mode { + DiagMode::Import { append: true, .. } => { + append_candidates(&mut msg, accessible_path_strings); + err.span_help(span, msg); + } + _ => { + err.span_suggestions_with_style( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + } + } + if let [first, .., last] = &path[..] { let sp = first.ident.span.until(last.ident.span); // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). @@ -2860,17 +2882,11 @@ fn show_candidates( } } } else { - msg.push(':'); - - for candidate in accessible_path_strings { - msg.push('\n'); - msg.push_str(&candidate.0); - } - + append_candidates(&mut msg, accessible_path_strings); err.help(msg); } true - } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagMode::Import)) { + } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagMode::Import { .. })) { let prefix = if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; if let [(name, descr, def_id, note, _)] = &inaccessible_path_strings[..] { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index a9fdddbfab90f..6518d9735ae52 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -868,7 +868,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .into_iter() .find_map(|binding| if binding == ignore_binding { None } else { binding }); - if let Some(Finalize { path_span, report_private, used, .. }) = finalize { + if let Some(Finalize { path_span, report_private, used, root_span, .. }) = finalize { let Some(binding) = binding else { return Err((Determined, Weak::No)); }; @@ -881,6 +881,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { dedup_span: path_span, outermost_res: None, parent_scope: *parent_scope, + single_nested: path_span != root_span, }); } else { return Err((Determined, Weak::No)); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 6647a9a279285..9e5b2fe094f4c 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -715,7 +715,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &mut diag, Some(err.span), candidates, - DiagMode::Import, + DiagMode::Import { append: false }, (source != target) .then(|| format!(" as {target}")) .as_deref() diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0434ff171934e..5de147c2b2444 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -729,6 +729,8 @@ struct PrivacyError<'a> { dedup_span: Span, outermost_res: Option<(Res, Ident)>, parent_scope: ParentScope<'a>, + /// Is the format `use a::{b,c}`? + single_nested: bool, } #[derive(Debug)] diff --git a/tests/ui/imports/append-import-suggestion.rs b/tests/ui/imports/append-import-suggestion.rs new file mode 100644 index 0000000000000..6b75804b86fcb --- /dev/null +++ b/tests/ui/imports/append-import-suggestion.rs @@ -0,0 +1,16 @@ +// https://github.com/rust-lang/rust/issues/114884 + +mod mod1 { + pub trait TraitA {} +} + +mod mod2 { + mod sub_mod { + use super::super::mod1::TraitA; + } +} + +use mod2::{sub_mod::TraitA}; +//~^ ERROR: module `sub_mod` is private + +fn main() {} diff --git a/tests/ui/imports/append-import-suggestion.stderr b/tests/ui/imports/append-import-suggestion.stderr new file mode 100644 index 0000000000000..6d7b657f3d128 --- /dev/null +++ b/tests/ui/imports/append-import-suggestion.stderr @@ -0,0 +1,21 @@ +error[E0603]: module `sub_mod` is private + --> $DIR/append-import-suggestion.rs:13:12 + | +LL | use mod2::{sub_mod::TraitA}; + | ^^^^^^^ private module + | +help: consider importing this trait instead: + mod1::TraitA + --> $DIR/append-import-suggestion.rs:13:12 + | +LL | use mod2::{sub_mod::TraitA}; + | ^^^^^^^^^^^^^^^ +note: the module `sub_mod` is defined here + --> $DIR/append-import-suggestion.rs:8:5 + | +LL | mod sub_mod { + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0603`.