From b711f5689fd8314734fb32431b5d4f9dea0729e0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Mar 2026 10:06:21 +1100 Subject: [PATCH 01/10] Invert dependency between `rustc_errors` and `rustc_abi`. Currently, `rustc_errors` depends on `rustc_abi`, which depends on `rustc_error_messages`. This is a bit odd. `rustc_errors` depends on `rustc_abi` for a single reason: `rustc_abi` defines a type `TargetDataLayoutErrors` and `rustc_errors` impls `Diagnostic` for that type. We can get a more natural relationship by inverting the dependency, moving the `Diagnostic` trait upstream. Then `rustc_abi` defines `TargetDataLayoutErrors` and also impls `Diagnostic` for it. `rustc_errors` is already pretty far upstream in the crate graph, it doesn't hurt to push it a little further because errors are a very low-level concept. --- Cargo.lock | 2 +- compiler/rustc_abi/Cargo.toml | 6 ++- compiler/rustc_abi/src/lib.rs | 47 +++++++++++++++++ compiler/rustc_errors/Cargo.toml | 1 - compiler/rustc_errors/src/diagnostic_impls.rs | 52 +------------------ 5 files changed, 53 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4c1a02c018a..4426ad75eee9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3504,6 +3504,7 @@ dependencies = [ "rand_xoshiro", "rustc_data_structures", "rustc_error_messages", + "rustc_errors", "rustc_hashes", "rustc_index", "rustc_macros", @@ -3908,7 +3909,6 @@ dependencies = [ "anstream", "anstyle", "derive_setters", - "rustc_abi", "rustc_ast", "rustc_data_structures", "rustc_error_codes", diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 83d96d8d04da..13e0bd8703d9 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -10,6 +10,7 @@ rand = { version = "0.9.0", default-features = false, optional = true } rand_xoshiro = { version = "0.7.0", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_error_messages = { path = "../rustc_error_messages", optional = true } +rustc_errors = { path = "../rustc_errors", optional = true } rustc_hashes = { path = "../rustc_hashes" } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } @@ -21,11 +22,12 @@ tracing = "0.1" [features] # tidy-alphabetical-start default = ["nightly", "randomize"] -# rust-analyzer depends on this crate and we therefore require it to built on a stable toolchain -# without depending on rustc_data_structures, rustc_macros and rustc_serialize +# rust-analyzer depends on this crate and we therefore require it to build on a stable toolchain +# without depending on the rustc_* crates in the following list. nightly = [ "dep:rustc_data_structures", "dep:rustc_error_messages", + "dep:rustc_errors", "dep:rustc_macros", "dep:rustc_serialize", "dep:rustc_span", diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 253dff6f8e75..eca49bb71dd9 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -46,6 +46,8 @@ use bitflags::bitflags; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::StableOrd; +#[cfg(feature = "nightly")] +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, msg}; use rustc_hashes::Hash64; use rustc_index::{Idx, IndexSlice, IndexVec}; #[cfg(feature = "nightly")] @@ -349,6 +351,51 @@ pub enum TargetDataLayoutErrors<'a> { UnknownPointerSpecification { err: String }, } +#[cfg(feature = "nightly")] +impl Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + match self { + TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { + Diag::new(dcx, level, msg!("invalid address space `{$addr_space}` for `{$cause}` in \"data-layout\": {$err}")) + .with_arg("addr_space", addr_space) + .with_arg("cause", cause) + .with_arg("err", err) + } + TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => { + Diag::new(dcx, level, msg!("invalid {$kind} `{$bit}` for `{$cause}` in \"data-layout\": {$err}")) + .with_arg("kind", kind) + .with_arg("bit", bit) + .with_arg("cause", cause) + .with_arg("err", err) + } + TargetDataLayoutErrors::MissingAlignment { cause } => { + Diag::new(dcx, level, msg!("missing alignment for `{$cause}` in \"data-layout\"")) + .with_arg("cause", cause) + } + TargetDataLayoutErrors::InvalidAlignment { cause, err } => { + Diag::new(dcx, level, msg!("invalid alignment for `{$cause}` in \"data-layout\": {$err}")) + .with_arg("cause", cause) + .with_arg("err", err.to_string()) + } + TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { + Diag::new(dcx, level, msg!("inconsistent target specification: \"data-layout\" claims architecture is {$dl}-endian, while \"target-endian\" is `{$target}`")) + .with_arg("dl", dl).with_arg("target", target) + } + TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => { + Diag::new(dcx, level, msg!("inconsistent target specification: \"data-layout\" claims pointers are {$pointer_size}-bit, while \"target-pointer-width\" is `{$target}`")) + .with_arg("pointer_size", pointer_size).with_arg("target", target) + } + TargetDataLayoutErrors::InvalidBitsSize { err } => { + Diag::new(dcx, level, msg!("{$err}")).with_arg("err", err) + } + TargetDataLayoutErrors::UnknownPointerSpecification { err } => { + Diag::new(dcx, level, msg!("unknown pointer specification `{$err}` in datalayout string")) + .with_arg("err", err) + } + } + } +} + impl TargetDataLayout { /// Parse data layout from an /// [llvm data layout string](https://llvm.org/docs/LangRef.html#data-layout) diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index a81fc496c828..58303c83e7cd 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -9,7 +9,6 @@ annotate-snippets = { version = "0.12.10", features = ["simd"] } anstream = "0.6.20" anstyle = "1.0.13" derive_setters = "0.1.6" -rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_codes = { path = "../rustc_error_codes" } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index e50cbbbf06e0..ba7569c51a07 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,12 +1,11 @@ use std::borrow::Cow; -use rustc_abi::TargetDataLayoutErrors; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::Subdiagnostic; use rustc_span::{Span, Symbol}; use crate::diagnostic::DiagLocation; -use crate::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, msg}; +use crate::{Diag, EmissionGuarantee, Subdiagnostic}; impl IntoDiagArg for DiagLocation { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { @@ -37,55 +36,6 @@ fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { } } -impl Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - match self { - TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { - Diag::new(dcx, level, msg!("invalid address space `{$addr_space}` for `{$cause}` in \"data-layout\": {$err}")) - .with_arg("addr_space", addr_space) - .with_arg("cause", cause) - .with_arg("err", err) - } - TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => { - Diag::new(dcx, level, msg!("invalid {$kind} `{$bit}` for `{$cause}` in \"data-layout\": {$err}")) - .with_arg("kind", kind) - .with_arg("bit", bit) - .with_arg("cause", cause) - .with_arg("err", err) - } - TargetDataLayoutErrors::MissingAlignment { cause } => { - Diag::new(dcx, level, msg!("missing alignment for `{$cause}` in \"data-layout\"")) - .with_arg("cause", cause) - } - TargetDataLayoutErrors::InvalidAlignment { cause, err } => { - Diag::new(dcx, level, msg!( - "invalid alignment for `{$cause}` in \"data-layout\": {$err}" - )) - .with_arg("cause", cause) - .with_arg("err", err.to_string()) - } - TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { - Diag::new(dcx, level, msg!( - "inconsistent target specification: \"data-layout\" claims architecture is {$dl}-endian, while \"target-endian\" is `{$target}`" - )) - .with_arg("dl", dl).with_arg("target", target) - } - TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => { - Diag::new(dcx, level, msg!( - "inconsistent target specification: \"data-layout\" claims pointers are {$pointer_size}-bit, while \"target-pointer-width\" is `{$target}`" - )).with_arg("pointer_size", pointer_size).with_arg("target", target) - } - TargetDataLayoutErrors::InvalidBitsSize { err } => { - Diag::new(dcx, level, msg!("{$err}")).with_arg("err", err) - } - TargetDataLayoutErrors::UnknownPointerSpecification { err } => { - Diag::new(dcx, level, msg!("unknown pointer specification `{$err}` in datalayout string")) - .with_arg("err", err) - } - } - } -} - /// Utility struct used to apply a single label while highlighting multiple spans pub struct SingleLabelManySpans { pub spans: Vec, From 0db4e3a883665b44b26f5364dc26d033a2b111da Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Mar 2026 10:24:08 +1100 Subject: [PATCH 02/10] De-pluralize the names of two error enums. Error enum names should not be plural. Even though there are multiple possible errors, each instance of an error enum describes a single error. There are dozens of singular error enum names, and only two plural error enum names. This commit makes them both singular. --- compiler/rustc_abi/src/lib.rs | 34 +++++++++++++-------------- compiler/rustc_codegen_ssa/src/lib.rs | 14 +++++------ compiler/rustc_driver_impl/src/lib.rs | 14 +++++------ compiler/rustc_target/src/spec/mod.rs | 10 ++++---- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index eca49bb71dd9..0b5513e54557 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -340,7 +340,7 @@ fn default() -> TargetDataLayout { } } -pub enum TargetDataLayoutErrors<'a> { +pub enum TargetDataLayoutError<'a> { InvalidAddressSpace { addr_space: &'a str, cause: &'a str, err: ParseIntError }, InvalidBits { kind: &'a str, bit: &'a str, cause: &'a str, err: ParseIntError }, MissingAlignment { cause: &'a str }, @@ -352,43 +352,43 @@ pub enum TargetDataLayoutErrors<'a> { } #[cfg(feature = "nightly")] -impl Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { +impl Diagnostic<'_, G> for TargetDataLayoutError<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { match self { - TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { + TargetDataLayoutError::InvalidAddressSpace { addr_space, err, cause } => { Diag::new(dcx, level, msg!("invalid address space `{$addr_space}` for `{$cause}` in \"data-layout\": {$err}")) .with_arg("addr_space", addr_space) .with_arg("cause", cause) .with_arg("err", err) } - TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => { + TargetDataLayoutError::InvalidBits { kind, bit, cause, err } => { Diag::new(dcx, level, msg!("invalid {$kind} `{$bit}` for `{$cause}` in \"data-layout\": {$err}")) .with_arg("kind", kind) .with_arg("bit", bit) .with_arg("cause", cause) .with_arg("err", err) } - TargetDataLayoutErrors::MissingAlignment { cause } => { + TargetDataLayoutError::MissingAlignment { cause } => { Diag::new(dcx, level, msg!("missing alignment for `{$cause}` in \"data-layout\"")) .with_arg("cause", cause) } - TargetDataLayoutErrors::InvalidAlignment { cause, err } => { + TargetDataLayoutError::InvalidAlignment { cause, err } => { Diag::new(dcx, level, msg!("invalid alignment for `{$cause}` in \"data-layout\": {$err}")) .with_arg("cause", cause) .with_arg("err", err.to_string()) } - TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { + TargetDataLayoutError::InconsistentTargetArchitecture { dl, target } => { Diag::new(dcx, level, msg!("inconsistent target specification: \"data-layout\" claims architecture is {$dl}-endian, while \"target-endian\" is `{$target}`")) .with_arg("dl", dl).with_arg("target", target) } - TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => { + TargetDataLayoutError::InconsistentTargetPointerWidth { pointer_size, target } => { Diag::new(dcx, level, msg!("inconsistent target specification: \"data-layout\" claims pointers are {$pointer_size}-bit, while \"target-pointer-width\" is `{$target}`")) .with_arg("pointer_size", pointer_size).with_arg("target", target) } - TargetDataLayoutErrors::InvalidBitsSize { err } => { + TargetDataLayoutError::InvalidBitsSize { err } => { Diag::new(dcx, level, msg!("{$err}")).with_arg("err", err) } - TargetDataLayoutErrors::UnknownPointerSpecification { err } => { + TargetDataLayoutError::UnknownPointerSpecification { err } => { Diag::new(dcx, level, msg!("unknown pointer specification `{$err}` in datalayout string")) .with_arg("err", err) } @@ -405,17 +405,17 @@ impl TargetDataLayout { pub fn parse_from_llvm_datalayout_string<'a>( input: &'a str, default_address_space: AddressSpace, - ) -> Result> { + ) -> Result> { // Parse an address space index from a string. let parse_address_space = |s: &'a str, cause: &'a str| { s.parse::().map(AddressSpace).map_err(|err| { - TargetDataLayoutErrors::InvalidAddressSpace { addr_space: s, cause, err } + TargetDataLayoutError::InvalidAddressSpace { addr_space: s, cause, err } }) }; // Parse a bit count from a string. let parse_bits = |s: &'a str, kind: &'a str, cause: &'a str| { - s.parse::().map_err(|err| TargetDataLayoutErrors::InvalidBits { + s.parse::().map_err(|err| TargetDataLayoutError::InvalidBits { kind, bit: s, cause, @@ -431,7 +431,7 @@ pub fn parse_from_llvm_datalayout_string<'a>( let parse_align_str = |s: &'a str, cause: &'a str| { let align_from_bits = |bits| { Align::from_bits(bits) - .map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err }) + .map_err(|err| TargetDataLayoutError::InvalidAlignment { cause, err }) }; let abi = parse_bits(s, "alignment", cause)?; Ok(align_from_bits(abi)?) @@ -441,7 +441,7 @@ pub fn parse_from_llvm_datalayout_string<'a>( // ignoring the secondary alignment specifications. let parse_align_seq = |s: &[&'a str], cause: &'a str| { if s.is_empty() { - return Err(TargetDataLayoutErrors::MissingAlignment { cause }); + return Err(TargetDataLayoutError::MissingAlignment { cause }); } parse_align_str(s[0], cause) }; @@ -479,7 +479,7 @@ pub fn parse_from_llvm_datalayout_string<'a>( // However, we currently don't take into account further specifications: // an error is emitted instead. if p.starts_with(char::is_alphabetic) { - return Err(TargetDataLayoutErrors::UnknownPointerSpecification { + return Err(TargetDataLayoutError::UnknownPointerSpecification { err: p.to_string(), }); } @@ -524,7 +524,7 @@ pub fn parse_from_llvm_datalayout_string<'a>( // However, we currently don't take into account further specifications: // an error is emitted instead. if p.starts_with(char::is_alphabetic) { - return Err(TargetDataLayoutErrors::UnknownPointerSpecification { + return Err(TargetDataLayoutError::UnknownPointerSpecification { err: p.to_string(), }); } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index ced4b59c4f0c..1c266382d027 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -256,7 +256,7 @@ pub struct CompiledModules { pub allocator_module: Option, } -pub enum CodegenErrors { +pub enum CodegenError { WrongFileType, EmptyVersionNumber, EncodingVersionMismatch { version_array: String, rlink_version: u32 }, @@ -317,32 +317,32 @@ pub fn serialize_rlink( pub fn deserialize_rlink( sess: &Session, data: Vec, - ) -> Result<(Self, CrateInfo, EncodedMetadata, OutputFilenames), CodegenErrors> { + ) -> Result<(Self, CrateInfo, EncodedMetadata, OutputFilenames), CodegenError> { // The Decodable machinery is not used here because it panics if the input data is invalid // and because its internal representation may change. if !data.starts_with(RLINK_MAGIC) { - return Err(CodegenErrors::WrongFileType); + return Err(CodegenError::WrongFileType); } let data = &data[RLINK_MAGIC.len()..]; if data.len() < 4 { - return Err(CodegenErrors::EmptyVersionNumber); + return Err(CodegenError::EmptyVersionNumber); } let mut version_array: [u8; 4] = Default::default(); version_array.copy_from_slice(&data[..4]); if u32::from_be_bytes(version_array) != RLINK_VERSION { - return Err(CodegenErrors::EncodingVersionMismatch { + return Err(CodegenError::EncodingVersionMismatch { version_array: String::from_utf8_lossy(&version_array).to_string(), rlink_version: RLINK_VERSION, }); } let Ok(mut decoder) = MemDecoder::new(&data[4..], 0) else { - return Err(CodegenErrors::CorruptFile); + return Err(CodegenError::CorruptFile); }; let rustc_version = decoder.read_str(); if rustc_version != sess.cfg_version { - return Err(CodegenErrors::RustcVersionMismatch { + return Err(CodegenError::RustcVersionMismatch { rustc_version: rustc_version.to_string(), }); } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index ff7e170543be..bb9c63d22432 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -28,7 +28,7 @@ use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CodegenErrors, CompiledModules}; +use rustc_codegen_ssa::{CodegenError, CompiledModules}; use rustc_data_structures::profiling::{ TimePassesFormat, get_resident_set_size, print_time_passes_entry, }; @@ -567,23 +567,21 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { } Err(err) => { match err { - CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), - CodegenErrors::EmptyVersionNumber => { - dcx.emit_fatal(RLinkEmptyVersionNumber) - } - CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => { + CodegenError::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), + CodegenError::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber), + CodegenError::EncodingVersionMismatch { version_array, rlink_version } => { dcx.emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version, }) } - CodegenErrors::RustcVersionMismatch { rustc_version } => { + CodegenError::RustcVersionMismatch { rustc_version } => { dcx.emit_fatal(RLinkRustcVersionMismatch { rustc_version, current_version: sess.cfg_version, }) } - CodegenErrors::CorruptFile => { + CodegenError::CorruptFile => { dcx.emit_fatal(RlinkCorruptFile { file }); } }; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 14746da57c47..76186eeab89c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -47,7 +47,7 @@ use std::{fmt, io}; use rustc_abi::{ - Align, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors, + Align, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutError, }; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_error_messages::{DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; @@ -2184,7 +2184,7 @@ pub struct TargetMetadata { } impl Target { - pub fn parse_data_layout(&self) -> Result> { + pub fn parse_data_layout(&self) -> Result> { let mut dl = TargetDataLayout::parse_from_llvm_datalayout_string( &self.data_layout, self.options.default_address_space, @@ -2192,7 +2192,7 @@ pub fn parse_data_layout(&self) -> Result Result Result Date: Sun, 3 Aug 2025 21:57:44 +0200 Subject: [PATCH 03/10] Add suggestion to `.to_owned()` used on `Cow` when borrowing --- .../src/diagnostics/conflict_errors.rs | 18 ++++++++++++++++++ tests/ui/errors/cow-to-owned.rs | 7 +++++++ tests/ui/errors/cow-to-owned.stderr | 17 +++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests/ui/errors/cow-to-owned.rs create mode 100644 tests/ui/errors/cow-to-owned.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index cddb37c7d816..e438da1f629a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3530,6 +3530,24 @@ fn try_report_cannot_return_reference_to_local( Applicability::MaybeIncorrect, ); } + + if let Some(cow) = tcx.get_diagnostic_item(sym::Cow) + && let ty::Adt(adtdef, _) = return_ty.kind() + && adtdef.did() == cow + { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) { + if let Some(pos) = snippet.rfind(".to_owned") { + let byte_pos = BytePos(pos as u32 + 1u32); + let to_owned_span = return_span.with_hi(return_span.lo() + byte_pos); + err.span_suggestion_short( + to_owned_span.shrink_to_hi(), + "try using `.into_owned()` if you meant to convert a `Cow<'_, T>` to an owned `T`", + "in", + Applicability::MaybeIncorrect, + ); + } + } + } } Err(err) diff --git a/tests/ui/errors/cow-to-owned.rs b/tests/ui/errors/cow-to-owned.rs new file mode 100644 index 000000000000..03e828bf3ebf --- /dev/null +++ b/tests/ui/errors/cow-to-owned.rs @@ -0,0 +1,7 @@ +// issue #144792 + +fn main() { + _ = std::env::var_os("RUST_LOG").map_or("warn".into(), |x| x.to_string_lossy().to_owned()); + //~^ ERROR cannot return value referencing function parameter + //~| HELP try using `.into_owned()` +} diff --git a/tests/ui/errors/cow-to-owned.stderr b/tests/ui/errors/cow-to-owned.stderr new file mode 100644 index 000000000000..02bcebd4faf5 --- /dev/null +++ b/tests/ui/errors/cow-to-owned.stderr @@ -0,0 +1,17 @@ +error[E0515]: cannot return value referencing function parameter `x` + --> $DIR/cow-to-owned.rs:4:64 + | +LL | _ = std::env::var_os("RUST_LOG").map_or("warn".into(), |x| x.to_string_lossy().to_owned()); + | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `x` is borrowed here + | +help: try using `.into_owned()` if you meant to convert a `Cow<'_, T>` to an owned `T` + | +LL | _ = std::env::var_os("RUST_LOG").map_or("warn".into(), |x| x.to_string_lossy().into_owned()); + | ++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0515`. From 41f7c2d4e2556fe11d0a7cd487d5e190e20c7200 Mon Sep 17 00:00:00 2001 From: may Date: Tue, 31 Mar 2026 19:03:11 +0200 Subject: [PATCH 04/10] apply review suggestions and move Cow sym to rustc --- .../src/diagnostics/conflict_errors.rs | 6 +-- compiler/rustc_span/src/symbol.rs | 1 + src/tools/clippy/clippy_utils/src/sym.rs | 1 - tests/ui/errors/cow-to-owned.rs | 7 --- tests/ui/errors/cow-to-owned.stderr | 17 -------- .../suggestions/cow-into-owned-suggestion.rs | 23 ++++++++++ .../cow-into-owned-suggestion.stderr | 43 +++++++++++++++++++ 7 files changed, 70 insertions(+), 28 deletions(-) delete mode 100644 tests/ui/errors/cow-to-owned.rs delete mode 100644 tests/ui/errors/cow-to-owned.stderr create mode 100644 tests/ui/suggestions/cow-into-owned-suggestion.rs create mode 100644 tests/ui/suggestions/cow-into-owned-suggestion.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index e438da1f629a..4fbb172f0f94 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3531,9 +3531,9 @@ fn try_report_cannot_return_reference_to_local( ); } - if let Some(cow) = tcx.get_diagnostic_item(sym::Cow) - && let ty::Adt(adtdef, _) = return_ty.kind() - && adtdef.did() == cow + if let Some(cow_did) = tcx.get_diagnostic_item(sym::Cow) + && let ty::Adt(adt_def, _) = return_ty.kind() + && adt_def.did() == cow_did { if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) { if let Some(pos) = snippet.rfind(".to_owned") { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7b359dcd6b25..47cf9bdffa1b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -196,6 +196,7 @@ Continue, ControlFlow, Copy, + Cow, Debug, Default, Deref, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 7d579d85d808..71e62f047463 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -50,7 +50,6 @@ macro_rules! generate { Cargo_toml: "Cargo.toml", Child, Command, - Cow, Current, DOUBLE_QUOTE: "\"", DebugStruct, diff --git a/tests/ui/errors/cow-to-owned.rs b/tests/ui/errors/cow-to-owned.rs deleted file mode 100644 index 03e828bf3ebf..000000000000 --- a/tests/ui/errors/cow-to-owned.rs +++ /dev/null @@ -1,7 +0,0 @@ -// issue #144792 - -fn main() { - _ = std::env::var_os("RUST_LOG").map_or("warn".into(), |x| x.to_string_lossy().to_owned()); - //~^ ERROR cannot return value referencing function parameter - //~| HELP try using `.into_owned()` -} diff --git a/tests/ui/errors/cow-to-owned.stderr b/tests/ui/errors/cow-to-owned.stderr deleted file mode 100644 index 02bcebd4faf5..000000000000 --- a/tests/ui/errors/cow-to-owned.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0515]: cannot return value referencing function parameter `x` - --> $DIR/cow-to-owned.rs:4:64 - | -LL | _ = std::env::var_os("RUST_LOG").map_or("warn".into(), |x| x.to_string_lossy().to_owned()); - | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | returns a value referencing data owned by the current function - | `x` is borrowed here - | -help: try using `.into_owned()` if you meant to convert a `Cow<'_, T>` to an owned `T` - | -LL | _ = std::env::var_os("RUST_LOG").map_or("warn".into(), |x| x.to_string_lossy().into_owned()); - | ++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/suggestions/cow-into-owned-suggestion.rs b/tests/ui/suggestions/cow-into-owned-suggestion.rs new file mode 100644 index 000000000000..8d1019431df0 --- /dev/null +++ b/tests/ui/suggestions/cow-into-owned-suggestion.rs @@ -0,0 +1,23 @@ +//! Regression test for: https://github.com/rust-lang/rust/issues/144792 + +fn main() { + let _ = || { + let os_string = std::ffi::OsString::from("test"); + os_string.to_string_lossy().to_owned() + //~^ ERROR: cannot return value referencing local variable `os_string` [E0515] + }; + + let _ = || { + let s = "hello".to_owned(); + let cow = std::borrow::Cow::from(&s); + cow.to_owned() + //~^ ERROR: cannot return value referencing local variable `s` [E0515] + }; + + let _ = || { + let bytes = b"hello".to_owned(); + let cow = std::borrow::Cow::from(&bytes[..]); + cow.to_owned() + //~^ ERROR: cannot return value referencing local variable `bytes` [E0515] + }; +} diff --git a/tests/ui/suggestions/cow-into-owned-suggestion.stderr b/tests/ui/suggestions/cow-into-owned-suggestion.stderr new file mode 100644 index 000000000000..8d30af958913 --- /dev/null +++ b/tests/ui/suggestions/cow-into-owned-suggestion.stderr @@ -0,0 +1,43 @@ +error[E0515]: cannot return value referencing local variable `os_string` + --> $DIR/cow-into-owned-suggestion.rs:6:9 + | +LL | os_string.to_string_lossy().to_owned() + | ---------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `os_string` is borrowed here + | +help: try using `.into_owned()` if you meant to convert a `Cow<'_, T>` to an owned `T` + | +LL | os_string.to_string_lossy().into_owned() + | ++ + +error[E0515]: cannot return value referencing local variable `s` + --> $DIR/cow-into-owned-suggestion.rs:13:9 + | +LL | let cow = std::borrow::Cow::from(&s); + | -- `s` is borrowed here +LL | cow.to_owned() + | ^^^^^^^^^^^^^^ returns a value referencing data owned by the current function + | +help: try using `.into_owned()` if you meant to convert a `Cow<'_, T>` to an owned `T` + | +LL | cow.into_owned() + | ++ + +error[E0515]: cannot return value referencing local variable `bytes` + --> $DIR/cow-into-owned-suggestion.rs:20:9 + | +LL | let cow = std::borrow::Cow::from(&bytes[..]); + | ----- `bytes` is borrowed here +LL | cow.to_owned() + | ^^^^^^^^^^^^^^ returns a value referencing data owned by the current function + | +help: try using `.into_owned()` if you meant to convert a `Cow<'_, T>` to an owned `T` + | +LL | cow.into_owned() + | ++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0515`. From 85633125c7616d00e3c3a33eb356e26540b8a438 Mon Sep 17 00:00:00 2001 From: Deepesh Varatharajan Date: Wed, 8 Apr 2026 08:46:43 -0700 Subject: [PATCH 05/10] compiletest: pass -Zunstable-options for unpretty and no-codegen paths Unconditionally pass -Zunstable-options in the `unpretty` and `-Zno-codegen` (typecheck) paths in compiletest. This ensures custom targets resolved via RUST_TARGET_PATH work consistently. This is primarily needed when using non-built-in targets without a .json extension. Signed-off-by: Deepesh Varatharajan --- src/tools/compiletest/src/runtest.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 5fc3dbbfbc54..6b5147cea662 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -452,6 +452,7 @@ fn print_source(&self, read_from: ReadFrom, pretty_type: &str) -> ProcRes { rustc .arg(input) .args(&["-Z", &format!("unpretty={}", pretty_type)]) + .arg("-Zunstable-options") .args(&["--target", &self.config.target]) .arg("-L") .arg(&aux_dir) @@ -557,6 +558,7 @@ fn typecheck_source(&self, src: String) -> ProcRes { rustc .arg("-") .arg("-Zno-codegen") + .arg("-Zunstable-options") .arg("--out-dir") .arg(&out_dir) .arg(&format!("--target={}", target)) From 6e5fc9075cf6e70f4a211e42d5670fa723457660 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Sun, 9 Feb 2025 18:29:32 +0100 Subject: [PATCH 06/10] Introduce a `#[diagnostic::on_unknown_item]` attribute This PR introduces a `#[diagnostic::on_unknown_item]` attribute that allows crate authors to customize the error messages emitted by unresolved imports. The main usecase for this is using this attribute as part of a proc macro that expects a certain external module structure to exist or certain dependencies to be there. For me personally the motivating use-case are several derives in diesel, that expect to refer to a `tabe` module. That is done either implicitly (via the name of the type with the derive) or explicitly by the user. This attribute would allow us to improve the error message in both cases: * For the implicit case we could explicity call out our assumptions (turning the name into lower case, adding an `s` in the end) + point to the explicit variant as alternative * For the explicit variant we would add additional notes to tell the user why this is happening and what they should look for to fix the problem (be more explicit about certain diesel specific assumptions of the module structure) I assume that similar use-cases exist for other proc-macros as well, therefore I decided to put in the work implementing this new attribute. I would also assume that this is likely not useful for std-lib internal usage. --- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- .../src/attributes/diagnostic/mod.rs | 19 +++- .../attributes/diagnostic/on_unknown_item.rs | 70 ++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 + compiler/rustc_attr_parsing/src/interface.rs | 10 +- .../src/deriving/generic/mod.rs | 2 +- .../src/proc_macro_harness.rs | 2 +- compiler/rustc_builtin_macros/src/test.rs | 2 +- .../rustc_builtin_macros/src/test_harness.rs | 2 +- compiler/rustc_expand/src/config.rs | 2 +- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 8 ++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_interface/src/passes.rs | 6 +- compiler/rustc_lint/src/builtin.rs | 2 +- compiler/rustc_lint/src/early/diagnostics.rs | 9 ++ compiler/rustc_lint/src/lints.rs | 18 +++ compiler/rustc_lint/src/nonstandard_style.rs | 2 +- compiler/rustc_lint_defs/src/lib.rs | 5 + compiler/rustc_passes/src/check_attr.rs | 21 ++++ .../rustc_passes/src/debugger_visualizer.rs | 2 +- .../rustc_resolve/src/build_reduced_graph.rs | 8 +- compiler/rustc_resolve/src/imports.rs | 85 +++++++++++++-- compiler/rustc_resolve/src/macros.rs | 22 +++- compiler/rustc_span/src/symbol.rs | 2 + .../on_unknown_item/incorrect-locations.rs | 52 +++++++++ .../incorrect-locations.stderr | 103 ++++++++++++++++++ .../incorrect_format_string.rs | 33 ++++++ .../incorrect_format_string.stderr | 96 ++++++++++++++++ .../on_unknown_item/malformed_attribute.rs | 19 ++++ .../malformed_attribute.stderr | 44 ++++++++ .../on_unknown_item/multiple_errors.rs | 48 ++++++++ .../on_unknown_item/multiple_errors.stderr | 43 ++++++++ .../on_unknown_item/unknown_import.rs | 17 +++ .../on_unknown_item/unknown_import.stderr | 13 +++ ...feature-gate-diagnostic-on-unknown-item.rs | 8 ++ ...ure-gate-diagnostic-on-unknown-item.stderr | 22 ++++ 38 files changed, 771 insertions(+), 34 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 1b615b611258..4e3310d3fb09 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -649,7 +649,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) AttributeParser::parse_limited( sess, &krate.attrs, - sym::feature, + &[sym::feature], DUMMY_SP, krate.id, Some(&features), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index e63baf77c085..61fd3f096248 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -24,6 +24,7 @@ pub(crate) mod on_const; pub(crate) mod on_move; pub(crate) mod on_unimplemented; +pub(crate) mod on_unknown_item; #[derive(Copy, Clone)] pub(crate) enum Mode { @@ -35,6 +36,8 @@ pub(crate) enum Mode { DiagnosticOnConst, /// `#[diagnostic::on_move]` DiagnosticOnMove, + /// `#[diagnostic::on_unknown_item]` + DiagnosticOnUnknownItem, } fn merge_directives( @@ -122,6 +125,13 @@ fn parse_directive_items<'p, S: Stage>( span, ); } + Mode::DiagnosticOnUnknownItem => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnknownItemdAttr { span }, + span, + ); + } } continue; }} @@ -140,7 +150,7 @@ fn parse_directive_items<'p, S: Stage>( Mode::RustcOnUnimplemented => { cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); } - Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove => { + Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnknownItem => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, AttributeLintKind::IgnoredDiagnosticOption { @@ -176,7 +186,8 @@ fn parse_directive_items<'p, S: Stage>( Ok((f, warnings)) => { for warning in warnings { let (FormatWarning::InvalidSpecifier { span, .. } - | FormatWarning::PositionalArgument { span, .. }) = warning; + | FormatWarning::PositionalArgument { span, .. } + | FormatWarning::DisallowedPlaceholder { span }) = warning; cx.emit_lint( MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, AttributeLintKind::MalformedDiagnosticFormat { warning }, @@ -326,6 +337,10 @@ fn parse_arg( is_source_literal: bool, ) -> FormatArg { let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); + if matches!(mode, Mode::DiagnosticOnUnknownItem) { + warnings.push(FormatWarning::DisallowedPlaceholder { span }); + return FormatArg::AsIs(sym::empty_braces); + } match arg.position { // Something like "hello {name}" diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs new file mode 100644 index 000000000000..a7abecc671ec --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs @@ -0,0 +1,70 @@ +use rustc_hir::attrs::diagnostic::Directive; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; + +#[derive(Default)] +pub(crate) struct OnUnknownItemParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl OnUnknownItemParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + let span = cx.attr_span; + self.span = Some(span); + + let items = match args { + ArgParser::List(items) if !items.is_empty() => items, + ArgParser::NoArgs | ArgParser::List(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnUnknownItem, + span, + ); + return; + } + ArgParser::NameValue(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnknownItemdAttr { span }, + span, + ); + return; + } + }; + + if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) { + merge_directives(cx, &mut self.directive, (span, directive)); + }; + } +} + +impl AttributeParser for OnUnknownItemParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_unknown_item], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::DiagnosticOnUnknownItem); + }, + )]; + //FIXME attribute is not parsed for non-traits but diagnostics are issued in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnUnknownItem { + span, + directive: self.directive.map(|d| Box::new(d.1)), + }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6ab3f98e2015..c10877ce72e6 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -32,6 +32,7 @@ use crate::attributes::diagnostic::on_const::*; use crate::attributes::diagnostic::on_move::*; use crate::attributes::diagnostic::on_unimplemented::*; +use crate::attributes::diagnostic::on_unknown_item::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -154,6 +155,7 @@ mod late { OnConstParser, OnMoveParser, OnUnimplementedParser, + OnUnknownItemParser, RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f66d6dd3f4c9..a2a2967f9ad6 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -29,7 +29,7 @@ pub struct AttributeParser<'sess, S: Stage = Late> { /// *Only* parse attributes with this symbol. /// /// Used in cases where we want the lowering infrastructure for parse just a single attribute. - parse_only: Option, + parse_only: Option<&'static [Symbol]>, } impl<'sess> AttributeParser<'sess, Early> { @@ -50,7 +50,7 @@ impl<'sess> AttributeParser<'sess, Early> { pub fn parse_limited( sess: &'sess Session, attrs: &[ast::Attribute], - sym: Symbol, + sym: &'static [Symbol], target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, @@ -72,7 +72,7 @@ pub fn parse_limited( pub fn parse_limited_should_emit( sess: &'sess Session, attrs: &[ast::Attribute], - sym: Symbol, + sym: &'static [Symbol], target_span: Span, target_node_id: NodeId, target: Target, @@ -103,7 +103,7 @@ pub fn parse_limited_should_emit( pub fn parse_limited_all( sess: &'sess Session, attrs: &[ast::Attribute], - parse_only: Option, + parse_only: Option<&'static [Symbol]>, target: Target, target_span: Span, target_node_id: NodeId, @@ -272,7 +272,7 @@ pub fn parse_attribute_list( for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { - if !attr.has_name(expected) { + if !attr.path_matches(expected) { continue; } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 63c06e672727..ae0078523adb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -493,7 +493,7 @@ pub(crate) fn expand_ext( match item { Annotatable::Item(item) => { let is_packed = matches!( - AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id, None), + AttributeParser::parse_limited(cx.sess, &item.attrs, &[sym::repr], item.span, item.id, None), Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..))) ); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 24a5d79958c6..84f2a8e35b02 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -108,7 +108,7 @@ fn collect_custom_derive( })) = AttributeParser::parse_limited( self.session, slice::from_ref(attr), - sym::proc_macro_derive, + &[sym::proc_macro_derive], item.span, item.node_id(), None, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 5764dfc83927..071b807109b7 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -483,7 +483,7 @@ fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic { AttributeParser::parse_limited( cx.sess, &i.attrs, - sym::should_panic, + &[sym::should_panic], i.span, i.node_id(), None, diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 1bb6d8a6bfd0..1c947ea07d1a 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -391,7 +391,7 @@ fn get_test_runner(sess: &Session, features: &Features, krate: &ast::Crate) -> O match AttributeParser::parse_limited( sess, &krate.attrs, - sym::test_runner, + &[sym::test_runner], krate.spans.inner_span, krate.id, Some(features), diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index ec5951e50e3a..87e157babe8d 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -54,7 +54,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - AttributeParser::parse_limited( sess, krate_attrs, - sym::feature, + &[sym::feature], DUMMY_SP, DUMMY_NODE_ID, Some(&features), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index acbcba90fbcc..5d5018ce448e 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1588,6 +1588,7 @@ pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool sym::on_unimplemented | sym::do_not_recommend => true, sym::on_const => features.diagnostic_on_const(), sym::on_move => features.diagnostic_on_move(), + sym::on_unknown_item => features.diagnostic_on_unknown_item(), _ => false, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 859a1ad391cb..7886a4fcac0d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -474,6 +474,8 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, diagnostic_on_const, "1.93.0", Some(143874)), /// Allows giving on-move borrowck custom diagnostic messages for a type (unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(154181)), + /// Allows giving unresolved imports a custom diagnostic message + (unstable, diagnostic_on_unknown_item, "CURRENT_RUSTC_VERSION", Some(152900)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a18ddff94709..6fa3df2dfa7d 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1192,6 +1192,14 @@ pub enum AttributeKind { /// None if the directive was malformed in some way. directive: Option>, }, + + /// Represents `#[diagnostic::on_unknown_item]` + OnUnknownItem { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index c19fc6976c6e..699caa203ae4 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -79,6 +79,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { OnConst { .. } => Yes, OnMove { .. } => Yes, OnUnimplemented { .. } => Yes, + OnUnknownItem { .. } => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index eadb099a3e1a..f450ddfc698e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1369,7 +1369,7 @@ pub(crate) fn parse_crate_name( AttributeParser::parse_limited_should_emit( sess, attrs, - sym::crate_name, + &[sym::crate_name], DUMMY_SP, rustc_ast::node_id::CRATE_NODE_ID, Target::Crate, @@ -1419,7 +1419,7 @@ pub fn collect_crate_types( AttributeParser::::parse_limited_should_emit( session, attrs, - sym::crate_type, + &[sym::crate_type], crate_span, CRATE_NODE_ID, Target::Crate, @@ -1476,7 +1476,7 @@ fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit let attr = AttributeParser::parse_limited_should_emit( sess, &krate_attrs, - sym::recursion_limit, + &[sym::recursion_limit], DUMMY_SP, rustc_ast::node_id::CRATE_NODE_ID, Target::Crate, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index af590d98c301..66082d782e0b 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -312,7 +312,7 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { AttributeParser::parse_limited( cx.builder.sess(), &it.attrs, - sym::allow_internal_unsafe, + &[sym::allow_internal_unsafe], it.span, DUMMY_NODE_ID, Some(cx.builder.features()), diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 776313a7e804..0171cf9ebd72 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -179,6 +179,9 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { &AttributeLintKind::MalformedOnUnimplementedAttr { span } => { lints::MalformedOnUnimplementedAttrLint { span }.into_diag(dcx, level) } + &AttributeLintKind::MalformedOnUnknownItemdAttr { span } => { + lints::MalformedOnUnknownItemAttrLint { span }.into_diag(dcx, level) + } &AttributeLintKind::MalformedOnConstAttr { span } => { lints::MalformedOnConstAttrLint { span }.into_diag(dcx, level) } @@ -189,6 +192,9 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { FormatWarning::InvalidSpecifier { .. } => { lints::InvalidFormatSpecifier.into_diag(dcx, level) } + FormatWarning::DisallowedPlaceholder { .. } => { + lints::DisallowedPlaceholder.into_diag(dcx, level) + } }, AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => { lints::WrappedParserError { description, label, span: *span }.into_diag(dcx, level) @@ -215,6 +221,9 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { &AttributeLintKind::MissingOptionsForOnMove => { lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level) } + &AttributeLintKind::MissingOptionsForOnUnknownItem => { + lints::MissingOptionsForOnUnknownItemAttr.into_diag(dcx, level) + } } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 4279ab230df5..9f7dbae70ec5 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3542,6 +3542,11 @@ pub(crate) struct UnknownCrateTypesSuggestion { )] pub(crate) struct DisallowedPositionalArgument; +#[derive(Diagnostic)] +#[diag("format arguments are not allowed here")] +#[help("consider removing this format argument")] +pub(crate) struct DisallowedPlaceholder; + #[derive(Diagnostic)] #[diag("invalid format specifier")] #[help("no format specifier are supported in this position")] @@ -3571,6 +3576,11 @@ pub(crate) struct IgnoredDiagnosticOption { #[help("at least one of the `message`, `note` and `label` options are expected")] pub(crate) struct MissingOptionsForOnUnimplementedAttr; +#[derive(Diagnostic)] +#[diag("missing options for `on_unknown_item` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnUnknownItemAttr; + #[derive(Diagnostic)] #[diag("missing options for `on_const` attribute")] #[help("at least one of the `message`, `note` and `label` options are expected")] @@ -3589,6 +3599,14 @@ pub(crate) struct MalformedOnUnimplementedAttrLint { pub span: Span, } +#[derive(Diagnostic)] +#[diag("malformed `on_unknown_item` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] +pub(crate) struct MalformedOnUnknownItemAttrLint { + #[label("invalid option found here")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("malformed `on_const` attribute")] #[help("only `message`, `note` and `label` are allowed as options")] diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 1fd6699e2506..297dfac4a5f7 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -145,7 +145,7 @@ fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) { impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { let has_repr_c = matches!( - AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id, None), + AttributeParser::parse_limited(cx.sess(), &it.attrs, &[sym::repr], it.span, it.id, None), Some(Attribute::Parsed(AttributeKind::Repr { reprs, ..})) if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprC) ); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 2cec2ed06a50..5497e2509eaa 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -736,6 +736,9 @@ pub enum AttributeLintKind { MalformedOnUnimplementedAttr { span: Span, }, + MalformedOnUnknownItemdAttr { + span: Span, + }, MalformedOnConstAttr { span: Span, }, @@ -757,6 +760,7 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, + MissingOptionsForOnUnknownItem, MissingOptionsForOnMove, OnMoveMalformedFormatLiterals { name: Symbol, @@ -768,6 +772,7 @@ pub enum AttributeLintKind { pub enum FormatWarning { PositionalArgument { span: Span, help: String }, InvalidSpecifier { name: String, span: Span }, + DisallowedPlaceholder { span: Span }, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6aeb0ae57e75..f0495fb820c9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -74,6 +74,13 @@ struct DiagnosticOnConstOnlyForNonConstTraitImpls { #[diag("`#[diagnostic::on_move]` can only be applied to enums, structs or unions")] struct DiagnosticOnMoveOnlyForAdt; +#[derive(Diagnostic)] +#[diag("`#[diagnostic::on_unknown_item]` can only be applied to `use` statements")] +struct DiagnosticOnUnknownItemOnlyForImports { + #[label("not an import")] + item_span: Span, +} + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -219,6 +226,7 @@ fn check_attributes( }, Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, + Attribute::Parsed(AttributeKind::OnUnknownItem { span, .. }) => { self.check_diagnostic_on_unknown_item(*span, hir_id, target) }, Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} Attribute::Parsed(AttributeKind::OnMove { span, directive }) => { self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref()) @@ -727,6 +735,19 @@ fn check_diagnostic_on_move( } } + /// Checks if `#[diagnostic::on_unknown_item]` is applied to a trait impl + fn check_diagnostic_on_unknown_item(&self, attr_span: Span, hir_id: HirId, target: Target) { + if !matches!(target, Target::Use) { + let item_span = self.tcx.hir_span(hir_id); + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnUnknownItemOnlyForImports { item_span }, + ); + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) { match target { diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 7211f3cf85b3..828ba698e0f2 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -25,7 +25,7 @@ fn check_for_debugger_visualizer( AttributeParser::parse_limited( &self.sess, attrs, - sym::debugger_visualizer, + &[sym::debugger_visualizer], span, node_id, None, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d00c306329b7..7eba016d4474 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -32,7 +32,7 @@ use crate::Namespace::{MacroNS, TypeNS, ValueNS}; use crate::def_collector::collect_definitions; -use crate::imports::{ImportData, ImportKind}; +use crate::imports::{ImportData, ImportKind, OnUnknownItemData}; use crate::macros::{MacroRulesDecl, MacroRulesScope, MacroRulesScopeRef}; use crate::ref_mut::CmCell; use crate::{ @@ -545,6 +545,7 @@ fn add_import( root_id, vis, vis_span: item.vis.span, + on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item), }); self.r.indeterminate_imports.push(import); @@ -1026,6 +1027,7 @@ fn build_reduced_graph_for_extern_crate( module_path: Vec::new(), vis, vis_span: item.vis.span, + on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item), }); if used { self.r.import_use_map.insert(import, Used::Other); @@ -1121,7 +1123,7 @@ fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> boo AttributeParser::parse_limited( self.r.tcx.sess, &item.attrs, - sym::macro_use, + &[sym::macro_use], item.span, item.id, None, @@ -1158,6 +1160,7 @@ fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> boo module_path: Vec::new(), vis: Visibility::Restricted(CRATE_DEF_ID), vis_span: item.vis.span, + on_unknown_item_attr: OnUnknownItemData::from_attrs(this.r.tcx, item), }) }; @@ -1329,6 +1332,7 @@ fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'ra> { module_path: Vec::new(), vis, vis_span: item.vis.span, + on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item), }); self.r.import_use_map.insert(import, Used::Other); let import_decl = self.r.new_import_decl(decl, import); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d51ce9fb7946..846efdf22b97 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -2,16 +2,20 @@ use std::mem; -use rustc_ast::NodeId; +use rustc_ast::{Item, NodeId}; +use rustc_attr_parsing::AttributeParser; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diagnostic, MultiSpan, pluralize, struct_span_code_err}; +use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; use rustc_hir::def::{self, DefKind, PartialRes}; use rustc_hir::def_id::{DefId, LocalDefIdMap}; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; -use rustc_middle::ty::Visibility; +use rustc_middle::ty::{TyCtxt, Visibility}; use rustc_session::lint::builtin::{ AMBIGUOUS_GLOB_REEXPORTS, EXPORTED_PRIVATE_DEPENDENCIES, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS, @@ -140,6 +144,30 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { } } +#[derive(Debug, Clone, Default)] +pub(crate) struct OnUnknownItemData { + directive: Directive, +} + +impl OnUnknownItemData { + pub(crate) fn from_attrs<'tcx>(tcx: TyCtxt<'tcx>, item: &Item) -> Option { + if let Some(Attribute::Parsed(AttributeKind::OnUnknownItem { directive, .. })) = + AttributeParser::parse_limited( + tcx.sess, + &item.attrs, + &[sym::diagnostic, sym::on_unknown_item], + item.span, + item.id, + Some(tcx.features()), + ) + { + Some(Self { directive: *directive? }) + } else { + None + } + } +} + /// One import. #[derive(Debug, Clone)] pub(crate) struct ImportData<'ra> { @@ -186,6 +214,11 @@ pub(crate) struct ImportData<'ra> { /// Span of the visibility. pub vis_span: Span, + + /// A `#[diagnostic::on_unknown_item]` attribute applied + /// to the given import. This allows crates to specify + /// custom error messages for a specific import + pub on_unknown_item_attr: Option, } /// All imports are unique and allocated on a same arena, @@ -284,6 +317,7 @@ struct UnresolvedImportError { segment: Option, /// comes from `PathRes::Failed { module }` module: Option, + on_unknown_item_attr: Option, } // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` @@ -700,6 +734,7 @@ pub(crate) fn finalize_imports(&mut self) { candidates: None, segment: None, module: None, + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }; errors.push((*import, err)) } @@ -822,11 +857,41 @@ fn throw_unresolved_import_error( format!("`{path}`") }) .collect::>(); - let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); + let default_message = + format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); + let (message, label, notes) = if self.tcx.features().diagnostic_on_unknown_item() + && let Some(directive) = errors[0].1.on_unknown_item_attr.as_ref().map(|a| &a.directive) + { + let args = FormatArgs { + this: paths.join(", "), + // Unused + this_sugared: String::new(), + // Unused + item_context: "", + // Unused + generic_args: Vec::new(), + }; + let CustomDiagnostic { message, label, notes, .. } = directive.eval(None, &args); - let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{msg}"); + (message, label, notes) + } else { + (None, None, Vec::new()) + }; + let has_custom_message = message.is_some(); + let message = message.as_deref().unwrap_or(default_message.as_str()); - if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() { + let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{message}"); + if has_custom_message { + diag.note(default_message); + } + + if !notes.is_empty() { + for note in notes { + diag.note(note); + } + } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = + errors.iter().last() + { diag.note(note.clone()); } @@ -834,8 +899,10 @@ fn throw_unresolved_import_error( const MAX_LABEL_COUNT: usize = 10; for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) { - if let Some(label) = err.label { - diag.span_label(err.span, label); + if let Some(label) = &label { + diag.span_label(err.span, label.clone()); + } else if let Some(label) = &err.label { + diag.span_label(err.span, label.clone()); } if let Some((suggestions, msg, applicability)) = err.suggestion { @@ -1101,6 +1168,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option UnresolvedImportError { span, @@ -1110,6 +1178,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option) -> Option) -> Option>(); + let typo = find_best_match_for_name(&candidates, attribute.ident.name, Some(5)) .map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name }); self.tcx.sess.psess.buffer_lint( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 33bc5a578e8b..d4ec05d6447b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -799,6 +799,7 @@ diagnostic_namespace, diagnostic_on_const, diagnostic_on_move, + diagnostic_on_unknown_item, dialect, direct, discriminant_kind, @@ -1418,6 +1419,7 @@ on_const, on_move, on_unimplemented, + on_unknown_item, opaque, opaque_module_name_placeholder: "", ops, diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs new file mode 100644 index 000000000000..7b450f2fd4fa --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs @@ -0,0 +1,52 @@ +//@ run-pass +#![allow(dead_code, unused_imports)] +#![feature(diagnostic_on_unknown_item)] + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +extern crate std as other_std; + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +const CONST: () = (); + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +static STATIC: () = (); + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +type Type = (); + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +enum Enum {} + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +impl Enum {} + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +extern "C" {} + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +fn fun() {} + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +struct Struct {} + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +trait Trait {} + +#[diagnostic::on_unknown_item(message = "foo")] +//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements +impl Trait for i32 {} + +#[diagnostic::on_unknown_item(message = "foo")] +use std::str::FromStr; + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr new file mode 100644 index 000000000000..b09b6c90fd0e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr @@ -0,0 +1,103 @@ +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:5:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | extern crate std as other_std; + | ----------------------------- not an import + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:9:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const CONST: () = (); + | --------------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:13:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | static STATIC: () = (); + | ----------------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:17:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Type = (); + | --------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:21:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | enum Enum {} + | --------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:25:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | impl Enum {} + | --------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:29:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | extern "C" {} + | ------------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:33:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn fun() {} + | -------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:37:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | struct Struct {} + | ------------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:41:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | trait Trait {} + | ----------- not an import + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:45:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | impl Trait for i32 {} + | ------------------ not an import + +warning: 11 warnings emitted + diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.rs b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.rs new file mode 100644 index 000000000000..d7d6f1845060 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.rs @@ -0,0 +1,33 @@ +#![feature(diagnostic_on_unknown_item)] + +#[diagnostic::on_unknown_item(message = "foo {}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist; +//~^ ERROR: foo {} + +#[diagnostic::on_unknown_item(message = "foo {A}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist2; +//~^ ERROR: foo {} + +#[diagnostic::on_unknown_item(label = "foo {}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist3; +//~^ ERROR: unresolved import `std::does_not_exist3` + +#[diagnostic::on_unknown_item(label = "foo {A}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist4; +//~^ ERROR: unresolved import `std::does_not_exist4` + +#[diagnostic::on_unknown_item(note = "foo {}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist5; +//~^ ERROR: unresolved import `std::does_not_exist5` + +#[diagnostic::on_unknown_item(note = "foo {A}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist6; +//~^ ERROR: unresolved import `std::does_not_exist6` + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.stderr new file mode 100644 index 000000000000..e8551479287e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.stderr @@ -0,0 +1,96 @@ +error[E0432]: foo {} + --> $DIR/incorrect_format_string.rs:5:5 + | +LL | use std::does_not_exist; + | ^^^^^^^^^^^^^^^^^^^ no `does_not_exist` in the root + | + = note: unresolved import `std::does_not_exist` + +error[E0432]: foo {} + --> $DIR/incorrect_format_string.rs:10:5 + | +LL | use std::does_not_exist2; + | ^^^^^^^^^^^^^^^^^^^^ no `does_not_exist2` in the root + | + = note: unresolved import `std::does_not_exist2` + +error[E0432]: unresolved import `std::does_not_exist3` + --> $DIR/incorrect_format_string.rs:15:5 + | +LL | use std::does_not_exist3; + | ^^^^^^^^^^^^^^^^^^^^ foo {} + +error[E0432]: unresolved import `std::does_not_exist4` + --> $DIR/incorrect_format_string.rs:20:5 + | +LL | use std::does_not_exist4; + | ^^^^^^^^^^^^^^^^^^^^ foo {} + +error[E0432]: unresolved import `std::does_not_exist5` + --> $DIR/incorrect_format_string.rs:25:5 + | +LL | use std::does_not_exist5; + | ^^^^^^^^^^^^^^^^^^^^ no `does_not_exist5` in the root + | + = note: foo {} + +error[E0432]: unresolved import `std::does_not_exist6` + --> $DIR/incorrect_format_string.rs:30:5 + | +LL | use std::does_not_exist6; + | ^^^^^^^^^^^^^^^^^^^^ no `does_not_exist6` in the root + | + = note: foo {} + +warning: format arguments are not allowed here + --> $DIR/incorrect_format_string.rs:3:47 + | +LL | #[diagnostic::on_unknown_item(message = "foo {}")] + | ^ + | + = help: consider removing this format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: format arguments are not allowed here + --> $DIR/incorrect_format_string.rs:8:47 + | +LL | #[diagnostic::on_unknown_item(message = "foo {A}")] + | ^ + | + = help: consider removing this format argument + +warning: format arguments are not allowed here + --> $DIR/incorrect_format_string.rs:13:45 + | +LL | #[diagnostic::on_unknown_item(label = "foo {}")] + | ^ + | + = help: consider removing this format argument + +warning: format arguments are not allowed here + --> $DIR/incorrect_format_string.rs:18:45 + | +LL | #[diagnostic::on_unknown_item(label = "foo {A}")] + | ^ + | + = help: consider removing this format argument + +warning: format arguments are not allowed here + --> $DIR/incorrect_format_string.rs:23:44 + | +LL | #[diagnostic::on_unknown_item(note = "foo {}")] + | ^ + | + = help: consider removing this format argument + +warning: format arguments are not allowed here + --> $DIR/incorrect_format_string.rs:28:44 + | +LL | #[diagnostic::on_unknown_item(note = "foo {A}")] + | ^ + | + = help: consider removing this format argument + +error: aborting due to 6 previous errors; 6 warnings emitted + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs new file mode 100644 index 000000000000..4ffa9ffe37b5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs @@ -0,0 +1,19 @@ +#![feature(diagnostic_on_unknown_item)] +#[diagnostic::on_unknown_item] +//~^WARN missing options for `on_unknown_item` attribute +use std::str::FromStr; + +#[diagnostic::on_unknown_item(foo = "bar", message = "foo")] +//~^WARN malformed `on_unknown_item` attribute +use std::str::Bytes; + +#[diagnostic::on_unknown_item(label = "foo", label = "bar")] +//~^WARN `label` is ignored due to previous definition of `label` +use std::str::Chars; + +#[diagnostic::on_unknown_item(message = "Foo", message = "Bar")] +//~^WARN `message` is ignored due to previous definition of `message` +use std::str::NotExisting; +//~^ERROR Foo + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr new file mode 100644 index 000000000000..42caaa9354af --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr @@ -0,0 +1,44 @@ +error[E0432]: Foo + --> $DIR/malformed_attribute.rs:16:5 + | +LL | use std::str::NotExisting; + | ^^^^^^^^^^^^^^^^^^^^^ no `NotExisting` in `str` + | + = note: unresolved import `std::str::NotExisting` + +warning: missing options for `on_unknown_item` attribute + --> $DIR/malformed_attribute.rs:2:1 + | +LL | #[diagnostic::on_unknown_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: malformed `on_unknown_item` attribute + --> $DIR/malformed_attribute.rs:6:31 + | +LL | #[diagnostic::on_unknown_item(foo = "bar", message = "foo")] + | ^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: `label` is ignored due to previous definition of `label` + --> $DIR/malformed_attribute.rs:10:46 + | +LL | #[diagnostic::on_unknown_item(label = "foo", label = "bar")] + | ------------- ^^^^^^^^^^^^^ `label` is later redundantly declared here + | | + | `label` is first declared here + +warning: `message` is ignored due to previous definition of `message` + --> $DIR/malformed_attribute.rs:14:48 + | +LL | #[diagnostic::on_unknown_item(message = "Foo", message = "Bar")] + | --------------- ^^^^^^^^^^^^^^^ `message` is later redundantly declared here + | | + | `message` is first declared here + +error: aborting due to 1 previous error; 4 warnings emitted + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs new file mode 100644 index 000000000000..431ab6cdd831 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs @@ -0,0 +1,48 @@ +#![feature(diagnostic_on_unknown_item)] + +mod test1 { + #[diagnostic::on_unknown_item( + message = "custom message", + label = "custom label", + note = "custom note" + )] + use std::vec::{NonExisting, Vec, Whatever}; + //~^ ERROR: custom message +} + +mod test2 { + #[diagnostic::on_unknown_item( + message = "custom message", + label = "custom label", + note = "custom note" + )] + use std::{Whatever, vec::NonExisting, vec::Vec, *}; + //~^ ERROR: custom message +} + +mod test3 { + #[diagnostic::on_unknown_item( + message = "custom message", + label = "custom label", + note = "custom note" + )] + use std::{ + string::String, + vec::{NonExisting, Vec}, + //~^ ERROR: custom message + }; +} + +mod test4 { + #[diagnostic::on_unknown_item( + message = "custom message", + label = "custom label", + note = "custom note" + )] + use std::{ + string::String, + vec::{Vec, non_existing::*}, + //~^ ERROR: custom message + }; +} +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr new file mode 100644 index 000000000000..fcce77f6aebb --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr @@ -0,0 +1,43 @@ +error[E0432]: custom message + --> $DIR/multiple_errors.rs:9:20 + | +LL | use std::vec::{NonExisting, Vec, Whatever}; + | ^^^^^^^^^^^ ^^^^^^^^ custom label + | | + | custom label + | + = note: unresolved imports `std::vec::NonExisting`, `std::vec::Whatever` + = note: custom note + +error[E0432]: custom message + --> $DIR/multiple_errors.rs:19:15 + | +LL | use std::{Whatever, vec::NonExisting, vec::Vec, *}; + | ^^^^^^^^ ^^^^^^^^^^^^^^^^ custom label + | | + | custom label + | + = note: unresolved imports `std::Whatever`, `std::vec::NonExisting` + = note: custom note + +error[E0432]: custom message + --> $DIR/multiple_errors.rs:31:15 + | +LL | vec::{NonExisting, Vec}, + | ^^^^^^^^^^^ custom label + | + = note: unresolved import `std::vec::NonExisting` + = note: custom note + +error[E0432]: custom message + --> $DIR/multiple_errors.rs:44:20 + | +LL | vec::{Vec, non_existing::*}, + | ^^^^^^^^^^^^ custom label + | + = note: unresolved import `std::vec::non_existing` + = note: custom note + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs new file mode 100644 index 000000000000..5af79af23c2c --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs @@ -0,0 +1,17 @@ +#![feature(diagnostic_on_unknown_item)] +pub mod foo { + pub struct Bar; +} + +#[diagnostic::on_unknown_item( + message = "first message", + label = "first label", + note = "custom note", + note = "custom note 2" +)] +use foo::Foo; +//~^ERROR first message + +use foo::Bar; + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr new file mode 100644 index 000000000000..a9867fd74bfb --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr @@ -0,0 +1,13 @@ +error[E0432]: first message + --> $DIR/unknown_import.rs:12:5 + | +LL | use foo::Foo; + | ^^^^^^^^ first label + | + = note: unresolved import `foo::Foo` + = note: custom note + = note: custom note 2 + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs new file mode 100644 index 000000000000..fffb54636cfb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs @@ -0,0 +1,8 @@ +#![deny(warnings)] + +#[diagnostic::on_unknown_item(message = "Tada")] +//~^ ERROR: unknown diagnostic attribute +use std::vec::NotExisting; +//~^ ERROR: unresolved import `std::vec::NotExisting` + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr new file mode 100644 index 000000000000..10662eb83b4a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr @@ -0,0 +1,22 @@ +error[E0432]: unresolved import `std::vec::NotExisting` + --> $DIR/feature-gate-diagnostic-on-unknown-item.rs:5:5 + | +LL | use std::vec::NotExisting; + | ^^^^^^^^^^^^^^^^^^^^^ no `NotExisting` in `vec` + +error: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-on-unknown-item.rs:3:15 + | +LL | #[diagnostic::on_unknown_item(message = "Tada")] + | ^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/feature-gate-diagnostic-on-unknown-item.rs:1:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(unknown_diagnostic_attributes)]` implied by `#[deny(warnings)]` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0432`. From bbf29b4ca9b05e769d17388cb15bbde7da9e221b Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Mon, 16 Mar 2026 13:01:13 +0100 Subject: [PATCH 07/10] Address review comments --- .../src/attributes/diagnostic/on_unknown_item.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs index a7abecc671ec..f2a243a8e0bb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs @@ -17,6 +17,9 @@ fn parse<'sess, S: Stage>( args: &ArgParser, mode: Mode, ) { + if !cx.features().diagnostic_on_unknown_item() { + return; + } let span = cx.attr_span; self.span = Some(span); @@ -54,7 +57,7 @@ impl AttributeParser for OnUnknownItemParser { this.parse(cx, args, Mode::DiagnosticOnUnknownItem); }, )]; - //FIXME attribute is not parsed for non-traits but diagnostics are issued in `check_attr.rs` + //FIXME attribute is not parsed for non-use statements but diagnostics are issued in `check_attr.rs` const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { From 97da8195dea600fbaba876914dfae2ee09ce83ce Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 8 Apr 2026 08:36:39 +0200 Subject: [PATCH 08/10] Rename the attribute to `on_unknown` --- .../src/attributes/diagnostic/mod.rs | 14 +-- .../{on_unknown_item.rs => on_unknown.rs} | 18 +-- compiler/rustc_attr_parsing/src/context.rs | 4 +- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- compiler/rustc_feature/src/unstable.rs | 2 +- .../rustc_hir/src/attrs/data_structures.rs | 4 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- compiler/rustc_lint/src/early/diagnostics.rs | 8 +- compiler/rustc_lint/src/lints.rs | 8 +- compiler/rustc_lint_defs/src/lib.rs | 4 +- compiler/rustc_passes/src/check_attr.rs | 12 +- .../rustc_resolve/src/build_reduced_graph.rs | 10 +- compiler/rustc_resolve/src/imports.rs | 30 ++--- compiler/rustc_resolve/src/macros.rs | 2 +- compiler/rustc_span/src/symbol.rs | 4 +- .../on_unknown/incorrect-locations.rs | 52 +++++++++ .../on_unknown/incorrect-locations.stderr | 103 ++++++++++++++++++ .../incorrect_format_string.rs | 14 +-- .../incorrect_format_string.stderr | 36 +++--- .../on_unknown/malformed_attribute.rs | 19 ++++ .../on_unknown/malformed_attribute.stderr | 44 ++++++++ .../multiple_errors.rs | 10 +- .../multiple_errors.stderr | 0 .../unknown_import.rs | 4 +- .../unknown_import.stderr | 0 .../on_unknown_item/incorrect-locations.rs | 52 --------- .../incorrect-locations.stderr | 103 ------------------ .../on_unknown_item/malformed_attribute.rs | 19 ---- .../malformed_attribute.stderr | 44 -------- ... => feature-gate-diagnostic-on-unknown.rs} | 2 +- ...feature-gate-diagnostic-on-unknown.stderr} | 10 +- 31 files changed, 318 insertions(+), 318 deletions(-) rename compiler/rustc_attr_parsing/src/attributes/diagnostic/{on_unknown_item.rs => on_unknown.rs} (82%) create mode 100644 tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.stderr rename tests/ui/diagnostic_namespace/{on_unknown_item => on_unknown}/incorrect_format_string.rs (67%) rename tests/ui/diagnostic_namespace/{on_unknown_item => on_unknown}/incorrect_format_string.stderr (70%) create mode 100644 tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.rs create mode 100644 tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.stderr rename tests/ui/diagnostic_namespace/{on_unknown_item => on_unknown}/multiple_errors.rs (83%) rename tests/ui/diagnostic_namespace/{on_unknown_item => on_unknown}/multiple_errors.stderr (100%) rename tests/ui/diagnostic_namespace/{on_unknown_item => on_unknown}/unknown_import.rs (75%) rename tests/ui/diagnostic_namespace/{on_unknown_item => on_unknown}/unknown_import.stderr (100%) delete mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs delete mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr delete mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs delete mode 100644 tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr rename tests/ui/feature-gates/{feature-gate-diagnostic-on-unknown-item.rs => feature-gate-diagnostic-on-unknown.rs} (76%) rename tests/ui/feature-gates/{feature-gate-diagnostic-on-unknown-item.stderr => feature-gate-diagnostic-on-unknown.stderr} (64%) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 61fd3f096248..f68bed620f1b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -24,7 +24,7 @@ pub(crate) mod on_const; pub(crate) mod on_move; pub(crate) mod on_unimplemented; -pub(crate) mod on_unknown_item; +pub(crate) mod on_unknown; #[derive(Copy, Clone)] pub(crate) enum Mode { @@ -36,8 +36,8 @@ pub(crate) enum Mode { DiagnosticOnConst, /// `#[diagnostic::on_move]` DiagnosticOnMove, - /// `#[diagnostic::on_unknown_item]` - DiagnosticOnUnknownItem, + /// `#[diagnostic::on_unknown]` + DiagnosticOnUnknown, } fn merge_directives( @@ -125,10 +125,10 @@ fn parse_directive_items<'p, S: Stage>( span, ); } - Mode::DiagnosticOnUnknownItem => { + Mode::DiagnosticOnUnknown => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - AttributeLintKind::MalformedOnUnknownItemdAttr { span }, + AttributeLintKind::MalformedOnUnknownAttr { span }, span, ); } @@ -150,7 +150,7 @@ fn parse_directive_items<'p, S: Stage>( Mode::RustcOnUnimplemented => { cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); } - Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnknownItem => { + Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnknown => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, AttributeLintKind::IgnoredDiagnosticOption { @@ -337,7 +337,7 @@ fn parse_arg( is_source_literal: bool, ) -> FormatArg { let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); - if matches!(mode, Mode::DiagnosticOnUnknownItem) { + if matches!(mode, Mode::DiagnosticOnUnknown) { warnings.push(FormatWarning::DisallowedPlaceholder { span }); return FormatArg::AsIs(sym::empty_braces); } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs similarity index 82% rename from compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs rename to compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs index f2a243a8e0bb..bd5eb4cbf82c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs @@ -5,19 +5,19 @@ use crate::attributes::prelude::*; #[derive(Default)] -pub(crate) struct OnUnknownItemParser { +pub(crate) struct OnUnknownParser { span: Option, directive: Option<(Span, Directive)>, } -impl OnUnknownItemParser { +impl OnUnknownParser { fn parse<'sess, S: Stage>( &mut self, cx: &mut AcceptContext<'_, 'sess, S>, args: &ArgParser, mode: Mode, ) { - if !cx.features().diagnostic_on_unknown_item() { + if !cx.features().diagnostic_on_unknown() { return; } let span = cx.attr_span; @@ -28,7 +28,7 @@ fn parse<'sess, S: Stage>( ArgParser::NoArgs | ArgParser::List(_) => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - AttributeLintKind::MissingOptionsForOnUnknownItem, + AttributeLintKind::MissingOptionsForOnUnknown, span, ); return; @@ -36,7 +36,7 @@ fn parse<'sess, S: Stage>( ArgParser::NameValue(_) => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - AttributeLintKind::MalformedOnUnknownItemdAttr { span }, + AttributeLintKind::MalformedOnUnknownAttr { span }, span, ); return; @@ -49,12 +49,12 @@ fn parse<'sess, S: Stage>( } } -impl AttributeParser for OnUnknownItemParser { +impl AttributeParser for OnUnknownParser { const ATTRIBUTES: AcceptMapping = &[( - &[sym::diagnostic, sym::on_unknown_item], + &[sym::diagnostic, sym::on_unknown], template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), |this, cx, args| { - this.parse(cx, args, Mode::DiagnosticOnUnknownItem); + this.parse(cx, args, Mode::DiagnosticOnUnknown); }, )]; //FIXME attribute is not parsed for non-use statements but diagnostics are issued in `check_attr.rs` @@ -62,7 +62,7 @@ impl AttributeParser for OnUnknownItemParser { fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { if let Some(span) = self.span { - Some(AttributeKind::OnUnknownItem { + Some(AttributeKind::OnUnknown { span, directive: self.directive.map(|d| Box::new(d.1)), }) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index c10877ce72e6..3fde8d79f514 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -32,7 +32,7 @@ use crate::attributes::diagnostic::on_const::*; use crate::attributes::diagnostic::on_move::*; use crate::attributes::diagnostic::on_unimplemented::*; -use crate::attributes::diagnostic::on_unknown_item::*; +use crate::attributes::diagnostic::on_unknown::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -155,7 +155,7 @@ mod late { OnConstParser, OnMoveParser, OnUnimplementedParser, - OnUnknownItemParser, + OnUnknownParser, RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5d5018ce448e..3edc19e4314c 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1588,7 +1588,7 @@ pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool sym::on_unimplemented | sym::do_not_recommend => true, sym::on_const => features.diagnostic_on_const(), sym::on_move => features.diagnostic_on_move(), - sym::on_unknown_item => features.diagnostic_on_unknown_item(), + sym::on_unknown => features.diagnostic_on_unknown(), _ => false, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 7886a4fcac0d..c2fe6e136020 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -475,7 +475,7 @@ pub fn internal(&self, feature: Symbol) -> bool { /// Allows giving on-move borrowck custom diagnostic messages for a type (unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(154181)), /// Allows giving unresolved imports a custom diagnostic message - (unstable, diagnostic_on_unknown_item, "CURRENT_RUSTC_VERSION", Some(152900)), + (unstable, diagnostic_on_unknown, "CURRENT_RUSTC_VERSION", Some(152900)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 6fa3df2dfa7d..c94444893f1e 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1193,8 +1193,8 @@ pub enum AttributeKind { directive: Option>, }, - /// Represents `#[diagnostic::on_unknown_item]` - OnUnknownItem { + /// Represents `#[diagnostic::on_unknown]` + OnUnknown { span: Span, /// None if the directive was malformed in some way. directive: Option>, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 699caa203ae4..861d74766ccb 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -79,7 +79,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { OnConst { .. } => Yes, OnMove { .. } => Yes, OnUnimplemented { .. } => Yes, - OnUnknownItem { .. } => Yes, + OnUnknown { .. } => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 0171cf9ebd72..1b31639c4078 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -179,8 +179,8 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { &AttributeLintKind::MalformedOnUnimplementedAttr { span } => { lints::MalformedOnUnimplementedAttrLint { span }.into_diag(dcx, level) } - &AttributeLintKind::MalformedOnUnknownItemdAttr { span } => { - lints::MalformedOnUnknownItemAttrLint { span }.into_diag(dcx, level) + &AttributeLintKind::MalformedOnUnknownAttr { span } => { + lints::MalformedOnUnknownAttrLint { span }.into_diag(dcx, level) } &AttributeLintKind::MalformedOnConstAttr { span } => { lints::MalformedOnConstAttrLint { span }.into_diag(dcx, level) @@ -221,8 +221,8 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { &AttributeLintKind::MissingOptionsForOnMove => { lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level) } - &AttributeLintKind::MissingOptionsForOnUnknownItem => { - lints::MissingOptionsForOnUnknownItemAttr.into_diag(dcx, level) + &AttributeLintKind::MissingOptionsForOnUnknown => { + lints::MissingOptionsForOnUnknownAttr.into_diag(dcx, level) } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 9f7dbae70ec5..19ec3f4ca45d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3577,9 +3577,9 @@ pub(crate) struct IgnoredDiagnosticOption { pub(crate) struct MissingOptionsForOnUnimplementedAttr; #[derive(Diagnostic)] -#[diag("missing options for `on_unknown_item` attribute")] +#[diag("missing options for `on_unknown` attribute")] #[help("at least one of the `message`, `note` and `label` options are expected")] -pub(crate) struct MissingOptionsForOnUnknownItemAttr; +pub(crate) struct MissingOptionsForOnUnknownAttr; #[derive(Diagnostic)] #[diag("missing options for `on_const` attribute")] @@ -3600,9 +3600,9 @@ pub(crate) struct MalformedOnUnimplementedAttrLint { } #[derive(Diagnostic)] -#[diag("malformed `on_unknown_item` attribute")] +#[diag("malformed `on_unknown` attribute")] #[help("only `message`, `note` and `label` are allowed as options")] -pub(crate) struct MalformedOnUnknownItemAttrLint { +pub(crate) struct MalformedOnUnknownAttrLint { #[label("invalid option found here")] pub span: Span, } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 5497e2509eaa..a77b7bc7d948 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -736,7 +736,7 @@ pub enum AttributeLintKind { MalformedOnUnimplementedAttr { span: Span, }, - MalformedOnUnknownItemdAttr { + MalformedOnUnknownAttr { span: Span, }, MalformedOnConstAttr { @@ -760,7 +760,7 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, - MissingOptionsForOnUnknownItem, + MissingOptionsForOnUnknown, MissingOptionsForOnMove, OnMoveMalformedFormatLiterals { name: Symbol, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f0495fb820c9..c49872802878 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -75,8 +75,8 @@ struct DiagnosticOnConstOnlyForNonConstTraitImpls { struct DiagnosticOnMoveOnlyForAdt; #[derive(Diagnostic)] -#[diag("`#[diagnostic::on_unknown_item]` can only be applied to `use` statements")] -struct DiagnosticOnUnknownItemOnlyForImports { +#[diag("`#[diagnostic::on_unknown]` can only be applied to `use` statements")] +struct DiagnosticOnUnknownOnlyForImports { #[label("not an import")] item_span: Span, } @@ -226,7 +226,7 @@ fn check_attributes( }, Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, - Attribute::Parsed(AttributeKind::OnUnknownItem { span, .. }) => { self.check_diagnostic_on_unknown_item(*span, hir_id, target) }, + Attribute::Parsed(AttributeKind::OnUnknown { span, .. }) => { self.check_diagnostic_on_unknown(*span, hir_id, target) }, Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} Attribute::Parsed(AttributeKind::OnMove { span, directive }) => { self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref()) @@ -735,15 +735,15 @@ fn check_diagnostic_on_move( } } - /// Checks if `#[diagnostic::on_unknown_item]` is applied to a trait impl - fn check_diagnostic_on_unknown_item(&self, attr_span: Span, hir_id: HirId, target: Target) { + /// Checks if `#[diagnostic::on_unknown]` is applied to a trait impl + fn check_diagnostic_on_unknown(&self, attr_span: Span, hir_id: HirId, target: Target) { if !matches!(target, Target::Use) { let item_span = self.tcx.hir_span(hir_id); self.tcx.emit_node_span_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, hir_id, attr_span, - DiagnosticOnUnknownItemOnlyForImports { item_span }, + DiagnosticOnUnknownOnlyForImports { item_span }, ); } } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 7eba016d4474..983a7a201b10 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -32,7 +32,7 @@ use crate::Namespace::{MacroNS, TypeNS, ValueNS}; use crate::def_collector::collect_definitions; -use crate::imports::{ImportData, ImportKind, OnUnknownItemData}; +use crate::imports::{ImportData, ImportKind, OnUnknownData}; use crate::macros::{MacroRulesDecl, MacroRulesScope, MacroRulesScopeRef}; use crate::ref_mut::CmCell; use crate::{ @@ -545,7 +545,7 @@ fn add_import( root_id, vis, vis_span: item.vis.span, - on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item), + on_unknown_attr: OnUnknownData::from_attrs(self.r.tcx, item), }); self.r.indeterminate_imports.push(import); @@ -1027,7 +1027,7 @@ fn build_reduced_graph_for_extern_crate( module_path: Vec::new(), vis, vis_span: item.vis.span, - on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item), + on_unknown_attr: OnUnknownData::from_attrs(self.r.tcx, item), }); if used { self.r.import_use_map.insert(import, Used::Other); @@ -1160,7 +1160,7 @@ fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> boo module_path: Vec::new(), vis: Visibility::Restricted(CRATE_DEF_ID), vis_span: item.vis.span, - on_unknown_item_attr: OnUnknownItemData::from_attrs(this.r.tcx, item), + on_unknown_attr: OnUnknownData::from_attrs(this.r.tcx, item), }) }; @@ -1332,7 +1332,7 @@ fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'ra> { module_path: Vec::new(), vis, vis_span: item.vis.span, - on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item), + on_unknown_attr: OnUnknownData::from_attrs(self.r.tcx, item), }); self.r.import_use_map.insert(import, Used::Other); let import_decl = self.r.new_import_decl(decl, import); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 846efdf22b97..18db60167c27 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -145,17 +145,17 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { } #[derive(Debug, Clone, Default)] -pub(crate) struct OnUnknownItemData { +pub(crate) struct OnUnknownData { directive: Directive, } -impl OnUnknownItemData { - pub(crate) fn from_attrs<'tcx>(tcx: TyCtxt<'tcx>, item: &Item) -> Option { - if let Some(Attribute::Parsed(AttributeKind::OnUnknownItem { directive, .. })) = +impl OnUnknownData { + pub(crate) fn from_attrs<'tcx>(tcx: TyCtxt<'tcx>, item: &Item) -> Option { + if let Some(Attribute::Parsed(AttributeKind::OnUnknown { directive, .. })) = AttributeParser::parse_limited( tcx.sess, &item.attrs, - &[sym::diagnostic, sym::on_unknown_item], + &[sym::diagnostic, sym::on_unknown], item.span, item.id, Some(tcx.features()), @@ -215,10 +215,10 @@ pub(crate) struct ImportData<'ra> { /// Span of the visibility. pub vis_span: Span, - /// A `#[diagnostic::on_unknown_item]` attribute applied + /// A `#[diagnostic::on_unknown]` attribute applied /// to the given import. This allows crates to specify /// custom error messages for a specific import - pub on_unknown_item_attr: Option, + pub on_unknown_attr: Option, } /// All imports are unique and allocated on a same arena, @@ -317,7 +317,7 @@ struct UnresolvedImportError { segment: Option, /// comes from `PathRes::Failed { module }` module: Option, - on_unknown_item_attr: Option, + on_unknown_attr: Option, } // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` @@ -734,7 +734,7 @@ pub(crate) fn finalize_imports(&mut self) { candidates: None, segment: None, module: None, - on_unknown_item_attr: import.on_unknown_item_attr.clone(), + on_unknown_attr: import.on_unknown_attr.clone(), }; errors.push((*import, err)) } @@ -859,8 +859,8 @@ fn throw_unresolved_import_error( .collect::>(); let default_message = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); - let (message, label, notes) = if self.tcx.features().diagnostic_on_unknown_item() - && let Some(directive) = errors[0].1.on_unknown_item_attr.as_ref().map(|a| &a.directive) + let (message, label, notes) = if self.tcx.features().diagnostic_on_unknown() + && let Some(directive) = errors[0].1.on_unknown_attr.as_ref().map(|a| &a.directive) { let args = FormatArgs { this: paths.join(", "), @@ -1168,7 +1168,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option UnresolvedImportError { span, @@ -1178,7 +1178,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option) -> Option) -> Option", ops, diff --git a/tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.rs b/tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.rs new file mode 100644 index 000000000000..b8852e7dd216 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.rs @@ -0,0 +1,52 @@ +//@ run-pass +#![allow(dead_code, unused_imports)] +#![feature(diagnostic_on_unknown)] + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +extern crate std as other_std; + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +const CONST: () = (); + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +static STATIC: () = (); + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +type Type = (); + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +enum Enum {} + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +impl Enum {} + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +extern "C" {} + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +fn fun() {} + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +struct Struct {} + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +trait Trait {} + +#[diagnostic::on_unknown(message = "foo")] +//~^WARN `#[diagnostic::on_unknown]` can only be applied to `use` statements +impl Trait for i32 {} + +#[diagnostic::on_unknown(message = "foo")] +use std::str::FromStr; + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.stderr b/tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.stderr new file mode 100644 index 000000000000..33636e1fcfc3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/incorrect-locations.stderr @@ -0,0 +1,103 @@ +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:5:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | extern crate std as other_std; + | ----------------------------- not an import + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:9:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const CONST: () = (); + | --------------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:13:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | static STATIC: () = (); + | ----------------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:17:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Type = (); + | --------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:21:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | enum Enum {} + | --------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:25:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | impl Enum {} + | --------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:29:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | extern "C" {} + | ------------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:33:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn fun() {} + | -------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:37:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | struct Struct {} + | ------------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:41:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | trait Trait {} + | ----------- not an import + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:45:1 + | +LL | #[diagnostic::on_unknown(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | impl Trait for i32 {} + | ------------------ not an import + +warning: 11 warnings emitted + diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.rs b/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.rs similarity index 67% rename from tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.rs rename to tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.rs index d7d6f1845060..cdf0f1e89efc 100644 --- a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.rs +++ b/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.rs @@ -1,31 +1,31 @@ -#![feature(diagnostic_on_unknown_item)] +#![feature(diagnostic_on_unknown)] -#[diagnostic::on_unknown_item(message = "foo {}")] +#[diagnostic::on_unknown(message = "foo {}")] //~^ WARN: format arguments are not allowed here use std::does_not_exist; //~^ ERROR: foo {} -#[diagnostic::on_unknown_item(message = "foo {A}")] +#[diagnostic::on_unknown(message = "foo {A}")] //~^ WARN: format arguments are not allowed here use std::does_not_exist2; //~^ ERROR: foo {} -#[diagnostic::on_unknown_item(label = "foo {}")] +#[diagnostic::on_unknown(label = "foo {}")] //~^ WARN: format arguments are not allowed here use std::does_not_exist3; //~^ ERROR: unresolved import `std::does_not_exist3` -#[diagnostic::on_unknown_item(label = "foo {A}")] +#[diagnostic::on_unknown(label = "foo {A}")] //~^ WARN: format arguments are not allowed here use std::does_not_exist4; //~^ ERROR: unresolved import `std::does_not_exist4` -#[diagnostic::on_unknown_item(note = "foo {}")] +#[diagnostic::on_unknown(note = "foo {}")] //~^ WARN: format arguments are not allowed here use std::does_not_exist5; //~^ ERROR: unresolved import `std::does_not_exist5` -#[diagnostic::on_unknown_item(note = "foo {A}")] +#[diagnostic::on_unknown(note = "foo {A}")] //~^ WARN: format arguments are not allowed here use std::does_not_exist6; //~^ ERROR: unresolved import `std::does_not_exist6` diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.stderr b/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.stderr similarity index 70% rename from tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.stderr rename to tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.stderr index e8551479287e..2b942338ffb4 100644 --- a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect_format_string.stderr +++ b/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.stderr @@ -43,51 +43,51 @@ LL | use std::does_not_exist6; = note: foo {} warning: format arguments are not allowed here - --> $DIR/incorrect_format_string.rs:3:47 + --> $DIR/incorrect_format_string.rs:3:42 | -LL | #[diagnostic::on_unknown_item(message = "foo {}")] - | ^ +LL | #[diagnostic::on_unknown(message = "foo {}")] + | ^ | = help: consider removing this format argument = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: format arguments are not allowed here - --> $DIR/incorrect_format_string.rs:8:47 + --> $DIR/incorrect_format_string.rs:8:42 | -LL | #[diagnostic::on_unknown_item(message = "foo {A}")] - | ^ +LL | #[diagnostic::on_unknown(message = "foo {A}")] + | ^ | = help: consider removing this format argument warning: format arguments are not allowed here - --> $DIR/incorrect_format_string.rs:13:45 + --> $DIR/incorrect_format_string.rs:13:40 | -LL | #[diagnostic::on_unknown_item(label = "foo {}")] - | ^ +LL | #[diagnostic::on_unknown(label = "foo {}")] + | ^ | = help: consider removing this format argument warning: format arguments are not allowed here - --> $DIR/incorrect_format_string.rs:18:45 + --> $DIR/incorrect_format_string.rs:18:40 | -LL | #[diagnostic::on_unknown_item(label = "foo {A}")] - | ^ +LL | #[diagnostic::on_unknown(label = "foo {A}")] + | ^ | = help: consider removing this format argument warning: format arguments are not allowed here - --> $DIR/incorrect_format_string.rs:23:44 + --> $DIR/incorrect_format_string.rs:23:39 | -LL | #[diagnostic::on_unknown_item(note = "foo {}")] - | ^ +LL | #[diagnostic::on_unknown(note = "foo {}")] + | ^ | = help: consider removing this format argument warning: format arguments are not allowed here - --> $DIR/incorrect_format_string.rs:28:44 + --> $DIR/incorrect_format_string.rs:28:39 | -LL | #[diagnostic::on_unknown_item(note = "foo {A}")] - | ^ +LL | #[diagnostic::on_unknown(note = "foo {A}")] + | ^ | = help: consider removing this format argument diff --git a/tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.rs b/tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.rs new file mode 100644 index 000000000000..d8fcd1336bce --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.rs @@ -0,0 +1,19 @@ +#![feature(diagnostic_on_unknown)] +#[diagnostic::on_unknown] +//~^WARN missing options for `on_unknown` attribute +use std::str::FromStr; + +#[diagnostic::on_unknown(foo = "bar", message = "foo")] +//~^WARN malformed `on_unknown` attribute +use std::str::Bytes; + +#[diagnostic::on_unknown(label = "foo", label = "bar")] +//~^WARN `label` is ignored due to previous definition of `label` +use std::str::Chars; + +#[diagnostic::on_unknown(message = "Foo", message = "Bar")] +//~^WARN `message` is ignored due to previous definition of `message` +use std::str::NotExisting; +//~^ERROR Foo + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.stderr b/tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.stderr new file mode 100644 index 000000000000..319d45c88c42 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/malformed_attribute.stderr @@ -0,0 +1,44 @@ +error[E0432]: Foo + --> $DIR/malformed_attribute.rs:16:5 + | +LL | use std::str::NotExisting; + | ^^^^^^^^^^^^^^^^^^^^^ no `NotExisting` in `str` + | + = note: unresolved import `std::str::NotExisting` + +warning: missing options for `on_unknown` attribute + --> $DIR/malformed_attribute.rs:2:1 + | +LL | #[diagnostic::on_unknown] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: malformed `on_unknown` attribute + --> $DIR/malformed_attribute.rs:6:26 + | +LL | #[diagnostic::on_unknown(foo = "bar", message = "foo")] + | ^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: `label` is ignored due to previous definition of `label` + --> $DIR/malformed_attribute.rs:10:41 + | +LL | #[diagnostic::on_unknown(label = "foo", label = "bar")] + | ------------- ^^^^^^^^^^^^^ `label` is later redundantly declared here + | | + | `label` is first declared here + +warning: `message` is ignored due to previous definition of `message` + --> $DIR/malformed_attribute.rs:14:43 + | +LL | #[diagnostic::on_unknown(message = "Foo", message = "Bar")] + | --------------- ^^^^^^^^^^^^^^^ `message` is later redundantly declared here + | | + | `message` is first declared here + +error: aborting due to 1 previous error; 4 warnings emitted + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs b/tests/ui/diagnostic_namespace/on_unknown/multiple_errors.rs similarity index 83% rename from tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs rename to tests/ui/diagnostic_namespace/on_unknown/multiple_errors.rs index 431ab6cdd831..3ccf2fc5f6ca 100644 --- a/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs +++ b/tests/ui/diagnostic_namespace/on_unknown/multiple_errors.rs @@ -1,7 +1,7 @@ -#![feature(diagnostic_on_unknown_item)] +#![feature(diagnostic_on_unknown)] mod test1 { - #[diagnostic::on_unknown_item( + #[diagnostic::on_unknown( message = "custom message", label = "custom label", note = "custom note" @@ -11,7 +11,7 @@ mod test1 { } mod test2 { - #[diagnostic::on_unknown_item( + #[diagnostic::on_unknown( message = "custom message", label = "custom label", note = "custom note" @@ -21,7 +21,7 @@ mod test2 { } mod test3 { - #[diagnostic::on_unknown_item( + #[diagnostic::on_unknown( message = "custom message", label = "custom label", note = "custom note" @@ -34,7 +34,7 @@ mod test3 { } mod test4 { - #[diagnostic::on_unknown_item( + #[diagnostic::on_unknown( message = "custom message", label = "custom label", note = "custom note" diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr b/tests/ui/diagnostic_namespace/on_unknown/multiple_errors.stderr similarity index 100% rename from tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr rename to tests/ui/diagnostic_namespace/on_unknown/multiple_errors.stderr diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs b/tests/ui/diagnostic_namespace/on_unknown/unknown_import.rs similarity index 75% rename from tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs rename to tests/ui/diagnostic_namespace/on_unknown/unknown_import.rs index 5af79af23c2c..f2b0f059bb0a 100644 --- a/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs +++ b/tests/ui/diagnostic_namespace/on_unknown/unknown_import.rs @@ -1,9 +1,9 @@ -#![feature(diagnostic_on_unknown_item)] +#![feature(diagnostic_on_unknown)] pub mod foo { pub struct Bar; } -#[diagnostic::on_unknown_item( +#[diagnostic::on_unknown( message = "first message", label = "first label", note = "custom note", diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr b/tests/ui/diagnostic_namespace/on_unknown/unknown_import.stderr similarity index 100% rename from tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr rename to tests/ui/diagnostic_namespace/on_unknown/unknown_import.stderr diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs deleted file mode 100644 index 7b450f2fd4fa..000000000000 --- a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs +++ /dev/null @@ -1,52 +0,0 @@ -//@ run-pass -#![allow(dead_code, unused_imports)] -#![feature(diagnostic_on_unknown_item)] - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -extern crate std as other_std; - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -const CONST: () = (); - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -static STATIC: () = (); - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -type Type = (); - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -enum Enum {} - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -impl Enum {} - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -extern "C" {} - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -fn fun() {} - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -struct Struct {} - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -trait Trait {} - -#[diagnostic::on_unknown_item(message = "foo")] -//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements -impl Trait for i32 {} - -#[diagnostic::on_unknown_item(message = "foo")] -use std::str::FromStr; - -fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr deleted file mode 100644 index b09b6c90fd0e..000000000000 --- a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr +++ /dev/null @@ -1,103 +0,0 @@ -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:5:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | extern crate std as other_std; - | ----------------------------- not an import - | - = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:9:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | const CONST: () = (); - | --------------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:13:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | static STATIC: () = (); - | ----------------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:17:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | type Type = (); - | --------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:21:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | enum Enum {} - | --------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:25:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | impl Enum {} - | --------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:29:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | extern "C" {} - | ------------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:33:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | fn fun() {} - | -------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:37:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | struct Struct {} - | ------------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:41:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | trait Trait {} - | ----------- not an import - -warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements - --> $DIR/incorrect-locations.rs:45:1 - | -LL | #[diagnostic::on_unknown_item(message = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | impl Trait for i32 {} - | ------------------ not an import - -warning: 11 warnings emitted - diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs deleted file mode 100644 index 4ffa9ffe37b5..000000000000 --- a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![feature(diagnostic_on_unknown_item)] -#[diagnostic::on_unknown_item] -//~^WARN missing options for `on_unknown_item` attribute -use std::str::FromStr; - -#[diagnostic::on_unknown_item(foo = "bar", message = "foo")] -//~^WARN malformed `on_unknown_item` attribute -use std::str::Bytes; - -#[diagnostic::on_unknown_item(label = "foo", label = "bar")] -//~^WARN `label` is ignored due to previous definition of `label` -use std::str::Chars; - -#[diagnostic::on_unknown_item(message = "Foo", message = "Bar")] -//~^WARN `message` is ignored due to previous definition of `message` -use std::str::NotExisting; -//~^ERROR Foo - -fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr deleted file mode 100644 index 42caaa9354af..000000000000 --- a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error[E0432]: Foo - --> $DIR/malformed_attribute.rs:16:5 - | -LL | use std::str::NotExisting; - | ^^^^^^^^^^^^^^^^^^^^^ no `NotExisting` in `str` - | - = note: unresolved import `std::str::NotExisting` - -warning: missing options for `on_unknown_item` attribute - --> $DIR/malformed_attribute.rs:2:1 - | -LL | #[diagnostic::on_unknown_item] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: at least one of the `message`, `note` and `label` options are expected - = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: malformed `on_unknown_item` attribute - --> $DIR/malformed_attribute.rs:6:31 - | -LL | #[diagnostic::on_unknown_item(foo = "bar", message = "foo")] - | ^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - -warning: `label` is ignored due to previous definition of `label` - --> $DIR/malformed_attribute.rs:10:46 - | -LL | #[diagnostic::on_unknown_item(label = "foo", label = "bar")] - | ------------- ^^^^^^^^^^^^^ `label` is later redundantly declared here - | | - | `label` is first declared here - -warning: `message` is ignored due to previous definition of `message` - --> $DIR/malformed_attribute.rs:14:48 - | -LL | #[diagnostic::on_unknown_item(message = "Foo", message = "Bar")] - | --------------- ^^^^^^^^^^^^^^^ `message` is later redundantly declared here - | | - | `message` is first declared here - -error: aborting due to 1 previous error; 4 warnings emitted - -For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.rs similarity index 76% rename from tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs rename to tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.rs index fffb54636cfb..11cc0d50e0c9 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.rs @@ -1,6 +1,6 @@ #![deny(warnings)] -#[diagnostic::on_unknown_item(message = "Tada")] +#[diagnostic::on_unknown(message = "Tada")] //~^ ERROR: unknown diagnostic attribute use std::vec::NotExisting; //~^ ERROR: unresolved import `std::vec::NotExisting` diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr similarity index 64% rename from tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr rename to tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr index 10662eb83b4a..f6d7ffadacea 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr @@ -1,17 +1,17 @@ error[E0432]: unresolved import `std::vec::NotExisting` - --> $DIR/feature-gate-diagnostic-on-unknown-item.rs:5:5 + --> $DIR/feature-gate-diagnostic-on-unknown.rs:5:5 | LL | use std::vec::NotExisting; | ^^^^^^^^^^^^^^^^^^^^^ no `NotExisting` in `vec` error: unknown diagnostic attribute - --> $DIR/feature-gate-diagnostic-on-unknown-item.rs:3:15 + --> $DIR/feature-gate-diagnostic-on-unknown.rs:3:15 | -LL | #[diagnostic::on_unknown_item(message = "Tada")] - | ^^^^^^^^^^^^^^^ +LL | #[diagnostic::on_unknown(message = "Tada")] + | ^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/feature-gate-diagnostic-on-unknown-item.rs:1:9 + --> $DIR/feature-gate-diagnostic-on-unknown.rs:1:9 | LL | #![deny(warnings)] | ^^^^^^^^ From 2c827319f6f6585e1481ccd1893cce538eee68b8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Apr 2026 14:58:23 +0200 Subject: [PATCH 09/10] Make `rustc_attr_parsing::SharedContext::emit_lint` take a `MultiSpan` instead of a `Span` --- compiler/rustc_attr_parsing/src/context.rs | 13 +++++++++---- compiler/rustc_attr_parsing/src/interface.rs | 6 +++--- compiler/rustc_attr_parsing/src/safety.rs | 5 +++-- compiler/rustc_error_messages/src/lib.rs | 6 +++--- compiler/rustc_hir/src/lints.rs | 4 ++-- compiler/rustc_interface/src/passes.rs | 2 +- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6ab3f98e2015..ffcf02465197 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -7,7 +7,7 @@ use private::Sealed; use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; -use rustc_errors::{Diag, Diagnostic, Level}; +use rustc_errors::{Diag, Diagnostic, Level, MultiSpan}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; @@ -455,14 +455,19 @@ pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuarant /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing /// must be delayed until after HIR is built. This method will take care of the details of /// that. - pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) { + pub(crate) fn emit_lint>( + &mut self, + lint: &'static Lint, + kind: AttributeLintKind, + span: M, + ) { if !matches!( self.stage.should_emit(), ShouldEmit::ErrorsAndLints { .. } | ShouldEmit::EarlyFatal { also_emit_lints: true } ) { return; } - (self.emit_lint)(LintId::of(lint), span, kind); + (self.emit_lint)(LintId::of(lint), span.into(), kind); } pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { @@ -528,7 +533,7 @@ pub struct SharedContext<'p, 'sess, S: Stage> { /// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S` /// is `Late` and is the ID of the syntactical component this attribute was applied to. - pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, AttributeLintKind), + pub(crate) emit_lint: &'p mut dyn FnMut(LintId, MultiSpan, AttributeLintKind), } /// Context given to every attribute parser during finalization. diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f66d6dd3f4c9..fb4e8e602486 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -3,7 +3,7 @@ use rustc_ast as ast; use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety}; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxtHandle, MultiSpan}; use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; @@ -195,7 +195,7 @@ pub fn parse_single_args( sess, stage: Early { emit_errors }, }; - let mut emit_lint = |lint_id: LintId, span: Span, kind: AttributeLintKind| { + let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: AttributeLintKind| { sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind) }; if let Some(safety) = attr_safety { @@ -256,7 +256,7 @@ pub fn parse_attribute_list( target: Target, omit_doc: OmitDoc, lower_span: impl Copy + Fn(Span) -> Span, - mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind), + mut emit_lint: impl FnMut(LintId, MultiSpan, AttributeLintKind), ) -> Vec { let mut attributes = Vec::new(); // We store the attributes we intend to discard at the end of this function in order to diff --git a/compiler/rustc_attr_parsing/src/safety.rs b/compiler/rustc_attr_parsing/src/safety.rs index 4cc703c5d0cc..262c9c7723ee 100644 --- a/compiler/rustc_attr_parsing/src/safety.rs +++ b/compiler/rustc_attr_parsing/src/safety.rs @@ -1,4 +1,5 @@ use rustc_ast::Safety; +use rustc_errors::MultiSpan; use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir::AttrPath; use rustc_hir::lints::AttributeLintKind; @@ -15,7 +16,7 @@ pub fn check_attribute_safety( attr_path: &AttrPath, attr_span: Span, attr_safety: Safety, - emit_lint: &mut impl FnMut(LintId, Span, AttributeLintKind), + emit_lint: &mut impl FnMut(LintId, MultiSpan, AttributeLintKind), ) { if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { return; @@ -83,7 +84,7 @@ pub fn check_attribute_safety( } else { emit_lint( LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE), - path_span, + path_span.into(), AttributeLintKind::UnsafeAttrOutsideUnsafe { attribute_name_span: path_span, sugg_spans: not_from_proc_macro diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index a2c026706ada..a1128f18043d 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -7,7 +7,7 @@ pub use fluent_bundle::types::FluentType; pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; -use rustc_macros::{Decodable, Encodable}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::Span; pub use unic_langid::{LanguageIdentifier, langid}; @@ -28,7 +28,7 @@ pub fn register_functions(bundle: &mut fluent_bundle::bundle::FluentBundle /// diagnostic messages. /// /// Intended to be removed once diagnostics are entirely translatable. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] #[rustc_diagnostic_item = "DiagMessage"] pub enum DiagMessage { /// Non-translatable diagnostic message or a message that has been translated eagerly. @@ -89,7 +89,7 @@ pub struct SpanLabel { /// the error, and would be rendered with `^^^`. /// - They can have a *label*. In this case, the label is written next /// to the mark in the snippet when we render. -#[derive(Clone, Debug, Hash, PartialEq, Eq, Encodable, Decodable)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Encodable, Decodable, HashStable_Generic)] pub struct MultiSpan { primary_spans: Vec, span_labels: Vec<(Span, DiagMessage)>, diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 1589a6de220e..23eda1a0355e 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fingerprint::Fingerprint; +use rustc_error_messages::MultiSpan; use rustc_lint_defs::LintId; pub use rustc_lint_defs::{AttributeLintKind, FormatWarning}; use rustc_macros::HashStable_Generic; -use rustc_span::Span; use crate::HirId; @@ -28,6 +28,6 @@ pub enum DelayedLint { pub struct AttributeLint { pub lint_id: LintId, pub id: Id, - pub span: Span, + pub span: MultiSpan, pub kind: AttributeLintKind, } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index eadb099a3e1a..f75bb0de91db 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1037,7 +1037,7 @@ pub fn emit_delayed_lints(tcx: TyCtxt<'_>) { tcx.emit_node_span_lint( attribute_lint.lint_id.lint, attribute_lint.id, - attribute_lint.span, + attribute_lint.span.clone(), DecorateAttrLint { sess: tcx.sess, tcx: Some(tcx), From 3c6cf27ae423145e25bc9f6d88cd57d54d29e9a9 Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Fri, 10 Apr 2026 21:45:26 +0800 Subject: [PATCH 10/10] Reject dangling attributes in where clauses --- compiler/rustc_parse/src/errors.rs | 8 +++++++ compiler/rustc_parse/src/parser/generics.rs | 11 ++++++++++ .../where-clause-attrs-without-predicate.rs | 21 +++++++++++++++++++ ...here-clause-attrs-without-predicate.stderr | 17 +++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 tests/ui/parser/where-clause-attrs-without-predicate.rs create mode 100644 tests/ui/parser/where-clause-attrs-without-predicate.stderr diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 6faaafcc0100..cc1e0ff85dae 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1304,6 +1304,14 @@ pub(crate) struct ExpectedStatementAfterOuterAttr { pub span: Span, } +#[derive(Diagnostic)] +#[diag("attribute without where predicates")] +pub(crate) struct AttrWithoutWherePredicates { + #[primary_span] + #[label("attributes are only permitted when preceding predicates")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("found a documentation comment that doesn't document anything", code = E0585)] #[help("doc comments must come before what they document, if a comment was intended use `//`")] diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 8c02092fd678..969c8548f68b 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -473,6 +473,17 @@ fn parse_where_clause_common( } } } else { + if let [.., last] = &attrs[..] { + if last.is_doc_comment() { + this.dcx().emit_err(errors::DocCommentDoesNotDocumentAnything { + span: last.span, + missing_comma: None, + }); + } else { + this.dcx() + .emit_err(errors::AttrWithoutWherePredicates { span: last.span }); + } + } None }; let predicate = kind.map(|kind| ast::WherePredicate { diff --git a/tests/ui/parser/where-clause-attrs-without-predicate.rs b/tests/ui/parser/where-clause-attrs-without-predicate.rs new file mode 100644 index 000000000000..367a4dcd4d34 --- /dev/null +++ b/tests/ui/parser/where-clause-attrs-without-predicate.rs @@ -0,0 +1,21 @@ +// Regression test for + +#![crate_type = "lib"] +#![feature(where_clause_attrs)] + +fn f() +where + T: Copy, + #[cfg(true)] + #[cfg(false)] + //~^ ERROR attribute without where predicates +{ +} + +fn g() +where + T: Copy, + /// dangling + //~^ ERROR found a documentation comment that doesn't document anything +{ +} diff --git a/tests/ui/parser/where-clause-attrs-without-predicate.stderr b/tests/ui/parser/where-clause-attrs-without-predicate.stderr new file mode 100644 index 000000000000..c4914238a5db --- /dev/null +++ b/tests/ui/parser/where-clause-attrs-without-predicate.stderr @@ -0,0 +1,17 @@ +error: attribute without where predicates + --> $DIR/where-clause-attrs-without-predicate.rs:10:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ attributes are only permitted when preceding predicates + +error[E0585]: found a documentation comment that doesn't document anything + --> $DIR/where-clause-attrs-without-predicate.rs:18:5 + | +LL | /// dangling + | ^^^^^^^^^^^^ + | + = help: doc comments must come before what they document, if a comment was intended use `//` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0585`.