From b711f5689fd8314734fb32431b5d4f9dea0729e0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Mar 2026 10:06:21 +1100 Subject: [PATCH 1/8] 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 d4c1a02c018af..4426ad75eee98 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 83d96d8d04daf..13e0bd8703d9a 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 253dff6f8e75c..eca49bb71dd95 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -46,6 +46,8 @@ use std::str::FromStr; 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 a81fc496c8288..58303c83e7cde 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 e50cbbbf06e0b..ba7569c51a07b 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 @@ impl IntoDiagArg for DiagSymbolList { } } -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 2/8] 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 eca49bb71dd95..0b5513e545573 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -340,7 +340,7 @@ impl Default for 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 @@ impl TargetDataLayout { 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 @@ impl TargetDataLayout { // 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 @@ impl TargetDataLayout { // 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 @@ impl TargetDataLayout { // 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 ced4b59c4f0ce..1c266382d0279 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 @@ impl CompiledModules { 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 ff7e170543bea..bb9c63d224327 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -28,7 +28,7 @@ use std::{env, str}; 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 14746da57c47c..76186eeab89cc 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -47,7 +47,7 @@ use std::str::FromStr; 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 @@ impl Target { // Perform consistency checks against the Target information. if dl.endian != self.endian { - return Err(TargetDataLayoutErrors::InconsistentTargetArchitecture { + return Err(TargetDataLayoutError::InconsistentTargetArchitecture { dl: dl.endian.as_str(), target: self.endian.as_str(), }); @@ -2201,7 +2201,7 @@ impl Target { let target_pointer_width: u64 = self.pointer_width.into(); let dl_pointer_size: u64 = dl.pointer_size().bits(); if dl_pointer_size != target_pointer_width { - return Err(TargetDataLayoutErrors::InconsistentTargetPointerWidth { + return Err(TargetDataLayoutError::InconsistentTargetPointerWidth { pointer_size: dl_pointer_size, target: self.pointer_width, }); @@ -2210,7 +2210,7 @@ impl Target { dl.c_enum_min_size = Integer::from_size(Size::from_bits( self.c_enum_min_bits.unwrap_or(self.c_int_width as _), )) - .map_err(|err| TargetDataLayoutErrors::InvalidBitsSize { err })?; + .map_err(|err| TargetDataLayoutError::InvalidBitsSize { err })?; Ok(dl) } From beee9e71c6f0085d72fad03963a7bd7882ebad62 Mon Sep 17 00:00:00 2001 From: Anne Stijns Date: Sun, 3 Aug 2025 21:57:44 +0200 Subject: [PATCH 3/8] 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 cddb37c7d816f..e438da1f629a2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3530,6 +3530,24 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { 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 0000000000000..03e828bf3ebf9 --- /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 0000000000000..02bcebd4faf5e --- /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 4/8] 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 e438da1f629a2..4fbb172f0f94f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3531,9 +3531,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } - 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 7b359dcd6b252..47cf9bdffa1bb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -196,6 +196,7 @@ symbols! { 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 7d579d85d8087..71e62f0474634 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -50,7 +50,6 @@ 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 03e828bf3ebf9..0000000000000 --- 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 02bcebd4faf5e..0000000000000 --- 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 0000000000000..8d1019431df0f --- /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 0000000000000..8d30af9589133 --- /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 6e5fc9075cf6e70f4a211e42d5670fa723457660 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Sun, 9 Feb 2025 18:29:32 +0100 Subject: [PATCH 5/8] 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 1b615b611258f..4e3310d3fb097 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 e63baf77c0852..61fd3f0962488 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 do_not_recommend; 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 0000000000000..a7abecc671ec3 --- /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 6ab3f98e2015c..c10877ce72e67 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::do_not_recommend::*; 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 @@ attribute_parsers!( 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 f66d6dd3f4c9c..a2a2967f9ad6c 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 @@ impl<'sess> AttributeParser<'sess, Early> { 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 @@ impl<'sess> AttributeParser<'sess, Early> { 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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { 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 63c06e672727d..ae0078523adbc 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 @@ impl<'a> TraitDef<'a> { 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 24a5d79958c60..84f2a8e35b02c 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 @@ impl<'a> CollectProcMacros<'a> { })) = 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 5764dfc83927a..071b807109b71 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 1bb6d8a6bfd05..1c947ea07d1a9 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 ec5951e50e3a8..87e157babe8dc 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 acbcba90fbcc0..5d5018ce448e2 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 859a1ad391cb9..7886a4fcac0df 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -474,6 +474,8 @@ declare_features! ( (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 a18ddff947099..6fa3df2dfa7d1 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 c19fc6976c6e6..699caa203ae4b 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 @@ impl AttributeKind { 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 eadb099a3e1a2..f450ddfc698e6 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 af590d98c301c..66082d782e0bb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -312,7 +312,7 @@ impl EarlyLintPass for UnsafeCode { 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 776313a7e8040..0171cf9ebd72b 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -179,6 +179,9 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { &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 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { 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 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { &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 4279ab230df55..9f7dbae70ec5b 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 1fd6699e2506e..297dfac4a5f78 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -145,7 +145,7 @@ impl NonCamelCaseTypes { 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 2cec2ed06a502..5497e2509eaa0 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 6aeb0ae57e752..f0495fb820c90 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 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, 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 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// 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 7211f3cf85b31..828ba698e0f2f 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -25,7 +25,7 @@ impl DebuggerVisualizerCollector<'_> { 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 d00c306329b7e..7eba016d44746 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -32,7 +32,7 @@ use tracing::debug; 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { AttributeParser::parse_limited( self.r.tcx.sess, &item.attrs, - sym::macro_use, + &[sym::macro_use], item.span, item.id, None, @@ -1158,6 +1160,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 d51ce9fb7946b..846efdf22b97c 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 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { } } +#[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 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: None, module: None, + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }; errors.push((*import, err)) } @@ -822,11 +857,41 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { 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 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { 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 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: Some(segment_name), module, + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }, None => UnresolvedImportError { span, @@ -1110,6 +1178,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: Some(segment_name), module, + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }, }; return Some(err); @@ -1152,6 +1221,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: None, module: None, + on_unknown_item_attr: None, }); } if let Some(max_vis) = max_vis.get() @@ -1374,6 +1444,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }), segment: Some(ident.name), + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }) } else { // `resolve_ident_in_module` reported a privacy error. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 619e61211984d..d9c71ed2f4338 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -140,7 +140,7 @@ pub fn registered_tools_ast( AttributeParser::parse_limited( sess, pre_configured_attrs, - sym::register_tool, + &[sym::register_tool], DUMMY_SP, DUMMY_NODE_ID, Some(features), @@ -712,17 +712,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); } - const DIAG_ATTRS: &[Symbol] = - &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const, sym::on_move]; + let diagnostic_attributes: &[(Symbol, bool)] = &[ + (sym::on_unimplemented, true), + (sym::do_not_recommend, true), + (sym::on_move, true), + (sym::on_const, self.tcx.features().diagnostic_on_const()), + (sym::on_unknown_item, self.tcx.features().diagnostic_on_unknown_item()), + ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && !DIAG_ATTRS.contains(&attribute.ident.name) + && !diagnostic_attributes + .iter() + .any(|(attr, stable)| *stable && attribute.ident.name == *attr) { let span = attribute.span(); - - let typo = find_best_match_for_name(DIAG_ATTRS, attribute.ident.name, Some(5)) + let candidates = diagnostic_attributes + .iter() + .filter_map(|(sym, stable)| stable.then_some(*sym)) + .collect::>(); + 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 33bc5a578e8b6..d4ec05d6447bd 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -799,6 +799,7 @@ symbols! { diagnostic_namespace, diagnostic_on_const, diagnostic_on_move, + diagnostic_on_unknown_item, dialect, direct, discriminant_kind, @@ -1418,6 +1419,7 @@ symbols! { 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 0000000000000..7b450f2fd4fac --- /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 0000000000000..b09b6c90fd0e2 --- /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 0000000000000..d7d6f1845060d --- /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 0000000000000..e8551479287eb --- /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 0000000000000..4ffa9ffe37b5b --- /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 0000000000000..42caaa9354afa --- /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 0000000000000..431ab6cdd831a --- /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 0000000000000..fcce77f6aebbe --- /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 0000000000000..5af79af23c2cc --- /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 0000000000000..a9867fd74bfb0 --- /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 0000000000000..fffb54636cfb7 --- /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 0000000000000..10662eb83b4a5 --- /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 6/8] 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 a7abecc671ec3..f2a243a8e0bba 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 @@ impl OnUnknownItemParser { 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 7/8] 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 61fd3f0962488..f68bed620f1b3 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 do_not_recommend; 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 f2a243a8e0bba..bd5eb4cbf82c7 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::diagnostic::*; 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 @@ impl OnUnknownItemParser { ArgParser::NoArgs | ArgParser::List(_) => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - AttributeLintKind::MissingOptionsForOnUnknownItem, + AttributeLintKind::MissingOptionsForOnUnknown, span, ); return; @@ -36,7 +36,7 @@ impl OnUnknownItemParser { ArgParser::NameValue(_) => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - AttributeLintKind::MalformedOnUnknownItemdAttr { span }, + AttributeLintKind::MalformedOnUnknownAttr { span }, span, ); return; @@ -49,12 +49,12 @@ impl OnUnknownItemParser { } } -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 c10877ce72e67..3fde8d79f5143 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::do_not_recommend::*; 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 @@ attribute_parsers!( 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 5d5018ce448e2..3edc19e4314ca 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 7886a4fcac0df..c2fe6e1360201 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -475,7 +475,7 @@ declare_features! ( /// 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 6fa3df2dfa7d1..c94444893f1e3 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 699caa203ae4b..861d74766ccb9 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 @@ impl AttributeKind { 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 0171cf9ebd72b..1b31639c40785 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -179,8 +179,8 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { &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 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { &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 9f7dbae70ec5b..19ec3f4ca45d1 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 5497e2509eaa0..a77b7bc7d9483 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 f0495fb820c90..c498728028786 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 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, 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 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// 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 7eba016d44746..983a7a201b107 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -32,7 +32,7 @@ use tracing::debug; 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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 846efdf22b97c..18db60167c27c 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -145,17 +145,17 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { } #[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 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { 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 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .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 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: Some(segment_name), module, - on_unknown_item_attr: import.on_unknown_item_attr.clone(), + on_unknown_attr: import.on_unknown_attr.clone(), }, None => UnresolvedImportError { span, @@ -1178,7 +1178,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: Some(segment_name), module, - on_unknown_item_attr: import.on_unknown_item_attr.clone(), + on_unknown_attr: import.on_unknown_attr.clone(), }, }; return Some(err); @@ -1221,7 +1221,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: None, module: None, - on_unknown_item_attr: None, + on_unknown_attr: None, }); } if let Some(max_vis) = max_vis.get() @@ -1444,7 +1444,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }), segment: Some(ident.name), - on_unknown_item_attr: import.on_unknown_item_attr.clone(), + on_unknown_attr: import.on_unknown_attr.clone(), }) } else { // `resolve_ident_in_module` reported a privacy error. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d9c71ed2f4338..67a896bdd7557 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -717,7 +717,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (sym::do_not_recommend, true), (sym::on_move, true), (sym::on_const, self.tcx.features().diagnostic_on_const()), - (sym::on_unknown_item, self.tcx.features().diagnostic_on_unknown_item()), + (sym::on_unknown, self.tcx.features().diagnostic_on_unknown()), ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d4ec05d6447bd..f128427cc675d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -799,7 +799,7 @@ symbols! { diagnostic_namespace, diagnostic_on_const, diagnostic_on_move, - diagnostic_on_unknown_item, + diagnostic_on_unknown, dialect, direct, discriminant_kind, @@ -1419,7 +1419,7 @@ symbols! { on_const, on_move, on_unimplemented, - on_unknown_item, + on_unknown, opaque, opaque_module_name_placeholder: "", 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 0000000000000..b8852e7dd216c --- /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 0000000000000..33636e1fcfc3f --- /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 d7d6f1845060d..cdf0f1e89efc1 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 e8551479287eb..2b942338ffb4e 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 0000000000000..d8fcd1336bce1 --- /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 0000000000000..319d45c88c429 --- /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 431ab6cdd831a..3ccf2fc5f6cab 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 5af79af23c2cc..f2b0f059bb0a8 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 7b450f2fd4fac..0000000000000 --- 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 b09b6c90fd0e2..0000000000000 --- 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 4ffa9ffe37b5b..0000000000000 --- 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 42caaa9354afa..0000000000000 --- 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 fffb54636cfb7..11cc0d50e0c94 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 10662eb83b4a5..f6d7ffadaceae 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 3c6cf27ae423145e25bc9f6d88cd57d54d29e9a9 Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Fri, 10 Apr 2026 21:45:26 +0800 Subject: [PATCH 8/8] 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 6faaafcc01005..cc1e0ff85dae4 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 8c02092fd6788..969c8548f68b4 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -473,6 +473,17 @@ impl<'a> Parser<'a> { } } } 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 0000000000000..367a4dcd4d344 --- /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 0000000000000..c4914238a5dbd --- /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`.