diff --git a/Cargo.lock b/Cargo.lock index d9f3e90d652e5..4564a5b5270a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3505,6 +3505,7 @@ dependencies = [ "rand_xoshiro", "rustc_data_structures", "rustc_error_messages", + "rustc_errors", "rustc_hashes", "rustc_index", "rustc_macros", @@ -3909,7 +3910,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 f148b776852aa..f28c9aa266737 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")] @@ -332,7 +334,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 }, @@ -343,6 +345,51 @@ pub enum TargetDataLayoutErrors<'a> { UnknownPointerSpecification { err: String }, } +#[cfg(feature = "nightly")] +impl Diagnostic<'_, G> for TargetDataLayoutError<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { + match self { + 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) + } + 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) + } + TargetDataLayoutError::MissingAlignment { cause } => { + Diag::new(dcx, level, msg!("missing alignment for `{$cause}` in \"data-layout\"")) + .with_arg("cause", cause) + } + 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()) + } + 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) + } + 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) + } + TargetDataLayoutError::InvalidBitsSize { err } => { + Diag::new(dcx, level, msg!("{$err}")).with_arg("err", err) + } + TargetDataLayoutError::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) @@ -352,17 +399,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, @@ -378,7 +425,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)?) @@ -388,7 +435,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) }; @@ -426,7 +473,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(), }); } @@ -471,7 +518,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_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..f68bed620f1b3 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; #[derive(Copy, Clone)] pub(crate) enum Mode { @@ -35,6 +36,8 @@ pub(crate) enum Mode { DiagnosticOnConst, /// `#[diagnostic::on_move]` DiagnosticOnMove, + /// `#[diagnostic::on_unknown]` + DiagnosticOnUnknown, } fn merge_directives( @@ -122,6 +125,13 @@ fn parse_directive_items<'p, S: Stage>( span, ); } + Mode::DiagnosticOnUnknown => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnknownAttr { 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::DiagnosticOnUnknown => { 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::DiagnosticOnUnknown) { + 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.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs new file mode 100644 index 0000000000000..bd5eb4cbf82c7 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs @@ -0,0 +1,73 @@ +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 OnUnknownParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl OnUnknownParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + if !cx.features().diagnostic_on_unknown() { + return; + } + 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::MissingOptionsForOnUnknown, + span, + ); + return; + } + ArgParser::NameValue(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnknownAttr { 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 OnUnknownParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_unknown], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::DiagnosticOnUnknown); + }, + )]; + //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 { + if let Some(span) = self.span { + Some(AttributeKind::OnUnknown { + 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..3fde8d79f5143 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::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -154,6 +155,7 @@ attribute_parsers!( OnConstParser, OnMoveParser, OnUnimplementedParser, + OnUnknownParser, 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_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b8eefa4dd0714..ffe5e15c48ec1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3538,6 +3538,24 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Applicability::MaybeIncorrect, ); } + + 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") { + 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/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_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_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 2560ac50bba42..386467c3e1edb 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -9,7 +9,6 @@ annotate-snippets = { version = "0.12.15", 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, 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..3edc19e4314ca 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 => features.diagnostic_on_unknown(), _ => false, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 859a1ad391cb9..c2fe6e1360201 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, "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..c94444893f1e3 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]` + OnUnknown { + 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..861d74766ccb9 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, + OnUnknown { .. } => 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..1b31639c40785 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::MalformedOnUnknownAttr { span } => { + lints::MalformedOnUnknownAttrLint { 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::MissingOptionsForOnUnknown => { + lints::MissingOptionsForOnUnknownAttr.into_diag(dcx, level) + } } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 4279ab230df55..19ec3f4ca45d1 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` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnUnknownAttr; + #[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` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] +pub(crate) struct MalformedOnUnknownAttrLint { + #[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..a77b7bc7d9483 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, }, + MalformedOnUnknownAttr { + span: Span, + }, MalformedOnConstAttr { span: Span, }, @@ -757,6 +760,7 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, + MissingOptionsForOnUnknown, 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_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/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6aeb0ae57e752..c498728028786 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]` can only be applied to `use` statements")] +struct DiagnosticOnUnknownOnlyForImports { + #[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::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()) @@ -727,6 +735,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// 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, + DiagnosticOnUnknownOnlyForImports { 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 910d9c909f96a..50977ba6cff5f 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, OnUnknownData}; 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_attr: OnUnknownData::from_attrs(self.r.tcx, item), }); self.r.indeterminate_imports.push(import); @@ -1030,6 +1031,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { module_path: Vec::new(), vis, vis_span: item.vis.span, + on_unknown_attr: OnUnknownData::from_attrs(self.r.tcx, item), }); if used { self.r.import_use_map.insert(import, Used::Other); @@ -1125,7 +1127,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, @@ -1162,6 +1164,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_attr: OnUnknownData::from_attrs(this.r.tcx, item), }) }; @@ -1333,6 +1336,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { module_path: Vec::new(), vis, vis_span: item.vis.span, + 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 d51ce9fb7946b..18db60167c27c 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 OnUnknownData { + directive: 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.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]` attribute applied + /// to the given import. This allows crates to specify + /// custom error messages for a specific import + pub on_unknown_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_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_attr: import.on_unknown_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() + && let Some(directive) = errors[0].1.on_unknown_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_attr: import.on_unknown_attr.clone(), }, None => UnresolvedImportError { span, @@ -1110,6 +1178,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: Some(segment_name), module, + on_unknown_attr: import.on_unknown_attr.clone(), }, }; return Some(err); @@ -1152,6 +1221,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: None, module: None, + on_unknown_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_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 619e61211984d..67a896bdd7557 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, self.tcx.features().diagnostic_on_unknown()), + ]; 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..0b88c67979249 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, @@ -799,6 +800,7 @@ symbols! { diagnostic_namespace, diagnostic_on_const, diagnostic_on_move, + diagnostic_on_unknown, dialect, direct, discriminant_kind, @@ -1418,6 +1420,7 @@ symbols! { on_const, on_move, on_unimplemented, + on_unknown, opaque, opaque_module_name_placeholder: "", ops, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 68d6162bd590e..0601f9d7c2cfb 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}; @@ -2166,7 +2166,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, @@ -2174,7 +2174,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(), }); @@ -2183,7 +2183,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, }); @@ -2192,7 +2192,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) } diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 03d81e010f7be..4eaeafe127078 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/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/incorrect_format_string.rs b/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.rs new file mode 100644 index 0000000000000..cdf0f1e89efc1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.rs @@ -0,0 +1,33 @@ +#![feature(diagnostic_on_unknown)] + +#[diagnostic::on_unknown(message = "foo {}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist; +//~^ ERROR: foo {} + +#[diagnostic::on_unknown(message = "foo {A}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist2; +//~^ ERROR: 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(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(note = "foo {}")] +//~^ WARN: format arguments are not allowed here +use std::does_not_exist5; +//~^ ERROR: unresolved import `std::does_not_exist5` + +#[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` + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.stderr b/tests/ui/diagnostic_namespace/on_unknown/incorrect_format_string.stderr new file mode 100644 index 0000000000000..2b942338ffb4e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/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:42 + | +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:42 + | +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:40 + | +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:40 + | +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:39 + | +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:39 + | +LL | #[diagnostic::on_unknown(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/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/multiple_errors.rs b/tests/ui/diagnostic_namespace/on_unknown/multiple_errors.rs new file mode 100644 index 0000000000000..3ccf2fc5f6cab --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/multiple_errors.rs @@ -0,0 +1,48 @@ +#![feature(diagnostic_on_unknown)] + +mod test1 { + #[diagnostic::on_unknown( + message = "custom message", + label = "custom label", + note = "custom note" + )] + use std::vec::{NonExisting, Vec, Whatever}; + //~^ ERROR: custom message +} + +mod test2 { + #[diagnostic::on_unknown( + message = "custom message", + label = "custom label", + note = "custom note" + )] + use std::{Whatever, vec::NonExisting, vec::Vec, *}; + //~^ ERROR: custom message +} + +mod test3 { + #[diagnostic::on_unknown( + message = "custom message", + label = "custom label", + note = "custom note" + )] + use std::{ + string::String, + vec::{NonExisting, Vec}, + //~^ ERROR: custom message + }; +} + +mod test4 { + #[diagnostic::on_unknown( + 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/multiple_errors.stderr b/tests/ui/diagnostic_namespace/on_unknown/multiple_errors.stderr new file mode 100644 index 0000000000000..fcce77f6aebbe --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/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/unknown_import.rs b/tests/ui/diagnostic_namespace/on_unknown/unknown_import.rs new file mode 100644 index 0000000000000..f2b0f059bb0a8 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/unknown_import.rs @@ -0,0 +1,17 @@ +#![feature(diagnostic_on_unknown)] +pub mod foo { + pub struct Bar; +} + +#[diagnostic::on_unknown( + 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/unknown_import.stderr b/tests/ui/diagnostic_namespace/on_unknown/unknown_import.stderr new file mode 100644 index 0000000000000..a9867fd74bfb0 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown/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.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.rs new file mode 100644 index 0000000000000..11cc0d50e0c94 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.rs @@ -0,0 +1,8 @@ +#![deny(warnings)] + +#[diagnostic::on_unknown(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.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr new file mode 100644 index 0000000000000..f6d7ffadaceae --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr @@ -0,0 +1,22 @@ +error[E0432]: unresolved import `std::vec::NotExisting` + --> $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.rs:3:15 + | +LL | #[diagnostic::on_unknown(message = "Tada")] + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/feature-gate-diagnostic-on-unknown.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`. 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`. 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`.