diff options
137 files changed, 3104 insertions, 1191 deletions
diff --git a/Cargo.lock b/Cargo.lock index 80f0a0b8b5b..d8cb1133c73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,6 +1239,40 @@ dependencies = [ ] [[package]] +name = "fluent-bundle" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +dependencies = [ + "thiserror", +] + +[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1783,6 +1817,26 @@ dependencies = [ ] [[package]] +name = "intl-memoizer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf" +dependencies = [ + "tinystr", + "unic-langid", +] + +[[package]] name = "itertools" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2813,6 +2867,12 @@ dependencies = [ ] [[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] name = "proc-macro2" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3646,12 +3706,28 @@ name = "rustc_error_codes" version = "0.0.0" [[package]] +name = "rustc_error_messages" +version = "0.0.0" +dependencies = [ + "fluent-bundle", + "fluent-syntax", + "intl-memoizer", + "rustc_data_structures", + "rustc_macros", + "rustc_serialize", + "rustc_span", + "tracing", + "unic-langid", +] + +[[package]] name = "rustc_errors" version = "0.0.0" dependencies = [ "annotate-snippets", "atty", "rustc_data_structures", + "rustc_error_messages", "rustc_lint_defs", "rustc_macros", "rustc_serialize", @@ -3708,6 +3784,7 @@ dependencies = [ "odht", "rustc_ast", "rustc_data_structures", + "rustc_error_messages", "rustc_feature", "rustc_index", "rustc_macros", @@ -3864,6 +3941,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_data_structures", + "rustc_error_messages", "rustc_hir", "rustc_macros", "rustc_serialize", @@ -4574,6 +4652,12 @@ dependencies = [ ] [[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + +[[package]] name = "semver" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5105,6 +5189,12 @@ dependencies = [ ] [[package]] +name = "tinystr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1" + +[[package]] name = "tinyvec" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5263,6 +5353,15 @@ dependencies = [ ] [[package]] +name = "type-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +dependencies = [ + "rustc-hash", +] + +[[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5317,6 +5416,49 @@ dependencies = [ ] [[package]] +name = "unic-langid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5" +dependencies = [ + "unic-langid-impl", + "unic-langid-macros", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d" +dependencies = [ + "tinystr", +] + +[[package]] +name = "unic-langid-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5" +dependencies = [ + "proc-macro-hack", + "tinystr", + "unic-langid-impl", + "unic-langid-macros-impl", +] + +[[package]] +name = "unic-langid-macros-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f" +dependencies = [ + "proc-macro-hack", + "quote", + "syn", + "unic-langid-impl", +] + +[[package]] name = "unic-ucd-version" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 21b582ef334..70f7f1e493e 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -1,6 +1,6 @@ -use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed}; +use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, MultiSpan}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { crate fn cannot_move_when_borrowed( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 883f711b201..aa2ddada350 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1,7 +1,7 @@ use either::Either; use rustc_const_eval::util::CallKind; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; @@ -15,7 +15,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::symbol::sym; -use rustc_span::{BytePos, MultiSpan, Span}; +use rustc_span::{BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::TraitEngineExt as _; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index e63450a1f58..1798c525f6d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -330,14 +330,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ty::RawPtr(ty_mut) => { assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut); ( - format!("a mutable pointer to {}", ty_mut.ty), + format!("a mutable pointer to `{}`", ty_mut.ty), "mutable pointers are invariant over their type parameter".to_string(), ) } ty::Ref(_, inner_ty, mutbl) => { assert_eq!(*mutbl, rustc_hir::Mutability::Mut); ( - format!("a mutable reference to {}", inner_ty), + format!("a mutable reference to `{}`", inner_ty), "mutable references are invariant over their type parameter" .to_string(), ) @@ -351,10 +351,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let adt_desc = adt.descr(); let desc = format!( - "the type {ty}, which makes the generic argument {generic_arg} invariant" + "the type `{ty}`, which makes the generic argument `{generic_arg}` invariant" ); let note = format!( - "the {adt_desc} {base_ty} is invariant over the parameter {base_generic_arg}" + "the {adt_desc} `{base_ty}` is invariant over the parameter `{base_generic_arg}`" + ); + (desc, note) + } + ty::FnDef(def_id, _) => { + let name = self.infcx.tcx.item_name(*def_id); + let identity_substs = + InternalSubsts::identity_for_item(self.infcx.tcx, *def_id); + let desc = format!("a function pointer to `{name}`"); + let note = format!( + "the function `{name}` is invariant over the parameter `{}`", + identity_substs[param_index as usize] ); (desc, note) } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index c9395492c9e..723adb8da1b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -109,7 +109,7 @@ impl RegionName { *span, format!("lifetime `{}` represents this closure's body", self), ); - diag.note(¬e); + diag.note(note); } RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( span, diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 31213412d45..138e1fa0176 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -7,11 +7,11 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{token, BlockCheckMode, UnsafeSource}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, Applicability, PResult}; +use rustc_errors::{pluralize, Applicability, MultiSpan, PResult}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{InnerSpan, MultiSpan, Span}; +use rustc_span::{InnerSpan, Span}; use smallvec::SmallVec; use std::borrow::Cow; @@ -446,7 +446,9 @@ impl<'a, 'b> Context<'a, 'b> { .iter() .filter(|fmt| fmt.precision_span.is_some()) .count(); - e.span_label(span, &format!( + e.span_label( + span, + &format!( "this precision flag adds an extra required argument at position {}, \ which is why there {} expected", pos, @@ -455,7 +457,8 @@ impl<'a, 'b> Context<'a, 'b> { } else { format!("are {} arguments", count) }, - )); + ), + ); if let Some(arg) = self.args.get(pos) { e.span_label( arg.span, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 9417874ffb4..92c4ab7eb86 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1707,23 +1707,33 @@ impl SharedEmitter { impl Emitter for SharedEmitter { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { + let fluent_args = self.to_fluent_args(diag.args()); drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: diag.message(), + msg: self.translate_messages(&diag.message, &fluent_args).to_string(), code: diag.code.clone(), lvl: diag.level(), }))); for child in &diag.children { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: child.message(), + msg: self.translate_messages(&child.message, &fluent_args).to_string(), code: None, lvl: child.level, }))); } drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); } + fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } + + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + panic!("shared emitter attempted to translate a diagnostic"); + } } impl SharedEmitterMain { @@ -1754,9 +1764,9 @@ impl SharedEmitterMain { let msg = msg.strip_prefix("error: ").unwrap_or(&msg); let mut err = match level { - Level::Error { lint: false } => sess.struct_err(&msg).forget_guarantee(), - Level::Warning => sess.struct_warn(&msg), - Level::Note => sess.struct_note_without_error(&msg), + Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(), + Level::Warning => sess.struct_warn(msg), + Level::Note => sess.struct_note_without_error(msg), _ => bug!("Invalid inline asm diagnostic level"), }; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 69f96d07f90..febdd0ed746 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1172,9 +1172,13 @@ static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + /// When `install_ice_hook` is called, this function will be called as the panic /// hook. pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + None, + fallback_bundle, false, false, None, @@ -1209,7 +1213,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { } for note in &xs { - handler.note_without_error(note); + handler.note_without_error(note.as_ref()); } // If backtraces are enabled, also print the query stack diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml new file mode 100644 index 00000000000..fc84c7c8665 --- /dev/null +++ b/compiler/rustc_error_messages/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "rustc_error_messages" +version = "0.0.0" +edition = "2021" + +[lib] +doctest = false + +[dependencies] +fluent-bundle = "0.15.2" +fluent-syntax = "0.11" +intl-memoizer = "0.5.1" +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_span = { path = "../rustc_span" } +rustc_macros = { path = "../rustc_macros" } +tracing = "0.1" +unic-langid = { version = "0.9.0", features = ["macros"] } diff --git a/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl b/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl new file mode 100644 index 00000000000..336e7a66857 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl @@ -0,0 +1,87 @@ +parser-struct-literal-body-without-path = + struct literal body without path + .suggestion = you might have forgotten to add the struct literal inside the block + +typeck-field-multiply-specified-in-initializer = + field `{$ident}` specified more than once + .label = used more than once + .previous-use-label = first use of `{$ident}` + +typeck-unrecognized-atomic-operation = + unrecognized atomic operation function: `{$op}` + .label = unrecognized atomic operation + +typeck-wrong-number-of-generic-arguments-to-intrinsic = + intrinsic has wrong number of {$descr} parameters: found {$found}, expected {$expected} + .label = expected {$expected} {$descr} {$expected -> + [one] parameter + *[other] parameters + } + +typeck-unrecognized-intrinsic-function = + unrecognized intrinsic function: `{$name}` + .label = unrecognized intrinsic + +typeck-lifetimes-or-bounds-mismatch-on-trait = + lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration + .label = lifetimes do not match {$item_kind} in trait + .generics-label = lifetimes in impl do not match this {$item_kind} in trait + +typeck-drop-impl-on-wrong-item = + the `Drop` trait may only be implemented for structs, enums, and unions + .label = must be a struct, enum, or union + +typeck-field-already-declared = + field `{$field_name}` is already declared + .label = field already declared + .previous-decl-label = `{$field_name}` first declared here + +typeck-copy-impl-on-type-with-dtor = + the trait `Copy` may not be implemented for this type; the type has a destructor + .label = `Copy` not allowed on types with destructors + +typeck-multiple-relaxed-default-bounds = + type parameter has more than one relaxed default bound, only one is supported + +typeck-copy-impl-on-non-adt = + the trait `Copy` may not be implemented for this type + .label = type is not a structure or enumeration + +typeck-trait-object-declared-with-no-traits = + at least one trait is required for an object type + +typeck-ambiguous-lifetime-bound = + ambiguous lifetime bound, explicit lifetime bound required + +typeck-assoc-type-binding-not-allowed = + associated type bindings are not allowed here + .label = associated type not allowed here + +typeck-functional-record-update-on-non-struct = + functional record update syntax requires a struct + +typeck-typeof-reserved-keyword-used = + `typeof` is a reserved keyword but unimplemented + .label = reserved keyword + +typeck-return-stmt-outside-of-fn-body = + return statement outside of function body + .encl-body-label = the return is part of this body... + .encl-fn-label = ...not the enclosing function body + +typeck-yield-expr-outside-of-generator = + yield expression outside of generator literal + +typeck-struct-expr-non-exhaustive = + cannot create non-exhaustive {$what} using struct expression + +typeck-method-call-on-unknown-type = + the type of this value must be known to call a method on a raw pointer on it + +typeck-value-of-associated-struct-already-specified = + the value of the associated type `{$item_name}` (from trait `{$def_path}`) is already specified + .label = re-bound here + .previous-bound-label = `{$item_name}` bound here first + +typeck-address-of-temporary-taken = cannot take address of a temporary + .label = temporary value diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs new file mode 100644 index 00000000000..88f3b547605 --- /dev/null +++ b/compiler/rustc_error_messages/src/lib.rs @@ -0,0 +1,390 @@ +#![feature(let_chains)] +#![feature(path_try_exists)] + +use fluent_bundle::FluentResource; +use fluent_syntax::parser::ParserError; +use rustc_data_structures::sync::Lrc; +use rustc_macros::{Decodable, Encodable}; +use rustc_span::Span; +use std::borrow::Cow; +use std::error::Error; +use std::fmt; +use std::fs; +use std::io; +use std::path::Path; +use tracing::{instrument, trace}; + +#[cfg(parallel_compiler)] +use intl_memoizer::concurrent::IntlLangMemoizer; +#[cfg(not(parallel_compiler))] +use intl_memoizer::IntlLangMemoizer; + +pub use fluent_bundle::{FluentArgs, FluentError, FluentValue}; +pub use unic_langid::{langid, LanguageIdentifier}; + +static FALLBACK_FLUENT_RESOURCE: &'static str = include_str!("../locales/en-US/diagnostics.ftl"); + +pub type FluentBundle = fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>; + +#[cfg(parallel_compiler)] +fn new_bundle(locales: Vec<LanguageIdentifier>) -> FluentBundle { + FluentBundle::new_concurrent(locales) +} + +#[cfg(not(parallel_compiler))] +fn new_bundle(locales: Vec<LanguageIdentifier>) -> FluentBundle { + FluentBundle::new(locales) +} + +#[derive(Debug)] +pub enum TranslationBundleError { + /// Failed to read from `.ftl` file. + ReadFtl(io::Error), + /// Failed to parse contents of `.ftl` file. + ParseFtl(ParserError), + /// Failed to add `FluentResource` to `FluentBundle`. + AddResource(FluentError), + /// `$sysroot/share/locale/$locale` does not exist. + MissingLocale(io::Error), + /// Cannot read directory entries of `$sysroot/share/locale/$locale`. + ReadLocalesDir(io::Error), + /// Cannot read directory entry of `$sysroot/share/locale/$locale`. + ReadLocalesDirEntry(io::Error), + /// `$sysroot/share/locale/$locale` is not a directory. + LocaleIsNotDir, +} + +impl fmt::Display for TranslationBundleError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TranslationBundleError::ReadFtl(e) => write!(f, "could not read ftl file: {}", e), + TranslationBundleError::ParseFtl(e) => { + write!(f, "could not parse ftl file: {}", e) + } + TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {}", e), + TranslationBundleError::MissingLocale(e) => { + write!(f, "missing locale directory: {}", e) + } + TranslationBundleError::ReadLocalesDir(e) => { + write!(f, "could not read locales dir: {}", e) + } + TranslationBundleError::ReadLocalesDirEntry(e) => { + write!(f, "could not read locales dir entry: {}", e) + } + TranslationBundleError::LocaleIsNotDir => { + write!(f, "`$sysroot/share/locales/$locale` is not a directory") + } + } + } +} + +impl Error for TranslationBundleError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + TranslationBundleError::ReadFtl(e) => Some(e), + TranslationBundleError::ParseFtl(e) => Some(e), + TranslationBundleError::AddResource(e) => Some(e), + TranslationBundleError::MissingLocale(e) => Some(e), + TranslationBundleError::ReadLocalesDir(e) => Some(e), + TranslationBundleError::ReadLocalesDirEntry(e) => Some(e), + TranslationBundleError::LocaleIsNotDir => None, + } + } +} + +impl From<(FluentResource, Vec<ParserError>)> for TranslationBundleError { + fn from((_, mut errs): (FluentResource, Vec<ParserError>)) -> Self { + TranslationBundleError::ParseFtl(errs.pop().expect("failed ftl parse with no errors")) + } +} + +impl From<Vec<FluentError>> for TranslationBundleError { + fn from(mut errs: Vec<FluentError>) -> Self { + TranslationBundleError::AddResource( + errs.pop().expect("failed adding resource to bundle with no errors"), + ) + } +} + +/// Returns Fluent bundle with the user's locale resources from +/// `$sysroot/share/locale/$requested_locale/*.ftl`. +/// +/// If `-Z additional-ftl-path` was provided, load that resource and add it to the bundle +/// (overriding any conflicting messages). +#[instrument(level = "trace")] +pub fn fluent_bundle( + sysroot: &Path, + requested_locale: Option<LanguageIdentifier>, + additional_ftl_path: Option<&Path>, + with_directionality_markers: bool, +) -> Result<Option<Lrc<FluentBundle>>, TranslationBundleError> { + if requested_locale.is_none() && additional_ftl_path.is_none() { + return Ok(None); + } + + let fallback_locale = langid!("en-US"); + let requested_fallback_locale = requested_locale.as_ref() == Some(&fallback_locale); + + // If there is only `-Z additional-ftl-path`, assume locale is "en-US", otherwise use user + // provided locale. + let locale = requested_locale.clone().unwrap_or(fallback_locale); + trace!(?locale); + let mut bundle = new_bundle(vec![locale]); + + // Fluent diagnostics can insert directionality isolation markers around interpolated variables + // indicating that there may be a shift from right-to-left to left-to-right text (or + // vice-versa). These are disabled because they are sometimes visible in the error output, but + // may be worth investigating in future (for example: if type names are left-to-right and the + // surrounding diagnostic messages are right-to-left, then these might be helpful). + bundle.set_use_isolating(with_directionality_markers); + + // If the user requests the default locale then don't try to load anything. + if !requested_fallback_locale && let Some(requested_locale) = requested_locale { + let mut sysroot = sysroot.to_path_buf(); + sysroot.push("share"); + sysroot.push("locale"); + sysroot.push(requested_locale.to_string()); + trace!(?sysroot); + + let _ = sysroot.try_exists().map_err(TranslationBundleError::MissingLocale)?; + + if !sysroot.is_dir() { + return Err(TranslationBundleError::LocaleIsNotDir); + } + + for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? { + let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?; + let path = entry.path(); + trace!(?path); + if path.extension().and_then(|s| s.to_str()) != Some("ftl") { + trace!("skipping"); + continue; + } + + let resource_str = + fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?; + let resource = + FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; + trace!(?resource); + bundle.add_resource(resource).map_err(TranslationBundleError::from)?; + } + } + + if let Some(additional_ftl_path) = additional_ftl_path { + let resource_str = + fs::read_to_string(additional_ftl_path).map_err(TranslationBundleError::ReadFtl)?; + let resource = + FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; + trace!(?resource); + bundle.add_resource_overriding(resource); + } + + let bundle = Lrc::new(bundle); + Ok(Some(bundle)) +} + +/// Return the default `FluentBundle` with standard "en-US" diagnostic messages. +#[instrument(level = "trace")] +pub fn fallback_fluent_bundle( + with_directionality_markers: bool, +) -> Result<Lrc<FluentBundle>, TranslationBundleError> { + let fallback_resource = FluentResource::try_new(FALLBACK_FLUENT_RESOURCE.to_string()) + .map_err(TranslationBundleError::from)?; + trace!(?fallback_resource); + let mut fallback_bundle = new_bundle(vec![langid!("en-US")]); + // See comment in `fluent_bundle`. + fallback_bundle.set_use_isolating(with_directionality_markers); + fallback_bundle.add_resource(fallback_resource).map_err(TranslationBundleError::from)?; + let fallback_bundle = Lrc::new(fallback_bundle); + Ok(fallback_bundle) +} + +/// Identifier for the Fluent message/attribute corresponding to a diagnostic message. +type FluentId = Cow<'static, str>; + +/// Abstraction over a message in a diagnostic to support both translatable and non-translatable +/// diagnostic messages. +/// +/// Intended to be removed once diagnostics are entirely translatable. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum DiagnosticMessage { + /// Non-translatable diagnostic message. + // FIXME(davidtwco): can a `Cow<'static, str>` be used here? + Str(String), + /// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic + /// message. + /// + /// <https://projectfluent.org/fluent/guide/hello.html> + /// <https://projectfluent.org/fluent/guide/attributes.html> + FluentIdentifier(FluentId, Option<FluentId>), +} + +impl DiagnosticMessage { + /// Returns the `String` contained within the `DiagnosticMessage::Str` variant, assuming that + /// this diagnostic message is of the legacy, non-translatable variety. Panics if this + /// assumption does not hold. + /// + /// Don't use this - it exists to support some places that do comparison with diagnostic + /// strings. + pub fn expect_str(&self) -> &str { + match self { + DiagnosticMessage::Str(s) => s, + _ => panic!("expected non-translatable diagnostic message"), + } + } + + /// Create a `DiagnosticMessage` for the provided Fluent identifier. + pub fn fluent(id: impl Into<FluentId>) -> Self { + DiagnosticMessage::FluentIdentifier(id.into(), None) + } + + /// Create a `DiagnosticMessage` for the provided Fluent identifier and attribute. + pub fn fluent_attr(id: impl Into<FluentId>, attr: impl Into<FluentId>) -> Self { + DiagnosticMessage::FluentIdentifier(id.into(), Some(attr.into())) + } +} + +/// `From` impl that enables existing diagnostic calls to functions which now take +/// `impl Into<DiagnosticMessage>` to continue to work as before. +impl<S: Into<String>> From<S> for DiagnosticMessage { + fn from(s: S) -> Self { + DiagnosticMessage::Str(s.into()) + } +} + +/// A span together with some additional data. +#[derive(Clone, Debug)] +pub struct SpanLabel { + /// The span we are going to include in the final snippet. + pub span: Span, + + /// Is this a primary span? This is the "locus" of the message, + /// and is indicated with a `^^^^` underline, versus `----`. + pub is_primary: bool, + + /// What label should we attach to this span (if any)? + pub label: Option<DiagnosticMessage>, +} + +/// A collection of `Span`s. +/// +/// Spans have two orthogonal attributes: +/// +/// - They can be *primary spans*. In this case they are the locus of +/// the error, and would be rendered with `^^^`. +/// - They can have a *label*. In this case, the label is written next +/// to the mark in the snippet when we render. +#[derive(Clone, Debug, Hash, PartialEq, Eq, Encodable, Decodable)] +pub struct MultiSpan { + primary_spans: Vec<Span>, + span_labels: Vec<(Span, DiagnosticMessage)>, +} + +impl MultiSpan { + #[inline] + pub fn new() -> MultiSpan { + MultiSpan { primary_spans: vec![], span_labels: vec![] } + } + + pub fn from_span(primary_span: Span) -> MultiSpan { + MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] } + } + + pub fn from_spans(mut vec: Vec<Span>) -> MultiSpan { + vec.sort(); + MultiSpan { primary_spans: vec, span_labels: vec![] } + } + + pub fn push_span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) { + self.span_labels.push((span, label.into())); + } + + /// Selects the first primary span (if any). + pub fn primary_span(&self) -> Option<Span> { + self.primary_spans.first().cloned() + } + + /// Returns all primary spans. + pub fn primary_spans(&self) -> &[Span] { + &self.primary_spans + } + + /// Returns `true` if any of the primary spans are displayable. + pub fn has_primary_spans(&self) -> bool { + self.primary_spans.iter().any(|sp| !sp.is_dummy()) + } + + /// Returns `true` if this contains only a dummy primary span with any hygienic context. + pub fn is_dummy(&self) -> bool { + let mut is_dummy = true; + for span in &self.primary_spans { + if !span.is_dummy() { + is_dummy = false; + } + } + is_dummy + } + + /// Replaces all occurrences of one Span with another. Used to move `Span`s in areas that don't + /// display well (like std macros). Returns whether replacements occurred. + pub fn replace(&mut self, before: Span, after: Span) -> bool { + let mut replacements_occurred = false; + for primary_span in &mut self.primary_spans { + if *primary_span == before { + *primary_span = after; + replacements_occurred = true; + } + } + for span_label in &mut self.span_labels { + if span_label.0 == before { + span_label.0 = after; + replacements_occurred = true; + } + } + replacements_occurred + } + + /// Returns the strings to highlight. We always ensure that there + /// is an entry for each of the primary spans -- for each primary + /// span `P`, if there is at least one label with span `P`, we return + /// those labels (marked as primary). But otherwise we return + /// `SpanLabel` instances with empty labels. + pub fn span_labels(&self) -> Vec<SpanLabel> { + let is_primary = |span| self.primary_spans.contains(&span); + + let mut span_labels = self + .span_labels + .iter() + .map(|&(span, ref label)| SpanLabel { + span, + is_primary: is_primary(span), + label: Some(label.clone()), + }) + .collect::<Vec<_>>(); + + for &span in &self.primary_spans { + if !span_labels.iter().any(|sl| sl.span == span) { + span_labels.push(SpanLabel { span, is_primary: true, label: None }); + } + } + + span_labels + } + + /// Returns `true` if any of the span labels is displayable. + pub fn has_span_labels(&self) -> bool { + self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) + } +} + +impl From<Span> for MultiSpan { + fn from(span: Span) -> MultiSpan { + MultiSpan::from_span(span) + } +} + +impl From<Vec<Span>> for MultiSpan { + fn from(spans: Vec<Span>) -> MultiSpan { + MultiSpan::from_spans(spans) + } +} diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 4846dc43605..5f919982890 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -8,6 +8,7 @@ doctest = false [dependencies] tracing = "0.1" +rustc_error_messages = { path = "../rustc_error_messages" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 5f59eba23f8..003fd1eea3a 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -7,16 +7,23 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; -use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Emitter, Level, SubDiagnostic}; +use crate::{ + CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, Level, + MultiSpan, Style, SubDiagnostic, +}; use annotate_snippets::display_list::{DisplayList, FormatOptions}; use annotate_snippets::snippet::*; use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; use rustc_span::source_map::SourceMap; -use rustc_span::{MultiSpan, SourceFile}; +use rustc_span::SourceFile; /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitterWriter { source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, + /// If true, hides the longer explanation text short_message: bool, /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs. @@ -28,8 +35,10 @@ pub struct AnnotateSnippetEmitterWriter { impl Emitter for AnnotateSnippetEmitterWriter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, diag: &Diagnostic) { + let fluent_args = self.to_fluent_args(diag.args()); + let mut children = diag.children.clone(); - let (mut primary_span, suggestions) = self.primary_span_formatted(&diag); + let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); self.fix_multispans_in_extern_macros_and_render_macro_backtrace( &self.source_map, @@ -41,7 +50,8 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.emit_messages_default( &diag.level, - diag.message(), + &diag.message, + &fluent_args, &diag.code, &primary_span, &children, @@ -53,6 +63,14 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.source_map.as_ref() } + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + self.fluent_bundle.as_ref() + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + &self.fallback_bundle + } + fn should_show_explain(&self) -> bool { !self.short_message } @@ -82,10 +100,19 @@ fn annotation_type_for_level(level: Level) -> AnnotationType { impl AnnotateSnippetEmitterWriter { pub fn new( source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, macro_backtrace: bool, ) -> Self { - Self { source_map, short_message, ui_testing: false, macro_backtrace } + Self { + source_map, + fluent_bundle, + fallback_bundle, + short_message, + ui_testing: false, + macro_backtrace, + } } /// Allows to modify `Self` to enable or disable the `ui_testing` flag. @@ -99,12 +126,14 @@ impl AnnotateSnippetEmitterWriter { fn emit_messages_default( &mut self, level: &Level, - message: String, + messages: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, code: &Option<DiagnosticId>, msp: &MultiSpan, _children: &[SubDiagnostic], _suggestions: &[CodeSuggestion], ) { + let message = self.translate_messages(messages, args); if let Some(source_map) = &self.source_map { // Make sure our primary file comes first let primary_lo = if let Some(ref primary_span) = msp.primary_span().as_ref() { @@ -120,8 +149,7 @@ impl AnnotateSnippetEmitterWriter { // should be done if it happens return; }; - let mut annotated_files = - FileWithAnnotatedLines::collect_annotations(msp, &self.source_map); + let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp); if let Ok(pos) = annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 32c52a6a8a6..ecb3cdd627c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,15 +1,16 @@ use crate::snippet::Style; -use crate::CodeSuggestion; -use crate::Level; -use crate::Substitution; -use crate::SubstitutionPart; -use crate::SuggestionStyle; -use crate::ToolMetadata; +use crate::{ + CodeSuggestion, DiagnosticMessage, Level, MultiSpan, Substitution, SubstitutionPart, + SuggestionStyle, ToolMetadata, +}; use rustc_data_structures::stable_map::FxHashMap; +use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_serialize::json::Json; use rustc_span::edition::LATEST_STABLE_EDITION; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Span, DUMMY_SP}; +use std::borrow::Cow; use std::fmt; use std::hash::{Hash, Hasher}; @@ -18,6 +19,66 @@ use std::hash::{Hash, Hasher}; #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub struct SuggestionsDisabled; +/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of +/// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of +/// diagnostic emission. +pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>); + +/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted +/// to a `FluentValue` by the emitter to be used in diagnostic translation. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum DiagnosticArgValue<'source> { + Str(Cow<'source, str>), + Number(usize), +} + +/// Converts a value of a type into a `DiagnosticArg` (typically a field of a `SessionDiagnostic` +/// struct). Implemented as a custom trait rather than `From` so that it is implemented on the type +/// being converted rather than on `DiagnosticArgValue`, which enables types from other `rustc_*` +/// crates to implement this. +pub trait IntoDiagnosticArg { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; +} + +impl IntoDiagnosticArg for String { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self)) + } +} + +impl IntoDiagnosticArg for Symbol { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_ident_string().into_diagnostic_arg() + } +} + +impl IntoDiagnosticArg for Ident { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + +impl<'a> IntoDiagnosticArg for &'a str { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + +impl IntoDiagnosticArg for usize { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Number(self) + } +} + +impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> { + fn into(self) -> FluentValue<'source> { + match self { + DiagnosticArgValue::Str(s) => From::from(s), + DiagnosticArgValue::Number(n) => From::from(n), + } + } +} + #[must_use] #[derive(Clone, Debug, Encodable, Decodable)] pub struct Diagnostic { @@ -25,11 +86,12 @@ pub struct Diagnostic { // outside of what methods in this crate themselves allow. crate level: Level, - pub message: Vec<(String, Style)>, + pub message: Vec<(DiagnosticMessage, Style)>, pub code: Option<DiagnosticId>, pub span: MultiSpan, pub children: Vec<SubDiagnostic>, pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, + args: Vec<DiagnosticArg<'static>>, /// This is not used for highlighting or rendering any error message. Rather, it can be used /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of @@ -52,7 +114,7 @@ pub enum DiagnosticId { #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub struct SubDiagnostic { pub level: Level, - pub message: Vec<(String, Style)>, + pub message: Vec<(DiagnosticMessage, Style)>, pub span: MultiSpan, pub render_span: Option<MultiSpan>, } @@ -105,18 +167,23 @@ impl StringPart { } impl Diagnostic { - pub fn new(level: Level, message: &str) -> Self { + pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self { Diagnostic::new_with_code(level, None, message) } - pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self { + pub fn new_with_code<M: Into<DiagnosticMessage>>( + level: Level, + code: Option<DiagnosticId>, + message: M, + ) -> Self { Diagnostic { level, - message: vec![(message.to_owned(), Style::NoStyle)], + message: vec![(message.into(), Style::NoStyle)], code, span: MultiSpan::new(), children: vec![], suggestions: Ok(vec![]), + args: vec![], sort_span: DUMMY_SP, is_lint: false, } @@ -210,7 +277,7 @@ impl Diagnostic { /// /// This span is *not* considered a ["primary span"][`MultiSpan`]; only /// the `Span` supplied when creating the diagnostic is primary. - pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self { + pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self { self.span.push_span_label(span, label.into()); self } @@ -234,7 +301,7 @@ impl Diagnostic { self.set_span(after); for span_label in before.span_labels() { if let Some(label) = span_label.label { - self.span_label(after, label); + self.span.push_span_label(after, label); } } self @@ -328,52 +395,67 @@ impl Diagnostic { } /// Add a note attached to this diagnostic. - pub fn note(&mut self, msg: &str) -> &mut Self { + pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::Note, msg, MultiSpan::new(), None); self } - pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self { + pub fn highlighted_note<M: Into<DiagnosticMessage>>( + &mut self, + msg: Vec<(M, Style)>, + ) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None); self } /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - pub fn note_once(&mut self, msg: &str) -> &mut Self { + pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::OnceNote, msg, MultiSpan::new(), None); self } /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_note<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::Note, msg, sp.into(), None); self } /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - pub fn span_note_once<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_note_once<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::OnceNote, msg, sp.into(), None); self } /// Add a warning attached to this diagnostic. - pub fn warn(&mut self, msg: &str) -> &mut Self { + pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new(), None); self } /// Prints the span with a warning above it. /// This is like [`Diagnostic::warn()`], but it gets its own span. - pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_warn<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::Warning, msg, sp.into(), None); self } /// Add a help message attached to this diagnostic. - pub fn help(&mut self, msg: &str) -> &mut Self { + pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new(), None); self } @@ -386,7 +468,11 @@ impl Diagnostic { /// Prints the span with some help above it. /// This is like [`Diagnostic::help()`], but it gets its own span. - pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_help<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::Help, msg, sp.into(), None); self } @@ -422,7 +508,7 @@ impl Diagnostic { /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -438,7 +524,7 @@ impl Diagnostic { /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion_verbose( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -452,7 +538,7 @@ impl Diagnostic { /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn multipart_suggestion_with_style( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, style: SuggestionStyle, @@ -465,7 +551,7 @@ impl Diagnostic { .map(|(span, snippet)| SubstitutionPart { snippet, span }) .collect(), }], - msg: msg.to_owned(), + msg: msg.into(), style, applicability, tool_metadata: Default::default(), @@ -481,7 +567,7 @@ impl Diagnostic { /// improve understandability. pub fn tool_only_multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -493,7 +579,7 @@ impl Diagnostic { .map(|(span, snippet)| SubstitutionPart { snippet, span }) .collect(), }], - msg: msg.to_owned(), + msg: msg.into(), style: SuggestionStyle::CompletelyHidden, applicability, tool_metadata: Default::default(), @@ -521,7 +607,7 @@ impl Diagnostic { pub fn span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -539,7 +625,7 @@ impl Diagnostic { pub fn span_suggestion_with_style( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, style: SuggestionStyle, @@ -548,7 +634,7 @@ impl Diagnostic { substitutions: vec![Substitution { parts: vec![SubstitutionPart { snippet: suggestion, span: sp }], }], - msg: msg.to_owned(), + msg: msg.into(), style, applicability, tool_metadata: Default::default(), @@ -560,7 +646,7 @@ impl Diagnostic { pub fn span_suggestion_verbose( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -579,7 +665,7 @@ impl Diagnostic { pub fn span_suggestions( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = String>, applicability: Applicability, ) -> &mut Self { @@ -591,7 +677,7 @@ impl Diagnostic { .collect(); self.push_suggestion(CodeSuggestion { substitutions, - msg: msg.to_owned(), + msg: msg.into(), style: SuggestionStyle::ShowCode, applicability, tool_metadata: Default::default(), @@ -603,7 +689,7 @@ impl Diagnostic { /// See also [`Diagnostic::span_suggestion()`]. pub fn multipart_suggestions( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = Vec<(Span, String)>>, applicability: Applicability, ) -> &mut Self { @@ -616,7 +702,7 @@ impl Diagnostic { .collect(), }) .collect(), - msg: msg.to_owned(), + msg: msg.into(), style: SuggestionStyle::ShowCode, applicability, tool_metadata: Default::default(), @@ -630,7 +716,7 @@ impl Diagnostic { pub fn span_suggestion_short( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -653,7 +739,7 @@ impl Diagnostic { pub fn span_suggestion_hidden( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -674,7 +760,7 @@ impl Diagnostic { pub fn tool_only_span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -692,13 +778,13 @@ impl Diagnostic { /// the suggestion in a tool-specific way, as it may not even directly involve Rust code. pub fn tool_only_suggestion_with_metadata( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, applicability: Applicability, tool_metadata: Json, ) { self.push_suggestion(CodeSuggestion { substitutions: vec![], - msg: msg.to_owned(), + msg: msg.into(), style: SuggestionStyle::CompletelyHidden, applicability, tool_metadata: ToolMetadata::new(tool_metadata), @@ -732,16 +818,25 @@ impl Diagnostic { self.code.clone() } - pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self { + pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.message[0] = (msg.into(), Style::NoStyle); self } - pub fn message(&self) -> String { - self.message.iter().map(|i| i.0.as_str()).collect::<String>() + pub fn args(&self) -> &[DiagnosticArg<'static>] { + &self.args + } + + pub fn set_arg( + &mut self, + name: impl Into<Cow<'static, str>>, + arg: DiagnosticArgValue<'static>, + ) -> &mut Self { + self.args.push((name.into(), arg.into())); + self } - pub fn styled_message(&self) -> &Vec<(String, Style)> { + pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> { &self.message } @@ -752,13 +847,13 @@ impl Diagnostic { pub fn sub( &mut self, level: Level, - message: &str, + message: impl Into<DiagnosticMessage>, span: MultiSpan, render_span: Option<MultiSpan>, ) { let sub = SubDiagnostic { level, - message: vec![(message.to_owned(), Style::NoStyle)], + message: vec![(message.into(), Style::NoStyle)], span, render_span, }; @@ -767,13 +862,14 @@ impl Diagnostic { /// Convenience function for internal use, clients should use one of the /// public methods above. - fn sub_with_highlights( + fn sub_with_highlights<M: Into<DiagnosticMessage>>( &mut self, level: Level, - message: Vec<(String, Style)>, + mut message: Vec<(M, Style)>, span: MultiSpan, render_span: Option<MultiSpan>, ) { + let message = message.drain(..).map(|m| (m.0.into(), m.1)).collect(); let sub = SubDiagnostic { level, message, span, render_span }; self.children.push(sub); } @@ -783,7 +879,7 @@ impl Diagnostic { &self, ) -> ( &Level, - &Vec<(String, Style)>, + &Vec<(DiagnosticMessage, Style)>, &Option<DiagnosticId>, &MultiSpan, &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, @@ -814,13 +910,3 @@ impl PartialEq for Diagnostic { self.keys() == other.keys() } } - -impl SubDiagnostic { - pub fn message(&self) -> String { - self.message.iter().map(|i| i.0.as_str()).collect::<String>() - } - - pub fn styled_message(&self) -> &Vec<(String, Style)> { - &self.message - } -} diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 853243ef3f0..74e0f742946 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,8 +1,10 @@ -use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString, ErrorGuaranteed}; -use crate::{Handler, Level, StashKey}; +use crate::diagnostic::DiagnosticArgValue; +use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed}; +use crate::{Handler, Level, MultiSpan, StashKey}; use rustc_lint_defs::Applicability; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; +use std::borrow::Cow; use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -99,7 +101,10 @@ mod sealed_level_is_error { impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new_guaranteeing_error<const L: Level>(handler: &'a Handler, message: &str) -> Self + crate fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>( + handler: &'a Handler, + message: M, + ) -> Self where (): sealed_level_is_error::IsError<L>, { @@ -163,7 +168,11 @@ impl EmissionGuarantee for ErrorGuaranteed { impl<'a> DiagnosticBuilder<'a, ()> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new(handler: &'a Handler, level: Level, message: &str) -> Self { + crate fn new<M: Into<DiagnosticMessage>>( + handler: &'a Handler, + level: Level, + message: M, + ) -> Self { let diagnostic = Diagnostic::new_with_code(level, None, message); Self::new_diagnostic(handler, diagnostic) } @@ -201,7 +210,7 @@ impl EmissionGuarantee for () { impl<'a> DiagnosticBuilder<'a, !> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new_fatal(handler: &'a Handler, message: &str) -> Self { + crate fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message); Self::new_diagnostic_fatal(handler, diagnostic) } @@ -346,7 +355,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { } // Take the `Diagnostic` by replacing it with a dummy. - let dummy = Diagnostic::new(Level::Allow, ""); + let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::Str("".to_string())); let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy); // Disable the ICE on `Drop`. @@ -399,7 +408,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// the diagnostic was constructed. However, the label span is *not* considered a /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is /// primary. - pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self); + pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self); forward!( /// Labels all the given spans with the provided label. @@ -434,25 +443,25 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { found: DiagnosticStyledString, ) -> &mut Self); - forward!(pub fn note(&mut self, msg: &str) -> &mut Self); - forward!(pub fn note_once(&mut self, msg: &str) -> &mut Self); + forward!(pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); + forward!(pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); forward!(pub fn span_note( &mut self, sp: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> &mut Self); forward!(pub fn span_note_once( &mut self, sp: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> &mut Self); - forward!(pub fn warn(&mut self, msg: &str) -> &mut Self); + forward!(pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); forward!(pub fn span_warn(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> &mut Self); - forward!(pub fn help(&mut self, msg: &str) -> &mut Self); + forward!(pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); forward!(pub fn span_help( &mut self, sp: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> &mut Self); forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self); forward!(pub fn set_is_lint(&mut self,) -> &mut Self); @@ -461,67 +470,67 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { forward!(pub fn multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self); forward!(pub fn multipart_suggestion_verbose( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self); forward!(pub fn tool_only_multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestions( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = String>, applicability: Applicability, ) -> &mut Self); forward!(pub fn multipart_suggestions( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = Vec<(Span, String)>>, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_short( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_verbose( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_hidden( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn tool_only_span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); @@ -529,6 +538,11 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { forward!(pub fn set_primary_message(&mut self, msg: impl Into<String>) -> &mut Self); forward!(pub fn set_span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self); forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); + forward!(pub fn set_arg( + &mut self, + name: impl Into<Cow<'static, str>>, + arg: DiagnosticArgValue<'static>, + ) -> &mut Self); } impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> { @@ -547,7 +561,9 @@ impl Drop for DiagnosticBuilderInner<'_> { if !panicking() { handler.emit_diagnostic(&mut Diagnostic::new( Level::Bug, - "the following error was constructed but not emitted", + DiagnosticMessage::Str( + "the following error was constructed but not emitted".to_string(), + ), )); handler.emit_diagnostic(&mut self.diagnostic); panic!(); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 93b7201023a..6a763d4d140 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -10,19 +10,20 @@ use Destination::*; use rustc_span::source_map::SourceMap; -use rustc_span::{MultiSpan, SourceFile, Span}; +use rustc_span::{SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; use crate::{ - CodeSuggestion, Diagnostic, DiagnosticId, Handler, Level, SubDiagnostic, SubstitutionHighlight, - SuggestionStyle, + CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle, + Handler, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle, }; use rustc_lint_defs::pluralize; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; use rustc_span::hygiene::{ExpnKind, MacroKind}; use std::borrow::Cow; use std::cmp::{max, min, Reverse}; @@ -58,13 +59,25 @@ impl HumanReadableErrorType { self, dst: Box<dyn Write + Send>, source_map: Option<Lrc<SourceMap>>, + bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, teach: bool, terminal_width: Option<usize>, macro_backtrace: bool, ) -> EmitterWriter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); - EmitterWriter::new(dst, source_map, short, teach, color, terminal_width, macro_backtrace) + EmitterWriter::new( + dst, + source_map, + bundle, + fallback_bundle, + short, + teach, + color, + terminal_width, + macro_backtrace, + ) } } @@ -212,6 +225,74 @@ pub trait Emitter { fn source_map(&self) -> Option<&Lrc<SourceMap>>; + /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no + /// language was requested by the user then this will be `None` and `fallback_fluent_bundle` + /// should be used. + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>; + + /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler. + /// Used when the user has not requested a specific language or when a localized diagnostic is + /// unavailable for the requested locale. + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle>; + + /// Convert diagnostic arguments (a rustc internal type that exists to implement + /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. + /// + /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then + /// passed around as a reference thereafter. + fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { + FromIterator::from_iter(args.to_vec().drain(..)) + } + + /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. + fn translate_messages( + &self, + messages: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, + ) -> Cow<'_, str> { + Cow::Owned( + messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(), + ) + } + + /// Convert a `DiagnosticMessage` to a string, performing translation if necessary. + fn translate_message<'a>( + &'a self, + message: &'a DiagnosticMessage, + args: &'a FluentArgs<'_>, + ) -> Cow<'_, str> { + trace!(?message, ?args); + let (identifier, attr) = match message { + DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg), + DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + }; + + let bundle = match self.fluent_bundle() { + Some(bundle) if bundle.has_message(&identifier) => bundle, + _ => self.fallback_fluent_bundle(), + }; + + let message = bundle.get_message(&identifier).expect("missing diagnostic in fluent bundle"); + let value = match attr { + Some(attr) => { + message.get_attribute(attr).expect("missing attribute in fluent message").value() + } + None => message.value().expect("missing value in fluent message"), + }; + + let mut err = vec![]; + let translated = bundle.format_pattern(value, Some(&args), &mut err); + trace!(?translated, ?err); + debug_assert!( + err.is_empty(), + "identifier: {:?}, args: {:?}, errors: {:?}", + identifier, + args, + err + ); + translated + } + /// Formats the substitutions of the primary_span /// /// There are a lot of conditions to this method, but in short: @@ -225,10 +306,12 @@ pub trait Emitter { fn primary_span_formatted<'a>( &mut self, diag: &'a Diagnostic, + fluent_args: &FluentArgs<'_>, ) -> (MultiSpan, &'a [CodeSuggestion]) { let mut primary_span = diag.span.clone(); let suggestions = diag.suggestions.as_ref().map_or(&[][..], |suggestions| &suggestions[..]); if let Some((sugg, rest)) = suggestions.split_first() { + let msg = self.translate_message(&sugg.msg, fluent_args); if rest.is_empty() && // ^ if there is only one suggestion // don't display multi-suggestions as labels @@ -236,7 +319,7 @@ pub trait Emitter { // don't display multipart suggestions as labels sugg.substitutions[0].parts.len() == 1 && // don't display long messages as labels - sugg.msg.split_whitespace().count() < 10 && + msg.split_whitespace().count() < 10 && // don't display multiline suggestions as labels !sugg.substitutions[0].parts[0].snippet.contains('\n') && ![ @@ -252,12 +335,12 @@ pub trait Emitter { let msg = if substitution.is_empty() || sugg.style.hide_inline() { // This substitution is only removal OR we explicitly don't want to show the // code inline (`hide_inline`). Therefore, we don't show the substitution. - format!("help: {}", sugg.msg) + format!("help: {}", &msg) } else { // Show the default suggestion text with the substitution format!( "help: {}{}: `{}`", - sugg.msg, + &msg, if self .source_map() .map(|sm| is_case_difference( @@ -333,7 +416,7 @@ pub trait Emitter { children.push(SubDiagnostic { level: Level::Note, - message: vec![(msg, Style::NoStyle)], + message: vec![(DiagnosticMessage::Str(msg), Style::NoStyle)], span: MultiSpan::new(), render_span: None, }); @@ -492,9 +575,19 @@ impl Emitter for EmitterWriter { self.sm.as_ref() } + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + self.fluent_bundle.as_ref() + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + &self.fallback_bundle + } + fn emit_diagnostic(&mut self, diag: &Diagnostic) { + let fluent_args = self.to_fluent_args(diag.args()); + let mut children = diag.children.clone(); - let (mut primary_span, suggestions) = self.primary_span_formatted(&diag); + let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); debug!("emit_diagnostic: suggestions={:?}", suggestions); self.fix_multispans_in_extern_macros_and_render_macro_backtrace( @@ -507,7 +600,8 @@ impl Emitter for EmitterWriter { self.emit_messages_default( &diag.level, - &diag.styled_message(), + &diag.message, + &fluent_args, &diag.code, &primary_span, &children, @@ -536,6 +630,15 @@ impl Emitter for SilentEmitter { fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } + + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + panic!("silent emitter attempted to translate message") + } + fn emit_diagnostic(&mut self, d: &Diagnostic) { if d.level == Level::Fatal { let mut d = d.clone(); @@ -591,6 +694,8 @@ impl ColorConfig { pub struct EmitterWriter { dst: Destination, sm: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, ui_testing: bool, @@ -610,6 +715,8 @@ impl EmitterWriter { pub fn stderr( color_config: ColorConfig, source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, terminal_width: Option<usize>, @@ -619,6 +726,8 @@ impl EmitterWriter { EmitterWriter { dst, sm: source_map, + fluent_bundle, + fallback_bundle, short_message, teach, ui_testing: false, @@ -630,6 +739,8 @@ impl EmitterWriter { pub fn new( dst: Box<dyn Write + Send>, source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, colored: bool, @@ -639,6 +750,8 @@ impl EmitterWriter { EmitterWriter { dst: Raw(dst, colored), sm: source_map, + fluent_bundle, + fallback_bundle, short_message, teach, ui_testing: false, @@ -1176,7 +1289,8 @@ impl EmitterWriter { fn msg_to_buffer( &self, buffer: &mut StyledBuffer, - msg: &[(String, Style)], + msg: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, padding: usize, label: &str, override_style: Option<Style>, @@ -1229,6 +1343,7 @@ impl EmitterWriter { // very *weird* formats // see? for &(ref text, ref style) in msg.iter() { + let text = self.translate_message(text, args); let lines = text.split('\n').collect::<Vec<_>>(); if lines.len() > 1 { for (i, line) in lines.iter().enumerate() { @@ -1239,7 +1354,7 @@ impl EmitterWriter { buffer.append(line_number, line, style_or_override(*style, override_style)); } } else { - buffer.append(line_number, text, style_or_override(*style, override_style)); + buffer.append(line_number, &text, style_or_override(*style, override_style)); } } } @@ -1247,7 +1362,8 @@ impl EmitterWriter { fn emit_message_default( &mut self, msp: &MultiSpan, - msg: &[(String, Style)], + msg: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, code: &Option<DiagnosticId>, level: &Level, max_line_num_len: usize, @@ -1266,7 +1382,7 @@ impl EmitterWriter { buffer.append(0, level.to_str(), Style::MainHeaderMsg); buffer.append(0, ": ", Style::NoStyle); } - self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None); + self.msg_to_buffer(&mut buffer, msg, args, max_line_num_len, "note", None); } else { let mut label_width = 0; // The failure note level itself does not provide any useful diagnostic information @@ -1287,8 +1403,9 @@ impl EmitterWriter { label_width += 2; } for &(ref text, _) in msg.iter() { + let text = self.translate_message(text, args); // Account for newlines to align output to its label. - for (line, text) in normalize_whitespace(text).lines().enumerate() { + for (line, text) in normalize_whitespace(&text).lines().enumerate() { buffer.append( 0 + line, &format!( @@ -1302,7 +1419,7 @@ impl EmitterWriter { } } - let mut annotated_files = FileWithAnnotatedLines::collect_annotations(msp, &self.sm); + let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp); // Make sure our primary file comes first let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) = @@ -1586,6 +1703,7 @@ impl EmitterWriter { fn emit_suggestion_default( &mut self, suggestion: &CodeSuggestion, + args: &FluentArgs<'_>, level: &Level, max_line_num_len: usize, ) -> io::Result<()> { @@ -1612,6 +1730,7 @@ impl EmitterWriter { self.msg_to_buffer( &mut buffer, &[(suggestion.msg.to_owned(), Style::NoStyle)], + args, max_line_num_len, "suggestion", Some(Style::HeaderMsg), @@ -1852,7 +1971,8 @@ impl EmitterWriter { fn emit_messages_default( &mut self, level: &Level, - message: &[(String, Style)], + message: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, code: &Option<DiagnosticId>, span: &MultiSpan, children: &[SubDiagnostic], @@ -1865,7 +1985,7 @@ impl EmitterWriter { num_decimal_digits(n) }; - match self.emit_message_default(span, message, code, level, max_line_num_len, false) { + match self.emit_message_default(span, message, args, code, level, max_line_num_len, false) { Ok(()) => { if !children.is_empty() || suggestions.iter().any(|s| s.style != SuggestionStyle::CompletelyHidden) @@ -1888,7 +2008,8 @@ impl EmitterWriter { let span = child.render_span.as_ref().unwrap_or(&child.span); if let Err(err) = self.emit_message_default( &span, - &child.styled_message(), + &child.message, + args, &None, &child.level, max_line_num_len, @@ -1904,6 +2025,7 @@ impl EmitterWriter { if let Err(e) = self.emit_message_default( &MultiSpan::new(), &[(sugg.msg.to_owned(), Style::HeaderMsg)], + args, &None, &Level::Help, max_line_num_len, @@ -1912,7 +2034,7 @@ impl EmitterWriter { panic!("failed to emit error: {}", e); } } else if let Err(e) = - self.emit_suggestion_default(sugg, &Level::Help, max_line_num_len) + self.emit_suggestion_default(sugg, args, &Level::Help, max_line_num_len) { panic!("failed to emit error: {}", e); }; @@ -1938,8 +2060,9 @@ impl FileWithAnnotatedLines { /// Preprocess all the annotations so that they are grouped by file and by line number /// This helps us quickly iterate over the whole message (including secondary file spans) pub fn collect_annotations( + emitter: &dyn Emitter, + args: &FluentArgs<'_>, msp: &MultiSpan, - source_map: &Option<Lrc<SourceMap>>, ) -> Vec<FileWithAnnotatedLines> { fn add_annotation_to_file( file_vec: &mut Vec<FileWithAnnotatedLines>, @@ -1974,7 +2097,7 @@ impl FileWithAnnotatedLines { let mut output = vec![]; let mut multiline_annotations = vec![]; - if let Some(ref sm) = source_map { + if let Some(ref sm) = emitter.source_map() { for span_label in msp.span_labels() { if span_label.span.is_dummy() { continue; @@ -2001,7 +2124,10 @@ impl FileWithAnnotatedLines { start_col: lo.col_display, end_col: hi.col_display, is_primary: span_label.is_primary, - label: span_label.label, + label: span_label + .label + .as_ref() + .map(|m| emitter.translate_message(m, args).to_string()), overlaps_exactly: false, }; multiline_annotations.push((lo.file, ml)); @@ -2010,7 +2136,10 @@ impl FileWithAnnotatedLines { start_col: lo.col_display, end_col: hi.col_display, is_primary: span_label.is_primary, - label: span_label.label, + label: span_label + .label + .as_ref() + .map(|m| emitter.translate_message(m, args).to_string()), annotation_type: AnnotationType::Singleline, }; add_annotation_to_file(&mut output, lo.file, lo.line, ann); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index dc28d1bb452..f78490da245 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -15,12 +15,13 @@ use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; use crate::DiagnosticId; use crate::ToolMetadata; -use crate::{CodeSuggestion, SubDiagnostic}; +use crate::{CodeSuggestion, FluentBundle, MultiSpan, SpanLabel, SubDiagnostic}; use rustc_lint_defs::Applicability; use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; use rustc_span::hygiene::ExpnData; -use rustc_span::{MultiSpan, Span, SpanLabel}; +use rustc_span::Span; use std::io::{self, Write}; use std::path::Path; use std::sync::{Arc, Mutex}; @@ -36,6 +37,8 @@ pub struct JsonEmitter { dst: Box<dyn Write + Send>, registry: Option<Registry>, sm: Lrc<SourceMap>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, pretty: bool, ui_testing: bool, json_rendered: HumanReadableErrorType, @@ -47,6 +50,8 @@ impl JsonEmitter { pub fn stderr( registry: Option<Registry>, source_map: Lrc<SourceMap>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, pretty: bool, json_rendered: HumanReadableErrorType, terminal_width: Option<usize>, @@ -56,6 +61,8 @@ impl JsonEmitter { dst: Box::new(io::BufWriter::new(io::stderr())), registry, sm: source_map, + fluent_bundle, + fallback_bundle, pretty, ui_testing: false, json_rendered, @@ -67,6 +74,8 @@ impl JsonEmitter { pub fn basic( pretty: bool, json_rendered: HumanReadableErrorType, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, terminal_width: Option<usize>, macro_backtrace: bool, ) -> JsonEmitter { @@ -74,6 +83,8 @@ impl JsonEmitter { JsonEmitter::stderr( None, Lrc::new(SourceMap::new(file_path_mapping)), + fluent_bundle, + fallback_bundle, pretty, json_rendered, terminal_width, @@ -85,6 +96,8 @@ impl JsonEmitter { dst: Box<dyn Write + Send>, registry: Option<Registry>, source_map: Lrc<SourceMap>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, pretty: bool, json_rendered: HumanReadableErrorType, terminal_width: Option<usize>, @@ -94,6 +107,8 @@ impl JsonEmitter { dst, registry, sm: source_map, + fluent_bundle, + fallback_bundle, pretty, ui_testing: false, json_rendered, @@ -173,6 +188,14 @@ impl Emitter for JsonEmitter { Some(&self.sm) } + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + self.fluent_bundle.as_ref() + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + &self.fallback_bundle + } + fn should_show_explain(&self) -> bool { !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) } @@ -345,14 +368,18 @@ struct UnusedExterns<'a, 'b, 'c> { impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { - let sugg = diag.suggestions.iter().flatten().map(|sugg| Diagnostic { - message: sugg.msg.clone(), - code: None, - level: "help", - spans: DiagnosticSpan::from_suggestion(sugg, je), - children: vec![], - rendered: None, - tool_metadata: sugg.tool_metadata.clone(), + let args = je.to_fluent_args(diag.args()); + let sugg = diag.suggestions.iter().flatten().map(|sugg| { + let translated_message = je.translate_message(&sugg.msg, &args); + Diagnostic { + message: translated_message.to_string(), + code: None, + level: "help", + spans: DiagnosticSpan::from_suggestion(sugg, &args, je), + children: vec![], + rendered: None, + tool_metadata: sugg.tool_metadata.clone(), + } }); // generate regular command line output and store it in the json @@ -375,6 +402,8 @@ impl Diagnostic { .new_emitter( Box::new(buf), Some(je.sm.clone()), + je.fluent_bundle.clone(), + je.fallback_bundle.clone(), false, je.terminal_width, je.macro_backtrace, @@ -384,15 +413,16 @@ impl Diagnostic { let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); let output = String::from_utf8(output).unwrap(); + let translated_message = je.translate_messages(&diag.message, &args); Diagnostic { - message: diag.message(), + message: translated_message.to_string(), code: DiagnosticCode::map_opt_string(diag.code.clone(), je), level: diag.level.to_str(), - spans: DiagnosticSpan::from_multispan(&diag.span, je), + spans: DiagnosticSpan::from_multispan(&diag.span, &args, je), children: diag .children .iter() - .map(|c| Diagnostic::from_sub_diagnostic(c, je)) + .map(|c| Diagnostic::from_sub_diagnostic(c, &args, je)) .chain(sugg) .collect(), rendered: Some(output), @@ -400,16 +430,21 @@ impl Diagnostic { } } - fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic { + fn from_sub_diagnostic( + diag: &SubDiagnostic, + args: &FluentArgs<'_>, + je: &JsonEmitter, + ) -> Diagnostic { + let translated_message = je.translate_messages(&diag.message, args); Diagnostic { - message: diag.message(), + message: translated_message.to_string(), code: None, level: diag.level.to_str(), spans: diag .render_span .as_ref() - .map(|sp| DiagnosticSpan::from_multispan(sp, je)) - .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)), + .map(|sp| DiagnosticSpan::from_multispan(sp, args, je)) + .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)), children: vec![], rendered: None, tool_metadata: ToolMetadata::default(), @@ -421,9 +456,16 @@ impl DiagnosticSpan { fn from_span_label( span: SpanLabel, suggestion: Option<(&String, Applicability)>, + args: &FluentArgs<'_>, je: &JsonEmitter, ) -> DiagnosticSpan { - Self::from_span_etc(span.span, span.is_primary, span.label, suggestion, je) + Self::from_span_etc( + span.span, + span.is_primary, + span.label.as_ref().map(|m| je.translate_message(m, args)).map(|m| m.to_string()), + suggestion, + je, + ) } fn from_span_etc( @@ -486,14 +528,22 @@ impl DiagnosticSpan { } } - fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> { + fn from_multispan( + msp: &MultiSpan, + args: &FluentArgs<'_>, + je: &JsonEmitter, + ) -> Vec<DiagnosticSpan> { msp.span_labels() .into_iter() - .map(|span_str| Self::from_span_label(span_str, None, je)) + .map(|span_str| Self::from_span_label(span_str, None, args, je)) .collect() } - fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec<DiagnosticSpan> { + fn from_suggestion( + suggestion: &CodeSuggestion, + args: &FluentArgs<'_>, + je: &JsonEmitter, + ) -> Vec<DiagnosticSpan> { suggestion .substitutions .iter() @@ -504,6 +554,7 @@ impl DiagnosticSpan { DiagnosticSpan::from_span_label( span_label, Some((&suggestion_inner.snippet, suggestion.applicability)), + args, je, ) }) diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index ed01afe6e30..0f175c732c1 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -39,12 +39,16 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); + let fallback_bundle = + crate::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), None, sm, + None, + fallback_bundle, true, HumanReadableErrorType::Short(ColorConfig::Never), None, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ec00910ec8b..c927fcb2109 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -31,11 +31,15 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; +pub use rustc_error_messages::{ + fallback_fluent_bundle, fluent_bundle, DiagnosticMessage, FluentBundle, LanguageIdentifier, + MultiSpan, SpanLabel, +}; pub use rustc_lint_defs::{pluralize, Applicability}; use rustc_serialize::json::Json; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::source_map::SourceMap; -use rustc_span::{Loc, MultiSpan, Span}; +use rustc_span::{Loc, Span}; use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -55,6 +59,7 @@ mod lock; pub mod registry; mod snippet; mod styled_buffer; + pub use snippet::Style; pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a, ErrorGuaranteed>>; @@ -145,7 +150,7 @@ pub struct CodeSuggestion { /// ] /// ``` pub substitutions: Vec<Substitution>, - pub msg: String, + pub msg: DiagnosticMessage, /// Visual representation of this suggestion. pub style: SuggestionStyle, /// Whether or not the suggestion is approximate @@ -400,7 +405,10 @@ impl fmt::Display for ExplicitBug { impl error::Error for ExplicitBug {} -pub use diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic}; +pub use diagnostic::{ + Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, + IntoDiagnosticArg, SubDiagnostic, +}; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee}; use std::backtrace::Backtrace; @@ -538,10 +546,14 @@ impl Handler { can_emit_warnings: bool, treat_err_as_bug: Option<NonZeroUsize>, sm: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, ) -> Self { Self::with_tty_emitter_and_flags( color_config, sm, + fluent_bundle, + fallback_bundle, HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, ) } @@ -549,11 +561,15 @@ impl Handler { pub fn with_tty_emitter_and_flags( color_config: ColorConfig, sm: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, flags: HandlerFlags, ) -> Self { let emitter = Box::new(EmitterWriter::stderr( color_config, sm, + fluent_bundle, + fallback_bundle, false, false, None, @@ -658,7 +674,7 @@ impl Handler { pub fn struct_span_warn( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_warn(msg); result.set_span(span); @@ -669,7 +685,7 @@ impl Handler { pub fn struct_span_allow( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_allow(msg); result.set_span(span); @@ -681,7 +697,7 @@ impl Handler { pub fn struct_span_warn_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_span_warn(span, msg); @@ -694,17 +710,21 @@ impl Handler { /// Attempting to `.emit()` the builder will only emit if either: /// * `can_emit_warnings` is `true` /// * `is_force_warn` was set in `DiagnosticId::Lint` - pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Warning, msg) } /// Construct a builder at the `Allow` level with the `msg`. - pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Allow, msg) } /// Construct a builder at the `Expect` level with the `msg`. - pub fn struct_expect(&self, msg: &str, id: LintExpectationId) -> DiagnosticBuilder<'_, ()> { + pub fn struct_expect( + &self, + msg: impl Into<DiagnosticMessage>, + id: LintExpectationId, + ) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Expect(id), msg) } @@ -712,7 +732,7 @@ impl Handler { pub fn struct_span_err( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_err(msg); result.set_span(span); @@ -723,7 +743,7 @@ impl Handler { pub fn struct_span_err_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_span_err(span, msg); @@ -733,20 +753,23 @@ impl Handler { /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). - pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - DiagnosticBuilder::new_guaranteeing_error::<{ Level::Error { lint: false } }>(self, msg) + pub fn struct_err( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg) } /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. #[doc(hidden)] - pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_err_lint(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Error { lint: true }, msg) } /// Construct a builder at the `Error` level with the `msg` and the `code`. pub fn struct_err_with_code( &self, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_err(msg); @@ -754,11 +777,22 @@ impl Handler { result } + /// Construct a builder at the `Warn` level with the `msg` and the `code`. + pub fn struct_warn_with_code( + &self, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, ()> { + let mut result = self.struct_warn(msg); + result.code(code); + result + } + /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. pub fn struct_span_fatal( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, !> { let mut result = self.struct_fatal(msg); result.set_span(span); @@ -769,7 +803,7 @@ impl Handler { pub fn struct_span_fatal_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, !> { let mut result = self.struct_span_fatal(span, msg); @@ -778,21 +812,24 @@ impl Handler { } /// Construct a builder at the `Error` level with the `msg`. - pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, !> { + pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { DiagnosticBuilder::new_fatal(self, msg) } /// Construct a builder at the `Help` level with the `msg`. - pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Help, msg) } /// Construct a builder at the `Note` level with the `msg`. - pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_note_without_error( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Note, msg) } - pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> ! { + pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); FatalError.raise() } @@ -800,80 +837,106 @@ impl Handler { pub fn span_fatal_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> ! { self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); FatalError.raise() } - pub fn span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed { + pub fn span_err( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap() } - pub fn span_err_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) { + pub fn span_err_with_code( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { self.emit_diag_at_span( Diagnostic::new_with_code(Error { lint: false }, Some(code), msg), span, ); } - pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: &str) { + pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { self.emit_diag_at_span(Diagnostic::new(Warning, msg), span); } - pub fn span_warn_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) { + pub fn span_warn_with_code( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span); } - pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ! { + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.inner.borrow_mut().span_bug(span, msg) } #[track_caller] - pub fn delay_span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed { + pub fn delay_span_bug( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.inner.borrow_mut().delay_span_bug(span, msg) } // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's // where the explanation of what "good path" is (also, it should be renamed). - pub fn delay_good_path_bug(&self, msg: &str) { + pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) { self.inner.borrow_mut().delay_good_path_bug(msg) } - pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: &str) { + pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { self.emit_diag_at_span(Diagnostic::new(Bug, msg), span); } - pub fn span_note_without_error(&self, span: impl Into<MultiSpan>, msg: &str) { + pub fn span_note_without_error( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) { self.emit_diag_at_span(Diagnostic::new(Note, msg), span); } - pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn span_note_diag( + &self, + span: Span, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { let mut db = DiagnosticBuilder::new(self, Note, msg); db.set_span(span); db } // NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread - pub fn fatal(&self, msg: &str) -> FatalError { + pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> FatalError { self.inner.borrow_mut().fatal(msg) } - pub fn err(&self, msg: &str) -> ErrorGuaranteed { + pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.inner.borrow_mut().err(msg) } - pub fn warn(&self, msg: &str) { + pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { let mut db = DiagnosticBuilder::new(self, Warning, msg); db.emit(); } - pub fn note_without_error(&self, msg: &str) { + pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) { DiagnosticBuilder::new(self, Note, msg).emit(); } - pub fn bug(&self, msg: &str) -> ! { + pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! { self.inner.borrow_mut().bug(msg) } @@ -1143,7 +1206,10 @@ impl HandlerInner { match (errors.len(), warnings.len()) { (0, 0) => return, - (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(Level::Warning, &warnings)), + (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new( + Level::Warning, + DiagnosticMessage::Str(warnings.to_owned()), + )), (_, 0) => { let _ = self.fatal(&errors); } @@ -1218,7 +1284,7 @@ impl HandlerInner { } } - fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ! { + fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); panic::panic_any(ExplicitBug); } @@ -1228,7 +1294,11 @@ impl HandlerInner { } #[track_caller] - fn delay_span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed { + fn delay_span_bug( + &mut self, + sp: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. // FIXME: Would be nice to increment err_count in a more coherent way. @@ -1244,7 +1314,7 @@ impl HandlerInner { // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's // where the explanation of what "good path" is (also, it should be renamed). - fn delay_good_path_bug(&mut self, msg: &str) { + fn delay_good_path_bug(&mut self, msg: impl Into<DiagnosticMessage>) { let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); if self.flags.report_delayed_bugs { self.emit_diagnostic(&mut diagnostic); @@ -1253,33 +1323,37 @@ impl HandlerInner { self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); } - fn failure(&mut self, msg: &str) { + fn failure(&mut self, msg: impl Into<DiagnosticMessage>) { self.emit_diagnostic(&mut Diagnostic::new(FailureNote, msg)); } - fn fatal(&mut self, msg: &str) -> FatalError { + fn fatal(&mut self, msg: impl Into<DiagnosticMessage>) -> FatalError { self.emit(Fatal, msg); FatalError } - fn err(&mut self, msg: &str) -> ErrorGuaranteed { + fn err(&mut self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.emit(Error { lint: false }, msg) } /// Emit an error; level should be `Error` or `Fatal`. - fn emit(&mut self, level: Level, msg: &str) -> ErrorGuaranteed { + fn emit(&mut self, level: Level, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { if self.treat_err_as_bug() { self.bug(msg); } self.emit_diagnostic(&mut Diagnostic::new(level, msg)).unwrap() } - fn bug(&mut self, msg: &str) -> ! { + fn bug(&mut self, msg: impl Into<DiagnosticMessage>) -> ! { self.emit_diagnostic(&mut Diagnostic::new(Bug, msg)); panic::panic_any(ExplicitBug); } - fn flush_delayed(&mut self, bugs: impl IntoIterator<Item = Diagnostic>, explanation: &str) { + fn flush_delayed( + &mut self, + bugs: impl IntoIterator<Item = Diagnostic>, + explanation: impl Into<DiagnosticMessage> + Copy, + ) { let mut no_bugs = true; for mut bug in bugs { if no_bugs { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 7f569af4abd..556b2c6fbf3 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -10,7 +10,7 @@ use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; -use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; @@ -20,7 +20,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::default::Default; diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index b55a40c5b2c..0e20e0970f4 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -109,10 +109,11 @@ use crate::mbe::{KleeneToken, TokenTree}; use rustc_ast::token::{DelimToken, Token, TokenKind}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_data_structures::fx::FxHashMap; +use rustc_errors::MultiSpan; use rustc_session::lint::builtin::META_VARIABLE_MISUSE; use rustc_session::parse::ParseSess; use rustc_span::symbol::kw; -use rustc_span::{symbol::MacroRulesNormalizedIdent, MultiSpan, Span}; +use rustc_span::{symbol::MacroRulesNormalizedIdent, Span}; use smallvec::SmallVec; @@ -249,7 +250,7 @@ fn check_binders( if let Some(prev_info) = binders.get(&name) { // 1. The meta-variable is already bound in the current LHS: This is an error. let mut span = MultiSpan::from_span(span); - span.push_span_label(prev_info.span, "previous declaration".into()); + span.push_span_label(prev_info.span, "previous declaration"); buffer_lint(sess, span, node_id, "duplicate matcher binding"); } else if get_binder_info(macros, binders, name).is_none() { // 2. The meta-variable is free: This is a binder. @@ -621,7 +622,7 @@ fn ops_is_prefix( for (i, binder) in binder_ops.iter().enumerate() { if i >= occurrence_ops.len() { let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition".into()); + span.push_span_label(binder.span, "expected repetition"); let message = &format!("variable '{}' is still repeating at this depth", name); buffer_lint(sess, span, node_id, message); return; @@ -629,8 +630,8 @@ fn ops_is_prefix( let occurrence = &occurrence_ops[i]; if occurrence.op != binder.op { let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition".into()); - span.push_span_label(occurrence.span, "conflicting repetition".into()); + span.push_span_label(binder.span, "expected repetition"); + span.push_span_label(occurrence.span, "conflicting repetition"); let message = "meta-variable repeats with different Kleene operator"; buffer_lint(sess, span, node_id, message); return; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 10b2b9f07e2..7fef87ed977 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -68,17 +68,18 @@ fn emit_frag_parse_err( arm_span: Span, kind: AstFragmentKind, ) { - if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") { + // FIXME(davidtwco): avoid depending on the error message text + if parser.token == token::Eof && e.message[0].0.expect_str().ends_with(", found `<eof>`") { if !e.span.is_dummy() { // early end of macro arm (#52866) e.replace_span_with(parser.sess.source_map().next_point(parser.token.span)); } let msg = &e.message[0]; e.message[0] = ( - format!( + rustc_errors::DiagnosticMessage::Str(format!( "macro expansion ends with an incomplete expression: {}", - msg.0.replace(", found `<eof>`", ""), - ), + msg.0.expect_str().replace(", found `<eof>`", ""), + )), msg.1, ); } diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 2949ca716b2..52a656e1d1c 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -128,13 +128,15 @@ fn parse_ident<'sess>( sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, Ident> { - let err_fn = |msg| sess.span_diagnostic.struct_span_err(span, msg); if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt { if let Some((elem, false)) = token.ident() { return Ok(elem); } let token_str = pprust::token_to_string(&token); - let mut err = err_fn(&format!("expected identifier, found `{}`", &token_str)); + let mut err = sess.span_diagnostic.struct_span_err( + span, + &format!("expected identifier, found `{}`", &token_str) + ); err.span_suggestion( token.span, &format!("try removing `{}`", &token_str), @@ -143,7 +145,7 @@ fn parse_ident<'sess>( ); return Err(err); } - Err(err_fn("expected identifier")) + Err(sess.span_diagnostic.struct_span_err(span, "expected identifier")) } /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 20351070f71..bd6f0b77ebf 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -7,13 +7,13 @@ use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing} use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Diagnostic, PResult}; +use rustc_errors::{Diagnostic, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str}; use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; use rustc_span::symbol::{self, kw, sym, Symbol}; -use rustc_span::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span}; +use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; use pm::bridge::{server, TokenTree}; use pm::{Delimiter, Level, LineColumn, Spacing}; diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index ed3aa1eaca8..88f3cf9d722 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -4,11 +4,11 @@ use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_str use rustc_session::parse::ParseSess; use rustc_span::create_default_session_if_not_set_then; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{BytePos, MultiSpan, Span}; +use rustc_span::{BytePos, Span}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Handler, PResult}; +use rustc_errors::{Handler, MultiSpan, PResult}; use std::io; use std::io::prelude::*; @@ -127,6 +127,8 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & create_default_session_if_not_set_then(|_| { let output = Arc::new(Mutex::new(Vec::new())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) + .expect("failed to load fallback fluent bundle"); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); @@ -142,6 +144,8 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & let emitter = EmitterWriter::new( Box::new(Shared { data: output.clone() }), Some(source_map.clone()), + None, + fallback_bundle, false, false, false, diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 41c63440ba3..34d366f4013 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -11,6 +11,7 @@ rustc_target = { path = "../rustc_target" } rustc_feature = { path = "../rustc_feature" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_error_messages = { path = "../rustc_error_messages" } rustc_index = { path = "../rustc_index" } rustc_span = { path = "../rustc_span" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 10871df3ab9..15118073b33 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -13,12 +13,13 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedMap; +use rustc_error_messages::MultiSpan; use rustc_index::vec::IndexVec; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{def_id::LocalDefId, BytePos, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP}; use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::spec::abi::Abi; diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index d915f9a5ae8..f25215fe813 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -59,7 +59,7 @@ use crate::traits::{ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed}; -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; @@ -72,7 +72,7 @@ use rustc_middle::ty::{ subst::{GenericArgKind, Subst, SubstsRef}, Binder, List, Region, Ty, TyCtxt, TypeFoldable, }; -use rustc_span::{sym, BytePos, DesugaringKind, MultiSpan, Pos, Span}; +use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::ops::ControlFlow; use std::{cmp, fmt, iter}; @@ -2075,7 +2075,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { diag.span_suggestion( span, - msg, + *msg, format!("{}.as_ref()", snippet), Applicability::MachineApplicable, ); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 61e3334862b..80500f3fe65 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -7,11 +7,10 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; use rustc_data_structures::stable_set::FxHashSet; -use rustc_errors::{Applicability, ErrorGuaranteed}; +use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::TypeVisitor; -use rustc_span::MultiSpan; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorGuaranteed> { @@ -42,8 +41,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); // FIXME: we should point at the lifetime let mut multi_span: MultiSpan = vec![binding_span].into(); - multi_span - .push_span_label(binding_span, "introduces a `'static` lifetime requirement".into()); + multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement"); err.span_note(multi_span, "because this has an unmet lifetime requirement"); note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span)); if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index c33c0c996bd..a4c46d5cf0b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -5,7 +5,7 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::stable_set::FxHashSet; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; @@ -13,7 +13,7 @@ use rustc_middle::ty::{ self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeFoldable, TypeVisitor, }; use rustc_span::symbol::Ident; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; use std::ops::ControlFlow; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 6d23dc4f471..1788eb8628a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -4,7 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, Subtype}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -12,8 +12,7 @@ use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; - -use rustc_span::{MultiSpan, Span, Symbol}; +use rustc_span::{Span, Symbol}; use std::ops::ControlFlow; diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 5dcac7f56cc..baea3e8285a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, Region}; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { - let mut label_or_note = |span, msg| { + let mut label_or_note = |span, msg: &str| { let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count(); let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span); diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 25b11e31d57..d297640c140 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -2,11 +2,11 @@ use super::ObjectSafetyViolation; use crate::infer::InferCtxt; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::TyCtxt; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; use std::fmt; use std::iter; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index f2164bccc3e..eac6a33cf22 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -10,7 +10,7 @@ use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::parallel; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{Applicability, ErrorGuaranteed, PResult}; +use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, PResult}; use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand}; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_hir::Crate; @@ -35,7 +35,7 @@ use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{FileName, MultiSpan}; +use rustc_span::FileName; use rustc_trait_selection::traits; use rustc_typeck as typeck; use tempfile::Builder as TempFileBuilder; diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d43c661dda6..e6f9246d732 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -31,7 +31,7 @@ use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::{self, expr_to_string}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{Applicability, Diagnostic, DiagnosticStyledString}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticStyledString, MultiSpan}; use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -49,7 +49,7 @@ use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, InnerSpan, MultiSpan, Span}; +use rustc_span::{BytePos, InnerSpan, Span}; use rustc_target::abi::VariantIdx; use rustc_trait_selection::traits::{self, misc::can_type_implement_copy}; @@ -1571,7 +1571,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { lint.build("bounds on generic parameters are not enforced in type aliases"); let msg = "the bound will not be checked when the type alias is used, \ and should be removed"; - err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); + err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); if !suggested_changing_assoc_types { TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); suggested_changing_assoc_types = true; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 882fa4496ca..3600b6ad212 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -21,7 +21,7 @@ use crate::passes::{EarlyLintPassObject, LateLintPassObject}; use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; -use rustc_errors::{struct_span_err, Applicability, SuggestionStyle}; +use rustc_errors::{struct_span_err, Applicability, MultiSpan, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; @@ -38,7 +38,7 @@ use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintI use rustc_session::Session; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::abi; use tracing::debug; diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 74fef0be9e9..67f5aa0540f 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -37,7 +37,7 @@ fn emit_unfulfilled_expectation_lint( |diag| { let mut diag = diag.build("this lint expectation is unfulfilled"); if let Some(rationale) = expectation.reason { - diag.note(&rationale.as_str()); + diag.note(rationale.as_str()); } if expectation.is_unfulfilled_lint_expectations { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 99a5720832e..01f1d1e79ac 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -3,7 +3,7 @@ use crate::late::unerased_lint_store; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, Applicability, Diagnostic}; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::nested_filter; @@ -20,7 +20,7 @@ use rustc_session::lint::{ use rustc_session::parse::{add_feature_diagnostics, feature_err}; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; use tracing::debug; fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index c95905b9b18..14ac30987b3 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -154,7 +154,7 @@ fn lint_overflowing_range_endpoint<'tcx>( let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); err.span_suggestion( parent_expr.span, - &"use an inclusive range instead", + "use an inclusive range instead", suggestion, Applicability::MachineApplicable, ); @@ -399,7 +399,7 @@ fn lint_uint_literal<'tcx>( lint.build("only `u8` can be cast into `char`") .span_suggestion( par_e.span, - &"use a `char` literal instead", + "use a `char` literal instead", format!("'\\u{{{:X}}}'", lit_val), Applicability::MachineApplicable, ) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 91b72f1d2b1..494bdaa1e2b 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -3,7 +3,7 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext} use rustc_ast as ast; use rustc_ast::util::{classify, parser}; use rustc_ast::{ExprKind, StmtKind}; -use rustc_errors::{pluralize, Applicability}; +use rustc_errors::{pluralize, Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -11,7 +11,7 @@ use rustc_middle::ty::adjustment; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, sym}; -use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{BytePos, Span, DUMMY_SP}; declare_lint! { /// The `unused_must_use` lint detects unused result of a type flagged as diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index 8acf7943de9..fcd8c37d677 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_error_messages = { path = "../rustc_error_messages" } rustc_span = { path = "../rustc_span" } rustc_serialize = { path = "../rustc_serialize" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index f0eb27c9002..031b01af5dd 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -7,10 +7,11 @@ pub use self::Level::*; use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::{AttrId, Attribute}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_error_messages::MultiSpan; use rustc_hir::HirId; use rustc_serialize::json::Json; use rustc_span::edition::Edition; -use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol}; +use rustc_span::{sym, symbol::Ident, Span, Symbol}; use rustc_target::spec::abi::Abi; pub mod builtin; diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 03e139755ba..3589860eb0e 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -63,9 +63,14 @@ decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_fo decl_derive!([Lift, attributes(lift)] => lift::lift_derive); decl_derive!( [SessionDiagnostic, attributes( - message, - lint, + // struct attributes + warning, error, + note, + help, + // field attributes + skip_arg, + primary_span, label, suggestion, suggestion_short, diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs index c9e404903f5..46f698f6f9b 100644 --- a/compiler/rustc_macros/src/session_diagnostic.rs +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -5,8 +5,9 @@ use syn::spanned::Spanned; use std::collections::{BTreeSet, HashMap}; -/// Implements #[derive(SessionDiagnostic)], which allows for errors to be specified as a struct, independent -/// from the actual diagnostics emitting code. +/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct, +/// independent from the actual diagnostics emitting code. +/// /// ```ignore (pseudo-rust) /// # extern crate rustc_errors; /// # use rustc_errors::Applicability; @@ -15,11 +16,11 @@ use std::collections::{BTreeSet, HashMap}; /// # extern crate rust_middle; /// # use rustc_middle::ty::Ty; /// #[derive(SessionDiagnostic)] -/// #[code = "E0505"] -/// #[error = "cannot move out of {name} because it is borrowed"] +/// #[error(code = "E0505", slug = "move-out-of-borrow-error")] /// pub struct MoveOutOfBorrowError<'tcx> { /// pub name: Ident, /// pub ty: Ty<'tcx>, +/// #[primary_span] /// #[label = "cannot move out of borrow"] /// pub span: Span, /// #[label = "`{ty}` first borrowed here"] @@ -28,6 +29,7 @@ use std::collections::{BTreeSet, HashMap}; /// pub opt_sugg: Option<(Span, Applicability)> /// } /// ``` +/// /// Then, later, to emit the error: /// /// ```ignore (pseudo-rust) @@ -47,10 +49,10 @@ pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2: SessionDiagnosticDerive::new(diag, sess, s).into_tokens() } -// Checks whether the type name of `ty` matches `name`. -// -// Given some struct at a::b::c::Foo, this will return true for c::Foo, b::c::Foo, or -// a::b::c::Foo. This reasonably allows qualified names to be used in the macro. +/// Checks whether the type name of `ty` matches `name`. +/// +/// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or +/// `a::b::c::Foo`. This reasonably allows qualified names to be used in the macro. fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool { if let syn::Type::Path(ty) = ty { ty.path @@ -65,7 +67,7 @@ fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool { } } -/// The central struct for constructing the as_error method from an annotated struct. +/// The central struct for constructing the `as_error` method from an annotated struct. struct SessionDiagnosticDerive<'a> { structure: synstructure::Structure<'a>, builder: SessionDiagnosticDeriveBuilder<'a>, @@ -77,13 +79,6 @@ impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError { } } -/// Equivalent to rustc:errors::diagnostic::DiagnosticId, except stores the quoted expression to -/// initialise the code with. -enum DiagnosticId { - Error(proc_macro2::TokenStream), - Lint(proc_macro2::TokenStream), -} - #[derive(Debug)] enum SessionDiagnosticDeriveError { SynError(syn::Error), @@ -98,7 +93,7 @@ impl SessionDiagnosticDeriveError { // Return ! to avoid having to create a blank DiagnosticBuilder to return when an // error has already been emitted to the compiler. quote! { - unreachable!() + { unreachable!(); } } } } @@ -109,9 +104,10 @@ fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnost Diagnostic::spanned(span, proc_macro::Level::Error, msg) } -/// For methods that return a Result<_, SessionDiagnosticDeriveError>: emit a diagnostic on -/// span $span with msg $msg (and, optionally, perform additional decoration using the FnOnce -/// passed in `diag`). Then, return Err(ErrorHandled). +/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`: +/// +/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration +/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. macro_rules! throw_span_err { ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }}; ($span:expr, $msg:expr, $f:expr) => {{ @@ -119,8 +115,8 @@ macro_rules! throw_span_err { }}; } -/// When possible, prefer using throw_span_err! over using this function directly. This only exists -/// as a function to constrain `f` to an impl FnOnce. +/// When possible, prefer using `throw_span_err!` over using this function directly. This only +/// exists as a function to constrain `f` to an `impl FnOnce`. fn _throw_span_err( span: impl proc_macro::MultiSpan, msg: &str, @@ -149,17 +145,25 @@ impl<'a> SessionDiagnosticDerive<'a> { } Self { - builder: SessionDiagnosticDeriveBuilder { diag, sess, fields: fields_map, kind: None }, + builder: SessionDiagnosticDeriveBuilder { + diag, + sess, + fields: fields_map, + kind: None, + code: None, + slug: None, + }, structure, } } + fn into_tokens(self) -> proc_macro2::TokenStream { - let SessionDiagnosticDerive { structure, mut builder } = self; + let SessionDiagnosticDerive { mut structure, mut builder } = self; let ast = structure.ast(); let attrs = &ast.attrs; - let implementation = { + let (implementation, param_ty) = { if let syn::Data::Struct(..) = ast.data { let preamble = { let preamble = attrs.iter().map(|attr| { @@ -167,16 +171,23 @@ impl<'a> SessionDiagnosticDerive<'a> { .generate_structure_code(attr) .unwrap_or_else(|v| v.to_compile_error()) }); + quote! { #(#preamble)*; } }; - let body = structure.each(|field_binding| { + // Generates calls to `span_label` and similar functions based on the attributes + // on fields. Code for suggestions uses formatting machinery and the value of + // other fields - because any given field can be referenced multiple times, it + // should be accessed through a borrow. When passing fields to `set_arg` (which + // happens below) for Fluent, we want to move the data, so that has to happen + // in a separate pass over the fields. + let attrs = structure.each(|field_binding| { let field = field_binding.ast(); let result = field.attrs.iter().map(|attr| { builder - .generate_field_code( + .generate_field_attr_code( attr, FieldInfo { vis: &field.vis, @@ -187,52 +198,109 @@ impl<'a> SessionDiagnosticDerive<'a> { ) .unwrap_or_else(|v| v.to_compile_error()) }); - return quote! { - #(#result);* - }; + + quote! { #(#result);* } + }); + + // When generating `set_arg` calls, move data rather than borrow it to avoid + // requiring clones - this must therefore be the last use of each field (for + // example, any formatting machinery that might refer to a field should be + // generated already). + structure.bind_with(|_| synstructure::BindStyle::Move); + let args = structure.each(|field_binding| { + let field = field_binding.ast(); + // When a field has attributes like `#[label]` or `#[note]` then it doesn't + // need to be passed as an argument to the diagnostic. But when a field has no + // attributes then it must be passed as an argument to the diagnostic so that + // it can be referred to by Fluent messages. + if field.attrs.is_empty() { + let diag = &builder.diag; + let ident = field_binding.ast().ident.as_ref().unwrap(); + quote! { + #diag.set_arg( + stringify!(#ident), + #field_binding.into_diagnostic_arg() + ); + } + } else { + quote! {} + } }); - // Finally, putting it altogether. - match builder.kind { - None => { - span_err(ast.span().unwrap(), "`code` not specified") - .help("use the [code = \"...\"] attribute to set this diagnostic's error code ") - .emit(); - SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + + let span = ast.span().unwrap(); + let (diag, sess) = (&builder.diag, &builder.sess); + let init = match (builder.kind, builder.slug) { + (None, _) => { + span_err(span, "diagnostic kind not specified") + .help("use the `#[error(...)]` attribute to create an error") + .emit(); + return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); } - Some((kind, _)) => match kind { - DiagnosticId::Lint(_lint) => todo!(), - DiagnosticId::Error(code) => { - let (diag, sess) = (&builder.diag, &builder.sess); - quote! { - let mut #diag = #sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error(#code)); - #preamble - match self { - #body - } - #diag - } + (Some((kind, _)), None) => { + span_err(span, "`slug` not specified") + .help(&format!("use the `#[{}(slug = \"...\")]` attribute to set this diagnostic's slug", kind.descr())) + .emit(); + return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); + } + (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => { + quote! { + let mut #diag = #sess.struct_err( + rustc_errors::DiagnosticMessage::fluent(#slug), + ); } - }, - } + } + (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => { + quote! { + let mut #diag = #sess.struct_warn( + rustc_errors::DiagnosticMessage::fluent(#slug), + ); + } + } + }; + + let implementation = quote! { + #init + #preamble + match self { + #attrs + } + match self { + #args + } + #diag + }; + let param_ty = match builder.kind { + Some((SessionDiagnosticKind::Error, _)) => { + quote! { rustc_errors::ErrorGuaranteed } + } + Some((SessionDiagnosticKind::Warn, _)) => quote! { () }, + _ => unreachable!(), + }; + + (implementation, param_ty) } else { span_err( ast.span().unwrap(), "`#[derive(SessionDiagnostic)]` can only be used on structs", ) .emit(); - SessionDiagnosticDeriveError::ErrorHandled.to_compile_error() + + let implementation = SessionDiagnosticDeriveError::ErrorHandled.to_compile_error(); + let param_ty = quote! { rustc_errors::ErrorGuaranteed }; + (implementation, param_ty) } }; let sess = &builder.sess; structure.gen_impl(quote! { - gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess> + gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty> for @Self { fn into_diagnostic( self, #sess: &'__session_diagnostic_sess rustc_session::Session - ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, rustc_errors::ErrorGuaranteed> { + ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> { + use rustc_errors::IntoDiagnosticArg; #implementation } } @@ -240,8 +308,8 @@ impl<'a> SessionDiagnosticDerive<'a> { } } -/// Field information passed to the builder. Deliberately omits attrs to discourage the generate_* -/// methods from walking the attributes themselves. +/// Field information passed to the builder. Deliberately omits attrs to discourage the +/// `generate_*` methods from walking the attributes themselves. struct FieldInfo<'a> { vis: &'a syn::Visibility, binding: &'a synstructure::BindingInfo<'a>, @@ -249,108 +317,256 @@ struct FieldInfo<'a> { span: &'a proc_macro2::Span, } +/// What kind of session diagnostic is being derived - an error or a warning? +#[derive(Copy, Clone)] +enum SessionDiagnosticKind { + /// `#[error(..)]` + Error, + /// `#[warn(..)]` + Warn, +} + +impl SessionDiagnosticKind { + /// Returns human-readable string corresponding to the kind. + fn descr(&self) -> &'static str { + match self { + SessionDiagnosticKind::Error => "error", + SessionDiagnosticKind::Warn => "warning", + } + } +} + /// Tracks persistent information required for building up the individual calls to diagnostic -/// methods for the final generated method. This is a separate struct to SessionDerive only to be -/// able to destructure and split self.builder and the self.structure up to avoid a double mut -/// borrow later on. +/// methods for the final generated method. This is a separate struct to `SessionDiagnosticDerive` +/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a +/// double mut borrow later on. struct SessionDiagnosticDeriveBuilder<'a> { - /// Name of the session parameter that's passed in to the as_error method. + /// Name of the session parameter that's passed in to the `as_error` method. sess: syn::Ident, + /// The identifier to use for the generated `DiagnosticBuilder` instance. + diag: syn::Ident, /// Store a map of field name to its corresponding field. This is built on construction of the /// derive builder. fields: HashMap<String, &'a syn::Field>, - /// The identifier to use for the generated DiagnosticBuilder instance. - diag: syn::Ident, - - /// Whether this is a lint or an error. This dictates how the diag will be initialised. Span - /// stores at what Span the kind was first set at (for error reporting purposes, if the kind - /// was multiply specified). - kind: Option<(DiagnosticId, proc_macro2::Span)>, + /// Kind of diagnostic requested via the struct attribute. + kind: Option<(SessionDiagnosticKind, proc_macro::Span)>, + /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that + /// has the actual diagnostic message. + slug: Option<(String, proc_macro::Span)>, + /// Error codes are a optional part of the struct attribute - this is only set to detect + /// multiple specifications. + code: Option<proc_macro::Span>, } impl<'a> SessionDiagnosticDeriveBuilder<'a> { + /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct + /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates + /// diagnostic builder calls for setting error code and creating note/help messages. fn generate_structure_code( &mut self, attr: &syn::Attribute, ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { - Ok(match attr.parse_meta()? { - syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - let formatted_str = self.build_format(&s.value(), attr.span()); - let name = attr.path.segments.last().unwrap().ident.to_string(); - let name = name.as_str(); - match name { - "message" => { - let diag = &self.diag; - quote! { - #diag.set_primary_message(#formatted_str); + let span = attr.span().unwrap(); + + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + let meta = attr.parse_meta()?; + + if matches!(name, "help" | "note") + && matches!(meta, syn::Meta::Path(_) | syn::Meta::NameValue(_)) + { + let diag = &self.diag; + let slug = match &self.slug { + Some((slug, _)) => slug.as_str(), + None => throw_span_err!( + span, + &format!( + "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`", + name, + match meta { + syn::Meta::Path(_) => "", + syn::Meta::NameValue(_) => " = ...", + _ => unreachable!(), + } + ) + ), + }; + let id = match meta { + syn::Meta::Path(..) => quote! { #name }, + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + quote! { #s } + } + _ => unreachable!(), + }; + let fn_name = proc_macro2::Ident::new(name, attr.span()); + + return Ok(quote! { + #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id)); + }); + } + + let nested = match meta { + syn::Meta::List(syn::MetaList { nested, .. }) => nested, + syn::Meta::Path(..) => throw_span_err!( + span, + &format!("`#[{}]` is not a valid `SessionDiagnostic` struct attribute", name) + ), + syn::Meta::NameValue(..) => throw_span_err!( + span, + &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` struct attribute", name) + ), + }; + + let kind = match name { + "error" => SessionDiagnosticKind::Error, + "warning" => SessionDiagnosticKind::Warn, + other => throw_span_err!( + span, + &format!("`#[{}(...)]` is not a valid `SessionDiagnostic` struct attribute", other) + ), + }; + self.set_kind_once(kind, span)?; + + let mut tokens = Vec::new(); + for attr in nested { + let span = attr.span().unwrap(); + let meta = match attr { + syn::NestedMeta::Meta(meta) => meta, + syn::NestedMeta::Lit(_) => throw_span_err!( + span, + &format!( + "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` struct attribute", + name + ) + ), + }; + + let path = meta.path(); + let nested_name = path.segments.last().unwrap().ident.to_string(); + match &meta { + // Struct attributes are only allowed to be applied once, and the diagnostic + // changes will be set in the initialisation code. + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + match nested_name.as_str() { + "slug" => { + self.set_slug_once(s.value(), s.span().unwrap()); + } + "code" => { + tokens.push(self.set_code_once(s.value(), s.span().unwrap())); + } + other => { + let diag = span_err( + span, + &format!( + "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute", + name, other + ), + ); + diag.emit(); } } - attr @ "error" | attr @ "lint" => { - self.set_kind_once( - if attr == "error" { - DiagnosticId::Error(formatted_str) - } else if attr == "lint" { - DiagnosticId::Lint(formatted_str) - } else { - unreachable!() - }, - s.span(), - )?; - // This attribute is only allowed to be applied once, and the attribute - // will be set in the initialisation code. - quote! {} - } - other => throw_span_err!( - attr.span().unwrap(), + } + syn::Meta::NameValue(..) => { + span_err( + span, &format!( - "`#[{} = ...]` is not a valid SessionDiagnostic struct attribute", - other - ) - ), + "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute", + name, nested_name + ), + ) + .help("value must be a string") + .emit(); + } + syn::Meta::Path(..) => { + span_err( + span, + &format!( + "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute", + name, nested_name + ), + ) + .emit(); + } + syn::Meta::List(..) => { + span_err( + span, + &format!( + "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute", + name, nested_name + ), + ) + .emit(); } } - _ => todo!("unhandled meta kind"), - }) + } + + Ok(tokens.drain(..).collect()) } #[must_use] fn set_kind_once( &mut self, - kind: DiagnosticId, - span: proc_macro2::Span, + kind: SessionDiagnosticKind, + span: proc_macro::Span, ) -> Result<(), SessionDiagnosticDeriveError> { - if self.kind.is_none() { - self.kind = Some((kind, span)); - Ok(()) - } else { - let kind_str = |kind: &DiagnosticId| match kind { - DiagnosticId::Lint(..) => "lint", - DiagnosticId::Error(..) => "error", - }; + match self.kind { + None => { + self.kind = Some((kind, span)); + Ok(()) + } + Some((prev_kind, prev_span)) => { + let existing = prev_kind.descr(); + let current = kind.descr(); + + let msg = if current == existing { + format!("`{}` specified multiple times", existing) + } else { + format!("`{}` specified when `{}` was already specified", current, existing) + }; + throw_span_err!(span, &msg, |diag| diag + .span_note(prev_span, "previously specified here")); + } + } + } + + fn set_code_once(&mut self, code: String, span: proc_macro::Span) -> proc_macro2::TokenStream { + match self.code { + None => { + self.code = Some(span); + } + Some(prev_span) => { + span_err(span, "`code` specified multiple times") + .span_note(prev_span, "previously specified here") + .emit(); + } + } - let existing_kind = kind_str(&self.kind.as_ref().unwrap().0); - let this_kind = kind_str(&kind); + let diag = &self.diag; + quote! { #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); } + } - let msg = if this_kind == existing_kind { - format!("`{}` specified multiple times", existing_kind) - } else { - format!("`{}` specified when `{}` was already specified", this_kind, existing_kind) - }; - throw_span_err!(span.unwrap(), &msg); + fn set_slug_once(&mut self, slug: String, span: proc_macro::Span) { + match self.slug { + None => { + self.slug = Some((slug, span)); + } + Some((_, prev_span)) => { + span_err(span, "`slug` specified multiple times") + .span_note(prev_span, "previously specified here") + .emit(); + } } } - fn generate_field_code( + fn generate_field_attr_code( &mut self, attr: &syn::Attribute, info: FieldInfo<'_>, ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { let field_binding = &info.binding.binding; - let option_ty = option_inner_ty(&info.ty); - let generated_code = self.generate_non_option_field_code( attr, FieldInfo { @@ -360,15 +576,16 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> { span: info.span, }, )?; - Ok(if option_ty.is_none() { - quote! { #generated_code } + + if option_ty.is_none() { + Ok(quote! { #generated_code }) } else { - quote! { + Ok(quote! { if let Some(#field_binding) = #field_binding { #generated_code } - } - }) + }) + } } fn generate_non_option_field_code( @@ -377,190 +594,261 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> { info: FieldInfo<'_>, ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> { let diag = &self.diag; + let span = attr.span().unwrap(); let field_binding = &info.binding.binding; + let name = attr.path.segments.last().unwrap().ident.to_string(); let name = name.as_str(); - // At this point, we need to dispatch based on the attribute key + the - // type. + let meta = attr.parse_meta()?; - Ok(match meta { - syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - let formatted_str = self.build_format(&s.value(), attr.span()); + match meta { + syn::Meta::Path(_) => match name { + "skip_arg" => { + // Don't need to do anything - by virtue of the attribute existing, the + // `set_arg` call will not be generated. + Ok(quote! {}) + } + "primary_span" => { + self.report_error_if_not_applied_to_span(attr, info)?; + Ok(quote! { + #diag.set_span(*#field_binding); + }) + } + "label" | "note" | "help" => { + self.report_error_if_not_applied_to_span(attr, info)?; + Ok(self.add_subdiagnostic(field_binding, name, name)) + } + other => throw_span_err!( + span, + &format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other) + ), + }, + syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name { + "label" | "note" | "help" => { + self.report_error_if_not_applied_to_span(attr, info)?; + Ok(self.add_subdiagnostic(field_binding, name, &s.value())) + } + other => throw_span_err!( + span, + &format!( + "`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", + other + ) + ), + }, + syn::Meta::NameValue(_) => throw_span_err!( + span, + &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name), + |diag| diag.help("value must be a string") + ), + syn::Meta::List(syn::MetaList { path, nested, .. }) => { + let name = path.segments.last().unwrap().ident.to_string(); + let name = name.as_ref(); + match name { - "message" => { - if type_matches_path(&info.ty, &["rustc_span", "Span"]) { - quote! { - #diag.set_span(*#field_binding); - #diag.set_primary_message(#formatted_str); - } - } else { - throw_span_err!( - attr.span().unwrap(), - "the `#[message = \"...\"]` attribute can only be applied to fields of type Span" - ); - } - } - "label" => { - if type_matches_path(&info.ty, &["rustc_span", "Span"]) { - quote! { - #diag.span_label(*#field_binding, #formatted_str); - } - } else { - throw_span_err!( - attr.span().unwrap(), - "The `#[label = ...]` attribute can only be applied to fields of type Span" - ); - } - } + "suggestion" | "suggestion_short" | "suggestion_hidden" + | "suggestion_verbose" => (), other => throw_span_err!( - attr.span().unwrap(), + span, &format!( - "`#[{} = ...]` is not a valid SessionDiagnostic field attribute", + "`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute", other ) ), - } - } - syn::Meta::List(list) => { - match list.path.segments.iter().last().unwrap().ident.to_string().as_str() { - suggestion_kind @ "suggestion" - | suggestion_kind @ "suggestion_short" - | suggestion_kind @ "suggestion_hidden" - | suggestion_kind @ "suggestion_verbose" => { - // For suggest, we need to ensure we are running on a (Span, - // Applicability) pair. - let (span, applicability) = (|| match &info.ty { - ty @ syn::Type::Path(..) - if type_matches_path(ty, &["rustc_span", "Span"]) => - { - let binding = &info.binding.binding; - Ok(( - quote!(*#binding), - quote!(rustc_errors::Applicability::Unspecified), - )) + }; + + let (span_, applicability) = self.span_and_applicability_of_ty(info)?; + + let mut msg = None; + let mut code = None; + + for attr in nested { + let meta = match attr { + syn::NestedMeta::Meta(meta) => meta, + syn::NestedMeta::Lit(_) => throw_span_err!( + span, + &format!( + "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute", + name + ) + ), + }; + + let span = meta.span().unwrap(); + let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + let nested_name = nested_name.as_str(); + + match meta { + syn::Meta::NameValue(syn::MetaNameValue { + lit: syn::Lit::Str(s), .. + }) => match nested_name { + "message" => { + msg = Some(s.value()); } - syn::Type::Tuple(tup) => { - let mut span_idx = None; - let mut applicability_idx = None; - for (idx, elem) in tup.elems.iter().enumerate() { - if type_matches_path(elem, &["rustc_span", "Span"]) { - if span_idx.is_none() { - span_idx = Some(syn::Index::from(idx)); - } else { - throw_span_err!( - info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more than one Span" - ); - } - } else if type_matches_path( - elem, - &["rustc_errors", "Applicability"], - ) { - if applicability_idx.is_none() { - applicability_idx = Some(syn::Index::from(idx)); - } else { - throw_span_err!( - info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more than one Applicability" - ); - } - } - } - if let Some(span_idx) = span_idx { - let binding = &info.binding.binding; - let span = quote!(#binding.#span_idx); - let applicability = applicability_idx - .map( - |applicability_idx| quote!(#binding.#applicability_idx), - ) - .unwrap_or_else(|| { - quote!(rustc_errors::Applicability::Unspecified) - }); - return Ok((span, applicability)); - } - throw_span_err!( - info.span.unwrap(), - "wrong types for suggestion", - |diag| { - diag.help("#[suggestion(...)] on a tuple field must be applied to fields of type (Span, Applicability)") - } - ); + "code" => { + let formatted_str = self.build_format(&s.value(), s.span()); + code = Some(formatted_str); } - _ => throw_span_err!( - info.span.unwrap(), - "wrong field type for suggestion", - |diag| { - diag.help("#[suggestion(...)] should be applied to fields of type Span or (Span, Applicability)") - } + other => throw_span_err!( + span, + &format!( + "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute", + name, other + ) ), - })()?; - // Now read the key-value pairs. - let mut msg = None; - let mut code = None; - - for arg in list.nested.iter() { - if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg - { - if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } = - arg_name_value - { - let name = arg_name_value - .path - .segments - .last() - .unwrap() - .ident - .to_string(); - let name = name.as_str(); - let formatted_str = self.build_format(&s.value(), arg.span()); - match name { - "message" => { - msg = Some(formatted_str); - } - "code" => { - code = Some(formatted_str); - } - other => throw_span_err!( - arg.span().unwrap(), - &format!( - "`{}` is not a valid key for `#[suggestion(...)]`", - other - ) - ), - } - } - } + }, + syn::Meta::NameValue(..) => throw_span_err!( + span, + &format!( + "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute", + name, nested_name + ), + |diag| diag.help("value must be a string") + ), + syn::Meta::Path(..) => throw_span_err!( + span, + &format!( + "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute", + name, nested_name + ) + ), + syn::Meta::List(..) => throw_span_err!( + span, + &format!( + "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute", + name, nested_name + ) + ), + } + } + + let method = format_ident!("span_{}", name); + + let slug = self + .slug + .as_ref() + .map(|(slug, _)| slug.as_str()) + .unwrap_or_else(|| "missing-slug"); + let msg = msg.as_deref().unwrap_or("suggestion"); + let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) }; + let code = code.unwrap_or_else(|| quote! { String::new() }); + + Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); }) + } + } + } + + /// Reports an error if the field's type is not `Span`. + fn report_error_if_not_applied_to_span( + &self, + attr: &syn::Attribute, + info: FieldInfo<'_>, + ) -> Result<(), SessionDiagnosticDeriveError> { + if !type_matches_path(&info.ty, &["rustc_span", "Span"]) { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + let meta = attr.parse_meta()?; + + throw_span_err!( + attr.span().unwrap(), + &format!( + "the `#[{}{}]` attribute can only be applied to fields of type `Span`", + name, + match meta { + syn::Meta::Path(_) => "", + syn::Meta::NameValue(_) => " = ...", + syn::Meta::List(_) => "(...)", + } + ) + ); + } + + Ok(()) + } + + /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and + /// `fluent_attr_identifier`. + fn add_subdiagnostic( + &self, + field_binding: &proc_macro2::Ident, + kind: &str, + fluent_attr_identifier: &str, + ) -> proc_macro2::TokenStream { + let diag = &self.diag; + + let slug = + self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug"); + let fn_name = format_ident!("span_{}", kind); + quote! { + #diag.#fn_name( + *#field_binding, + rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier) + ); + } + } + + fn span_and_applicability_of_ty( + &self, + info: FieldInfo<'_>, + ) -> Result<(proc_macro2::TokenStream, proc_macro2::TokenStream), SessionDiagnosticDeriveError> + { + match &info.ty { + // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`. + ty @ syn::Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => { + let binding = &info.binding.binding; + Ok((quote!(*#binding), quote!(rustc_errors::Applicability::Unspecified))) + } + // If `ty` is `(Span, Applicability)` then return tokens accessing those. + syn::Type::Tuple(tup) => { + let mut span_idx = None; + let mut applicability_idx = None; + + for (idx, elem) in tup.elems.iter().enumerate() { + if type_matches_path(elem, &["rustc_span", "Span"]) { + if span_idx.is_none() { + span_idx = Some(syn::Index::from(idx)); + } else { + throw_span_err!( + info.span.unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one `Span`" + ); } - let msg = if let Some(msg) = msg { - quote!(#msg.as_str()) + } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) { + if applicability_idx.is_none() { + applicability_idx = Some(syn::Index::from(idx)); } else { throw_span_err!( - list.span().unwrap(), - "missing suggestion message", - |diag| { - diag.help("provide a suggestion message using #[suggestion(message = \"...\")]") - } + info.span.unwrap(), + "type of field annotated with `#[suggestion(...)]` contains more than one Applicability" ); - }; - let code = code.unwrap_or_else(|| quote! { String::new() }); - // Now build it out: - let suggestion_method = format_ident!("span_{}", suggestion_kind); - quote! { - #diag.#suggestion_method(#span, #msg, #code, #applicability); } } - other => throw_span_err!( - list.span().unwrap(), - &format!("invalid annotation list `#[{}(...)]`", other) - ), } + + if let Some(span_idx) = span_idx { + let binding = &info.binding.binding; + let span = quote!(#binding.#span_idx); + let applicability = applicability_idx + .map(|applicability_idx| quote!(#binding.#applicability_idx)) + .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); + + return Ok((span, applicability)); + } + + throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| { + diag.help("`#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`") + }); } - _ => panic!("unhandled meta kind"), - }) + // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error. + _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| { + diag.help("`#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`") + }), + } } /// In the strings in the attributes supplied to this macro, we want callers to be able to - /// reference fields in the format string. Take this, for example: + /// reference fields in the format string. For example: + /// /// ```ignore (not-usage-example) /// struct Point { /// #[error = "Expected a point greater than ({x}, {y})"] @@ -568,12 +856,15 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> { /// y: i32, /// } /// ``` - /// We want to automatically pick up that {x} refers `self.x` and {y} refers to `self.y`, then - /// generate this call to format!: + /// + /// We want to automatically pick up that `{x}` refers `self.x` and `{y}` refers to `self.y`, + /// then generate this call to `format!`: + /// /// ```ignore (not-usage-example) /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y) /// ``` - /// This function builds the entire call to format!. + /// + /// This function builds the entire call to `format!`. fn build_format(&self, input: &str, span: proc_macro2::Span) -> proc_macro2::TokenStream { // This set is used later to generate the final format string. To keep builds reproducible, // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead @@ -646,7 +937,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> { } } -/// If `ty` is an Option, returns Some(inner type). Else, returns None. +/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`. fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> { if type_matches_path(ty, &["std", "option", "Option"]) { if let syn::Type::Path(ty_path) = ty { diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 002d25ab770..e55b0454eef 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -3,7 +3,7 @@ use std::cmp; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::{ - Diagnostic, DiagnosticBuilder, DiagnosticId, EmissionGuarantee, ErrorGuaranteed, + Diagnostic, DiagnosticBuilder, DiagnosticId, EmissionGuarantee, ErrorGuaranteed, MultiSpan, }; use rustc_hir::HirId; use rustc_index::vec::IndexVec; @@ -14,7 +14,7 @@ use rustc_session::lint::{ }; use rustc_session::Session; use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan}; +use rustc_span::source_map::{DesugaringKind, ExpnKind}; use rustc_span::{symbol, Span, Symbol, DUMMY_SP}; /// How a lint level was set. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ac9f04ee055..6ca8f8b1309 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -34,7 +34,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; use rustc_data_structures::vec_map::VecMap; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; @@ -54,7 +54,7 @@ use rustc_session::lint::{Level, Lint}; use rustc_session::Limit; use rustc_session::Session; use rustc_span::def_id::{DefPathHash, StableCrateId}; -use rustc_span::source_map::{MultiSpan, SourceMap}; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Layout, LayoutS, TargetDataLayout, VariantIdx}; diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index ee4ba494100..49d0ce52052 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -8,12 +8,18 @@ use crate::ty::{ }; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; use rustc_span::Span; +impl<'tcx> IntoDiagnosticArg for Ty<'tcx> { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + format!("{}", self).into_diagnostic_arg() + } +} + impl<'tcx> Ty<'tcx> { /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive. pub fn is_primitive_ty(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 3aad189b222..d93c9a79c65 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -3,11 +3,11 @@ use crate::ty::diagnostics::suggest_constraining_type_param; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; -use rustc_errors::{pluralize, Diagnostic}; +use rustc_errors::{pluralize, Diagnostic, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{BytePos, MultiSpan, Span}; +use rustc_span::{BytePos, Span}; use rustc_target::spec::abi; use std::borrow::Cow; diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index 791d5060fe5..fd7045d6a03 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -1,7 +1,8 @@ // These functions are used by macro expansion for bug! and span_bug! use crate::ty::{tls, TyCtxt}; -use rustc_span::{MultiSpan, Span}; +use rustc_errors::MultiSpan; +use rustc_span::Span; use std::fmt; use std::panic::{panic_any, Location}; diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 8a3a46c1190..44caa2ac076 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -8,7 +8,7 @@ use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, + ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::*; @@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span}; +use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 3ac226114cd..ed264045170 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -18,10 +18,12 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, ErrorGuaranteed}; -use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult}; +use rustc_errors::{ + Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult, +}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP}; +use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; use std::ops::{Deref, DerefMut}; use std::mem::take; @@ -273,12 +275,12 @@ impl<'a> Parser<'a> { pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, - m: &str, + m: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { self.sess.span_diagnostic.struct_span_err(sp, m) } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! { + pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<DiagnosticMessage>) -> ! { self.sess.span_diagnostic.span_bug(sp, m) } @@ -584,16 +586,22 @@ impl<'a> Parser<'a> { // field: value, // } } err.delay_as_bug(); - self.struct_span_err(expr.span, "struct literal body without path") - .multipart_suggestion( - "you might have forgotten to add the struct literal inside the block", - vec![ - (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()), - (expr.span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MaybeIncorrect, - ) - .emit(); + self.struct_span_err( + expr.span, + DiagnosticMessage::fluent("parser-struct-literal-body-without-path"), + ) + .multipart_suggestion( + DiagnosticMessage::fluent_attr( + "parser-struct-literal-body-without-path", + "suggestion", + ), + vec![ + (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MaybeIncorrect, + ) + .emit(); self.restore_snapshot(snapshot); let mut tail = self.mk_block( vec![self.mk_stmt_err(expr.span)], diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7c407cbe465..7978a1a7f5f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1703,7 +1703,7 @@ impl<'a> Parser<'a> { if matches!(expr.kind, ExprKind::Err) { let mut err = self .diagnostic() - .struct_span_err(self.token.span, &"invalid interpolated expression"); + .struct_span_err(self.token.span, "invalid interpolated expression"); err.downgrade_to_delayed_bug(); return err; } @@ -1820,7 +1820,7 @@ impl<'a> Parser<'a> { } else if let Some(fixed) = fix_base_capitalisation(suf) { let msg = "invalid base prefix for number literal"; - self.struct_span_err(span, &msg) + self.struct_span_err(span, msg) .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") .span_suggestion( span, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e55bdb0e553..5bf6f22b5d0 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1010,7 +1010,8 @@ impl<'a> Parser<'a> { let current_qual_sp = self.prev_token.span; let current_qual_sp = current_qual_sp.to(sp_start); if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) { - if err.message() == "expected `{`, found keyword `unsafe`" { + // FIXME(davidtwco): avoid depending on the error message text + if err.message[0].0.expect_str() == "expected `{`, found keyword `unsafe`" { let invalid_qual_sp = self.token.uninterpolated_span(); let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap(); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index b242c1e050d..792f9d9ccce 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -33,10 +33,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_errors::{ - struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, + struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan, }; use rustc_session::parse::ParseSess; -use rustc_span::source_map::{MultiSpan, Span, DUMMY_SP}; +use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use tracing::debug; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ad87481356..1f12f99efb3 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -6,7 +6,7 @@ use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability}; +use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; use std::collections::hash_map::Entry; pub(crate) fn target_from_impl_item<'tcx>( diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index bd4e1ddd25c..700290d67a4 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -620,8 +620,8 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D } else { sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd)) - .note(&"Please follow the instructions below to create a bug report with the provided information") - .note(&"See <https://github.com/rust-lang/rust/issues/84970> for more information") + .note("Please follow the instructions below to create a bug report with the provided information") + .note("See <https://github.com/rust-lang/rust/issues/84970> for more information") .emit(); panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 601f2d96ff5..6503b97a1d3 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -32,10 +32,10 @@ use rustc_ast::node_id::NodeMap; use rustc_ast::visit::{self, Visitor}; use rustc_ast_lowering::ResolverAstLowering; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::pluralize; +use rustc_errors::{pluralize, MultiSpan}; use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS}; use rustc_session::lint::BuiltinLintDiagnostics; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; struct UnusedImport<'a> { use_tree: &'a ast::UseTree, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6c7d4afea67..609dbd1fe1b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -4,7 +4,7 @@ use rustc_ast::{self as ast, Path}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; @@ -18,7 +18,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, MultiSpan, Span}; +use rustc_span::{BytePos, Span}; use tracing::debug; use crate::imports::{Import, ImportKind, ImportResolver}; @@ -1341,7 +1341,7 @@ impl<'a> Resolver<'a> { let def_span = self.session.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); if !first && binding.vis.is_public() { - note_span.push_span_label(def_span, "consider importing it directly".into()); + note_span.push_span_label(def_span, "consider importing it directly"); } err.span_note(note_span, &msg); } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 253b604bd23..02abdbaa983 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -12,7 +12,7 @@ use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBindin use rustc_ast::NodeId; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; -use rustc_errors::{pluralize, struct_span_err, Applicability}; +use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_hir::def::{self, PartialRes}; use rustc_hir::def_id::DefId; use rustc_middle::metadata::ModChild; @@ -23,7 +23,7 @@ use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::hygiene::LocalExpnId; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; use tracing::*; @@ -739,7 +739,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() { for message in note { - diag.note(&message); + diag.note(message); } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index b05ec654997..1e943f0e44a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -14,6 +14,7 @@ use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; @@ -25,7 +26,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{BytePos, Span, DUMMY_SP}; use std::iter; use std::ops::Deref; @@ -1106,7 +1107,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .collect(); if non_visible_spans.len() > 0 { - let mut m: rustc_span::MultiSpan = non_visible_spans.clone().into(); + let mut m: MultiSpan = non_visible_spans.clone().into(); non_visible_spans .into_iter() .for_each(|s| m.push_span_label(s, "private field".to_string())); @@ -1139,7 +1140,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } err.span_suggestion( span, - &"use this syntax instead", + "use this syntax instead", path_str.to_string(), Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 42bf6ff2a98..d5053034ed8 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -431,6 +431,20 @@ where } } +impl<'a, S: Encoder> Encodable<S> for Cow<'a, str> { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + let val: &str = self; + val.encode(s) + } +} + +impl<'a, D: Decoder> Decodable<D> for Cow<'a, str> { + fn decode(d: &mut D) -> Cow<'static, str> { + let v: String = Decodable::decode(d); + Cow::Owned(v) + } +} + impl<S: Encoder, T: Encodable<S>> Encodable<S> for Option<T> { fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_option(|s| match *self { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4182a5d0711..5a447aa6237 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2856,6 +2856,7 @@ crate mod dep_tracking { use crate::lint; use crate::options::WasiExecModel; use crate::utils::{NativeLib, NativeLibKind}; + use rustc_errors::LanguageIdentifier; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_span::RealFileName; @@ -2948,6 +2949,7 @@ crate mod dep_tracking { LocationDetail, BranchProtection, OomStrategy, + LanguageIdentifier, ); impl<T1, T2> DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 463c5c9d8b5..4994f8eaeb2 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -4,6 +4,7 @@ use crate::early_error; use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLib; +use rustc_errors::LanguageIdentifier; use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet}; use rustc_target::spec::{ RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, @@ -365,6 +366,7 @@ mod desc { pub const parse_string: &str = "a string"; pub const parse_opt_string: &str = parse_string; pub const parse_string_push: &str = parse_string; + pub const parse_opt_langid: &str = "a language identifier"; pub const parse_opt_pathbuf: &str = "a path"; pub const parse_list: &str = "a space-separated list of strings"; pub const parse_opt_comma_list: &str = "a comma-separated list of strings"; @@ -487,6 +489,17 @@ mod parse { } } + /// Parse an optional language identifier, e.g. `en-US` or `zh-CN`. + crate fn parse_opt_langid(slot: &mut Option<LanguageIdentifier>, v: Option<&str>) -> bool { + match v { + Some(s) => { + *slot = rustc_errors::LanguageIdentifier::from_str(s).ok(); + true + } + None => false, + } + } + crate fn parse_opt_pathbuf(slot: &mut Option<PathBuf>, v: Option<&str>) -> bool { match v { Some(s) => { @@ -1462,6 +1475,15 @@ options! { "the directory the intermediate files are written to"), terminal_width: Option<usize> = (None, parse_opt_number, [UNTRACKED], "set the current terminal width"), + // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved + // alongside query results and changes to translation options can affect diagnostics - so + // translation options should be tracked. + translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED], + "language identifier for diagnostic output"), + translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], + "additional fluent translation to preferentially use (for testing translation)"), + translate_directionality_markers: bool = (false, parse_bool, [TRACKED], + "emit directionality isolation markers in translated diagnostics"), tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 6f0c4761841..0b9c27c2cd6 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -7,12 +7,15 @@ use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; -use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{ + error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, + ErrorGuaranteed, MultiSpan, +}; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{MultiSpan, Span, Symbol}; +use rustc_span::{Span, Symbol}; use std::str; @@ -171,8 +174,17 @@ pub struct ParseSess { impl ParseSess { /// Used for testing. pub fn new(file_path_mapping: FilePathMapping) -> Self { + let fallback_bundle = + fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let sm = Lrc::new(SourceMap::new(file_path_mapping)); - let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, None, Some(sm.clone())); + let handler = Handler::with_tty_emitter( + ColorConfig::Auto, + true, + None, + Some(sm.clone()), + None, + fallback_bundle, + ); ParseSess::with_span_handler(handler, sm) } @@ -201,8 +213,11 @@ impl ParseSess { } pub fn with_silent_emitter(fatal_note: Option<String>) -> Self { + let fallback_bundle = + fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fatal_handler = Handler::with_tty_emitter(ColorConfig::Auto, false, None, None); + let fatal_handler = + Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, None, fallback_bundle); let handler = Handler::with_emitter( false, None, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index eed0f1e09ff..9881046ddfa 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -19,11 +19,14 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; -use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed}; +use rustc_errors::{ + fallback_fluent_bundle, fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, + EmissionGuarantee, ErrorGuaranteed, FluentBundle, MultiSpan, +}; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; -use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; +use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMap, Span}; use rustc_span::{sym, SourceFileHashAlgorithm, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; @@ -206,10 +209,10 @@ pub struct PerfStats { /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic]. -pub trait SessionDiagnostic<'a> { +pub trait SessionDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> { /// Write out as a diagnostic out of `sess`. #[must_use] - fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a, ErrorGuaranteed>; + fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a, T>; } impl Session { @@ -279,34 +282,34 @@ impl Session { pub fn struct_span_warn<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn(sp, msg) } pub fn struct_span_warn_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_code(sp, msg, code) } - pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn(msg) } pub fn struct_span_allow<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_allow(sp, msg) } - pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_allow(msg) } pub fn struct_expect( &self, - msg: &str, + msg: impl Into<DiagnosticMessage>, id: lint::LintExpectationId, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_expect(msg, id) @@ -314,81 +317,108 @@ impl Session { pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_span_err(sp, msg) } pub fn struct_span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_span_err_with_code(sp, msg, code) } // FIXME: This method should be removed (every error should have an associated error code). - pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + pub fn struct_err( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_err(msg) } pub fn struct_err_with_code( &self, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_err_with_code(msg, code) } + pub fn struct_warn_with_code( + &self, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, ()> { + self.diagnostic().struct_warn_with_code(msg, code) + } pub fn struct_span_fatal<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal(sp, msg) } pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal_with_code(sp, msg, code) } - pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, !> { + pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_fatal(msg) } - pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { + pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().span_fatal(sp, msg) } pub fn span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> ! { self.diagnostic().span_fatal_with_code(sp, msg, code) } - pub fn fatal(&self, msg: &str) -> ! { + pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().fatal(msg).raise() } - pub fn span_err_or_warn<S: Into<MultiSpan>>(&self, is_warning: bool, sp: S, msg: &str) { + pub fn span_err_or_warn<S: Into<MultiSpan>>( + &self, + is_warning: bool, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) { if is_warning { self.span_warn(sp, msg); } else { self.span_err(sp, msg); } } - pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ErrorGuaranteed { + pub fn span_err<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.diagnostic().span_err(sp, msg) } - pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { - self.diagnostic().span_err_with_code(sp, &msg, code) + pub fn span_err_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { + self.diagnostic().span_err_with_code(sp, msg, code) } - pub fn err(&self, msg: &str) -> ErrorGuaranteed { + pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.diagnostic().err(msg) } pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { err.into_diagnostic(self).emit() } + pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) { + warning.into_diagnostic(self).emit() + } #[inline] pub fn err_count(&self) -> usize { self.diagnostic().err_count() @@ -423,25 +453,34 @@ impl Session { Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) } } - pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) { self.diagnostic().span_warn(sp, msg) } - pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { + pub fn span_warn_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { self.diagnostic().span_warn_with_code(sp, msg, code) } - pub fn warn(&self, msg: &str) { + pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { self.diagnostic().warn(msg) } /// Delay a span_bug() call until abort_if_errors() #[track_caller] - pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ErrorGuaranteed { + pub fn delay_span_bug<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.diagnostic().delay_span_bug(sp, msg) } /// Used for code paths of expensive computations that should only take place when /// warnings or errors are emitted. If no messages are emitted ("good path"), then /// it's likely a bug. - pub fn delay_good_path_bug(&self, msg: &str) { + pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) { if self.opts.debugging_opts.print_type_sizes || self.opts.debugging_opts.query_dep_graph || self.opts.debugging_opts.dump_mir.is_some() @@ -455,13 +494,20 @@ impl Session { self.diagnostic().delay_good_path_bug(msg) } - pub fn note_without_error(&self, msg: &str) { + pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) { self.diagnostic().note_without_error(msg) } - pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + pub fn span_note_without_error<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) { self.diagnostic().span_note_without_error(sp, msg) } - pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_note_without_error( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_note_without_error(msg) } @@ -1033,6 +1079,8 @@ fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, source_map: Lrc<SourceMap>, + bundle: Option<Lrc<FluentBundle>>, + fallback_bundle: Lrc<FluentBundle>, emitter_dest: Option<Box<dyn Write + Send>>, ) -> Box<dyn Emitter + sync::Send> { let macro_backtrace = sopts.debugging_opts.macro_backtrace; @@ -1041,14 +1089,21 @@ fn default_emitter( let (short, color_config) = kind.unzip(); if let HumanReadableErrorType::AnnotateSnippet(_) = kind { - let emitter = - AnnotateSnippetEmitterWriter::new(Some(source_map), short, macro_backtrace); + let emitter = AnnotateSnippetEmitterWriter::new( + Some(source_map), + bundle, + fallback_bundle, + short, + macro_backtrace, + ); Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) } else { let emitter = match dst { None => EmitterWriter::stderr( color_config, Some(source_map), + bundle, + fallback_bundle, short, sopts.debugging_opts.teach, sopts.debugging_opts.terminal_width, @@ -1057,6 +1112,8 @@ fn default_emitter( Some(dst) => EmitterWriter::new( dst, Some(source_map), + bundle, + fallback_bundle, short, false, // no teach messages when writing to a buffer false, // no colors when writing to a buffer @@ -1071,6 +1128,8 @@ fn default_emitter( JsonEmitter::stderr( Some(registry), source_map, + bundle, + fallback_bundle, pretty, json_rendered, sopts.debugging_opts.terminal_width, @@ -1083,6 +1142,8 @@ fn default_emitter( dst, Some(registry), source_map, + bundle, + fallback_bundle, pretty, json_rendered, sopts.debugging_opts.terminal_width, @@ -1152,7 +1213,19 @@ pub fn build_session( sopts.file_path_mapping(), hash_kind, )); - let emitter = default_emitter(&sopts, registry, source_map.clone(), write_dest); + + let bundle = fluent_bundle( + &sysroot, + sopts.debugging_opts.translate_lang.clone(), + sopts.debugging_opts.translate_additional_ftl.as_deref(), + sopts.debugging_opts.translate_directionality_markers, + ) + .expect("failed to load fluent bundle"); + let fallback_bundle = + fallback_fluent_bundle(sopts.debugging_opts.translate_directionality_markers) + .expect("failed to load fallback fluent bundle"); + let emitter = + default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest); let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( emitter, @@ -1385,13 +1458,24 @@ pub enum IncrCompSession { } fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler { + let fallback_bundle = + fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let emitter: Box<dyn Emitter + sync::Send> = match output { config::ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); - Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) + Box::new(EmitterWriter::stderr( + color_config, + None, + None, + fallback_bundle, + short, + false, + None, + false, + )) } config::ErrorOutputType::Json { pretty, json_rendered } => { - Box::new(JsonEmitter::basic(pretty, json_rendered, None, false)) + Box::new(JsonEmitter::basic(pretty, json_rendered, None, fallback_bundle, None, false)) } }; rustc_errors::Handler::with_emitter(true, None, emitter) diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 6d7377927c2..5232c8d7006 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -520,20 +520,6 @@ impl Ord for Span { } } -/// A collection of `Span`s. -/// -/// Spans have two orthogonal attributes: -/// -/// - They can be *primary spans*. In this case they are the locus of -/// the error, and would be rendered with `^^^`. -/// - They can have a *label*. In this case, the label is written next -/// to the mark in the snippet when we render. -#[derive(Clone, Debug, Hash, PartialEq, Eq, Encodable, Decodable)] -pub struct MultiSpan { - primary_spans: Vec<Span>, - span_labels: Vec<(Span, String)>, -} - impl Span { #[inline] pub fn lo(self) -> BytePos { @@ -949,20 +935,6 @@ impl Span { } } -/// A span together with some additional data. -#[derive(Clone, Debug)] -pub struct SpanLabel { - /// The span we are going to include in the final snippet. - pub span: Span, - - /// Is this a primary span? This is the "locus" of the message, - /// and is indicated with a `^^^^` underline, versus `----`. - pub is_primary: bool, - - /// What label should we attach to this span (if any)? - pub label: Option<String>, -} - impl Default for Span { fn default() -> Self { DUMMY_SP @@ -1035,115 +1007,6 @@ impl fmt::Debug for SpanData { } } -impl MultiSpan { - #[inline] - pub fn new() -> MultiSpan { - MultiSpan { primary_spans: vec![], span_labels: vec![] } - } - - pub fn from_span(primary_span: Span) -> MultiSpan { - MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] } - } - - pub fn from_spans(mut vec: Vec<Span>) -> MultiSpan { - vec.sort(); - MultiSpan { primary_spans: vec, span_labels: vec![] } - } - - pub fn push_span_label(&mut self, span: Span, label: String) { - self.span_labels.push((span, label)); - } - - /// Selects the first primary span (if any). - pub fn primary_span(&self) -> Option<Span> { - self.primary_spans.first().cloned() - } - - /// Returns all primary spans. - pub fn primary_spans(&self) -> &[Span] { - &self.primary_spans - } - - /// Returns `true` if any of the primary spans are displayable. - pub fn has_primary_spans(&self) -> bool { - self.primary_spans.iter().any(|sp| !sp.is_dummy()) - } - - /// Returns `true` if this contains only a dummy primary span with any hygienic context. - pub fn is_dummy(&self) -> bool { - let mut is_dummy = true; - for span in &self.primary_spans { - if !span.is_dummy() { - is_dummy = false; - } - } - is_dummy - } - - /// Replaces all occurrences of one Span with another. Used to move `Span`s in areas that don't - /// display well (like std macros). Returns whether replacements occurred. - pub fn replace(&mut self, before: Span, after: Span) -> bool { - let mut replacements_occurred = false; - for primary_span in &mut self.primary_spans { - if *primary_span == before { - *primary_span = after; - replacements_occurred = true; - } - } - for span_label in &mut self.span_labels { - if span_label.0 == before { - span_label.0 = after; - replacements_occurred = true; - } - } - replacements_occurred - } - - /// Returns the strings to highlight. We always ensure that there - /// is an entry for each of the primary spans -- for each primary - /// span `P`, if there is at least one label with span `P`, we return - /// those labels (marked as primary). But otherwise we return - /// `SpanLabel` instances with empty labels. - pub fn span_labels(&self) -> Vec<SpanLabel> { - let is_primary = |span| self.primary_spans.contains(&span); - - let mut span_labels = self - .span_labels - .iter() - .map(|&(span, ref label)| SpanLabel { - span, - is_primary: is_primary(span), - label: Some(label.clone()), - }) - .collect::<Vec<_>>(); - - for &span in &self.primary_spans { - if !span_labels.iter().any(|sl| sl.span == span) { - span_labels.push(SpanLabel { span, is_primary: true, label: None }); - } - } - - span_labels - } - - /// Returns `true` if any of the span labels is displayable. - pub fn has_span_labels(&self) -> bool { - self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) - } -} - -impl From<Span> for MultiSpan { - fn from(span: Span) -> MultiSpan { - MultiSpan::from_span(span) - } -} - -impl From<Vec<Span>> for MultiSpan { - fn from(spans: Vec<Span>) -> MultiSpan { - MultiSpan::from_spans(spans) - } -} - /// Identifies an offset of a multi-byte character in a `SourceFile`. #[derive(Copy, Clone, Encodable, Decodable, Eq, PartialEq, Debug)] pub struct MultiByteChar { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 27748eef8f2..ff3488b7b76 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -15,7 +15,7 @@ use crate::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - Style, + MultiSpan, Style, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -33,7 +33,7 @@ use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, }; use rustc_span::symbol::{kw, sym}; -use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; use std::iter; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index c5324bf85a7..8dac56120e6 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, Style, + ErrorGuaranteed, MultiSpan, Style, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -27,7 +27,7 @@ use rustc_middle::ty::{TypeAndMut, TypeckResults}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span, DUMMY_SP}; +use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::fmt; @@ -597,7 +597,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { Some(format!("{}", name)) } _ => { - err.note(&msg); + err.note(msg); None } } @@ -780,7 +780,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if has_custom_message { err.note(&msg); } else { - err.message = vec![(msg, Style::NoStyle)]; + err.message = + vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; } if snippet.starts_with('&') { // This is already a literal borrow and the obligation is failing @@ -2480,7 +2481,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .opt_associated_item(trait_item_def_id) .and_then(|i| self.tcx.opt_item_name(i.container.id())) { - assoc_span.push_span_label(ident.span, "in this trait".into()); + assoc_span.push_span_label(ident.span, "in this trait"); } err.span_note(assoc_span, &msg); } @@ -2505,7 +2506,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .opt_associated_item(trait_item_def_id) .and_then(|i| self.tcx.opt_item_name(i.container.id())) { - assoc_span.push_span_label(ident.span, "in this trait".into()); + assoc_span.push_span_label(ident.span, "in this trait"); } err.span_note(assoc_span, &msg); } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 54f7c68060f..84958136cac 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -14,7 +14,7 @@ use crate::infer::TyCtxtInferExt; use crate::traits::const_evaluatable::{self, AbstractConst}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; -use rustc_errors::FatalError; +use rustc_errors::{FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; use smallvec::SmallVec; use std::iter; @@ -169,10 +169,7 @@ fn lint_object_unsafe_trait( let node = tcx.hir().get_if_local(trait_def_id); let mut spans = MultiSpan::from_span(span); if let Some(hir::Node::Item(item)) = node { - spans.push_span_label( - item.ident.span, - "this trait cannot be made into an object...".into(), - ); + spans.push_span_label(item.ident.span, "this trait cannot be made into an object..."); spans.push_span_label(span, format!("...because {}", violation.error_msg())); } else { spans.push_span_label( diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index a07700aa9f5..a50301dbc87 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -6,7 +6,7 @@ use crate::astconv::{ use crate::errors::AssocTypeBindingNotAllowed; use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; -use rustc_errors::{struct_span_err, Applicability, Diagnostic}; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -16,7 +16,7 @@ use rustc_middle::ty::{ self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt, }; use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; -use rustc_span::{symbol::kw, MultiSpan, Span}; +use rustc_span::{symbol::kw, Span}; use smallvec::SmallVec; impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 79be8bde55a..26f7c267ed1 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -1,11 +1,11 @@ use crate::check::coercion::{AsCoercionSite, CoerceMany}; use crate::check::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable}; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 7cb478d7888..9ebafc26f61 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -4,7 +4,7 @@ use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_ use super::*; use rustc_attr as attr; -use rustc_errors::{Applicability, ErrorGuaranteed}; +use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; @@ -20,7 +20,7 @@ use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_span::symbol::sym; -use rustc_span::{self, MultiSpan, Span}; +use rustc_span::{self, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index bb3bfbb7dd1..d403d6e3f33 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -8,7 +8,7 @@ use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed}; +use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -30,7 +30,7 @@ use rustc_session::lint; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_span::{self, BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 2a1b7a5ab47..1cc1460750a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -11,7 +11,7 @@ use crate::check::{ use rustc_ast as ast; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, Diagnostic, DiagnosticId}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticId, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -21,7 +21,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, MultiSpan, Span}; +use rustc_span::{self, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression}; use crate::structured_errors::StructuredDiagnostic; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 68d555b3a65..1ccdbb0aa50 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -2,9 +2,9 @@ use super::FnCtxt; use crate::astconv::AstConv; use rustc_ast::util::parser::ExprPrecedence; -use rustc_span::{self, MultiSpan, Span}; +use rustc_span::{self, Span}; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 74f6f50d412..cd6b1115ed8 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -7,7 +7,7 @@ use crate::errors::{ }; use crate::require_same_types; -use rustc_errors::{pluralize, struct_span_err}; +use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::subst::Subst; @@ -43,7 +43,6 @@ fn equate_intrinsic_type<'tcx>( span, found, expected, - expected_pluralize: pluralize!(expected), descr, }); false diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index c2cb233f5b8..ecc29965937 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -5,6 +5,7 @@ use crate::check::FnCtxt; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, }; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -17,7 +18,7 @@ use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, MultiSpan, Span}; +use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index bf0bf2ed59b..19d52f430fc 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -103,7 +103,7 @@ pub use inherited::{Inherited, InheritedBuilder}; use crate::astconv::AstConv; use crate::check::gather_locals::GatherLocalsVisitor; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, struct_span_err, Applicability}; +use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -121,7 +121,7 @@ use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_span::{self, BytePos, Span}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 464a2cd9524..0baca9048b4 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -4,6 +4,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -18,7 +19,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{BytePos, MultiSpan, DUMMY_SP}; +use rustc_span::{BytePos, DUMMY_SP}; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use ty::VariantDef; diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 0b343a5f905..1118e967707 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -33,7 +33,7 @@ use super::FnCtxt; use crate::expr_use_visitor as euv; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; @@ -46,7 +46,7 @@ use rustc_middle::ty::{ }; use rustc_session::lint; use rustc_span::sym; -use rustc_span::{BytePos, MultiSpan, Pos, Span, Symbol}; +use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_data_structures::stable_map::FxHashMap; diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 47077779616..0b78aea9f05 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -3,188 +3,185 @@ use rustc_macros::SessionDiagnostic; use rustc_span::{symbol::Ident, Span, Symbol}; #[derive(SessionDiagnostic)] -#[error = "E0062"] +#[error(code = "E0062", slug = "typeck-field-multiply-specified-in-initializer")] pub struct FieldMultiplySpecifiedInInitializer { - #[message = "field `{ident}` specified more than once"] - #[label = "used more than once"] + #[primary_span] + #[label] pub span: Span, - #[label = "first use of `{ident}`"] + #[label = "previous-use-label"] pub prev_span: Span, pub ident: Ident, } #[derive(SessionDiagnostic)] -#[error = "E0092"] +#[error(code = "E0092", slug = "typeck-unrecognized-atomic-operation")] pub struct UnrecognizedAtomicOperation<'a> { - #[message = "unrecognized atomic operation function: `{op}`"] - #[label = "unrecognized atomic operation"] + #[primary_span] + #[label] pub span: Span, pub op: &'a str, } #[derive(SessionDiagnostic)] -#[error = "E0094"] +#[error(code = "E0094", slug = "typeck-wrong-number-of-generic-arguments-to-intrinsic")] pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { - #[message = "intrinsic has wrong number of {descr} \ - parameters: found {found}, expected {expected}"] - #[label = "expected {expected} {descr} parameter{expected_pluralize}"] + #[primary_span] + #[label] pub span: Span, pub found: usize, pub expected: usize, - pub expected_pluralize: &'a str, pub descr: &'a str, } #[derive(SessionDiagnostic)] -#[error = "E0093"] +#[error(code = "E0093", slug = "typeck-unrecognized-intrinsic-function")] pub struct UnrecognizedIntrinsicFunction { - #[message = "unrecognized intrinsic function: `{name}`"] - #[label = "unrecognized intrinsic"] + #[primary_span] + #[label] pub span: Span, pub name: Symbol, } #[derive(SessionDiagnostic)] -#[error = "E0195"] +#[error(code = "E0195", slug = "typeck-lifetimes-or-bounds-mismatch-on-trait")] pub struct LifetimesOrBoundsMismatchOnTrait { - #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"] - #[label = "lifetimes do not match {item_kind} in trait"] + #[primary_span] + #[label] pub span: Span, - #[label = "lifetimes in impl do not match this {item_kind} in trait"] + #[label = "generics-label"] pub generics_span: Option<Span>, pub item_kind: &'static str, pub ident: Ident, } #[derive(SessionDiagnostic)] -#[error = "E0120"] +#[error(code = "E0120", slug = "typeck-drop-impl-on-wrong-item")] pub struct DropImplOnWrongItem { - #[message = "the `Drop` trait may only be implemented for structs, enums, and unions"] - #[label = "must be a struct, enum, or union"] + #[primary_span] + #[label] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0124"] +#[error(code = "E0124", slug = "typeck-field-already-declared")] pub struct FieldAlreadyDeclared { pub field_name: Ident, - #[message = "field `{field_name}` is already declared"] - #[label = "field already declared"] + #[primary_span] + #[label] pub span: Span, - #[label = "`{field_name}` first declared here"] + #[label = "previous-decl-label"] pub prev_span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0184"] +#[error(code = "E0184", slug = "typeck-copy-impl-on-type-with-dtor")] pub struct CopyImplOnTypeWithDtor { - #[message = "the trait `Copy` may not be implemented for this type; the \ - type has a destructor"] - #[label = "Copy not allowed on types with destructors"] + #[primary_span] + #[label] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0203"] +#[error(code = "E0203", slug = "typeck-multiple-relaxed-default-bounds")] pub struct MultipleRelaxedDefaultBounds { - #[message = "type parameter has more than one relaxed default bound, only one is supported"] + #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0206"] +#[error(code = "E0206", slug = "typeck-copy-impl-on-non-adt")] pub struct CopyImplOnNonAdt { - #[message = "the trait `Copy` may not be implemented for this type"] - #[label = "type is not a structure or enumeration"] + #[primary_span] + #[label] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0224"] +#[error(code = "E0224", slug = "typeck-trait-object-declared-with-no-traits")] pub struct TraitObjectDeclaredWithNoTraits { - #[message = "at least one trait is required for an object type"] + #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0227"] +#[error(code = "E0227", slug = "typeck-ambiguous-lifetime-bound")] pub struct AmbiguousLifetimeBound { - #[message = "ambiguous lifetime bound, explicit lifetime bound required"] + #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0229"] +#[error(code = "E0229", slug = "typeck-assoc-type-binding-not-allowed")] pub struct AssocTypeBindingNotAllowed { - #[message = "associated type bindings are not allowed here"] - #[label = "associated type not allowed here"] + #[primary_span] + #[label] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0436"] +#[error(code = "E0436", slug = "typeck-functional-record-update-on-non-struct")] pub struct FunctionalRecordUpdateOnNonStruct { - #[message = "functional record update syntax requires a struct"] + #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0516"] +#[error(code = "E0516", slug = "typeck-typeof-reserved-keyword-used")] pub struct TypeofReservedKeywordUsed { - #[message = "`typeof` is a reserved keyword but unimplemented"] - #[label = "reserved keyword"] + #[primary_span] + #[label] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0572"] +#[error(code = "E0572", slug = "typeck-return-stmt-outside-of-fn-body")] pub struct ReturnStmtOutsideOfFnBody { - #[message = "return statement outside of function body"] + #[primary_span] pub span: Span, - #[label = "the return is part of this body..."] + #[label = "encl-body-label"] pub encl_body_span: Option<Span>, - #[label = "...not the enclosing function body"] + #[label = "encl-fn-label"] pub encl_fn_span: Option<Span>, } #[derive(SessionDiagnostic)] -#[error = "E0627"] +#[error(code = "E0627", slug = "typeck-yield-expr-outside-of-generator")] pub struct YieldExprOutsideOfGenerator { - #[message = "yield expression outside of generator literal"] + #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0639"] +#[error(code = "E0639", slug = "typeck-struct-expr-non-exhaustive")] pub struct StructExprNonExhaustive { - #[message = "cannot create non-exhaustive {what} using struct expression"] + #[primary_span] pub span: Span, pub what: &'static str, } #[derive(SessionDiagnostic)] -#[error = "E0699"] +#[error(code = "E0699", slug = "typeck-method-call-on-unknown-type")] pub struct MethodCallOnUnknownType { - #[message = "the type of this value must be known to call a method on a raw pointer on it"] + #[primary_span] pub span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0719"] +#[error(code = "E0719", slug = "typeck-value-of-associated-struct-already-specified")] pub struct ValueOfAssociatedStructAlreadySpecified { - #[message = "the value of the associated type `{item_name}` (from trait `{def_path}`) is already specified"] - #[label = "re-bound here"] + #[primary_span] + #[label] pub span: Span, - #[label = "`{item_name}` bound here first"] + #[label = "previous-bound-label"] pub prev_span: Span, pub item_name: Ident, pub def_path: String, } #[derive(SessionDiagnostic)] -#[error = "E0745"] +#[error(code = "E0745", slug = "typeck-address-of-temporary-taken")] pub struct AddressOfTemporaryTaken { - #[message = "cannot take address of a temporary"] - #[label = "temporary value"] + #[primary_span] + #[label] pub span: Span, } diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index a117ed68ff7..24b6639d7f2 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -1,13 +1,14 @@ use crate::structured_errors::StructuredDiagnostic; use rustc_errors::{ pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, + MultiSpan, }; use rustc_hir as hir; use rustc_middle::hir::map::fn_sig; use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath; use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::Session; -use rustc_span::{def_id::DefId, MultiSpan}; +use rustc_span::def_id::DefId; use GenericArgsInfo::*; diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index ad3800834b0..4bba2eb4507 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -15,6 +15,10 @@ fn main() { let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set"); let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set"); + // Detect whether or not we're a build script depending on whether --target + // is passed (a bit janky...) + let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str()); + use std::str::FromStr; let verbose = match env::var("RUSTC_VERBOSE") { @@ -26,10 +30,18 @@ fn main() { dylib_path.insert(0, PathBuf::from(libdir.clone())); let mut cmd = Command::new(rustdoc); - cmd.args(&args) - .arg("--sysroot") - .arg(&sysroot) - .env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); + + if target.is_some() { + // The stage0 compiler has a special sysroot distinct from what we + // actually downloaded, so we just always pass the `--sysroot` option, + // unless one is already set. + if !args.iter().any(|arg| arg == "--sysroot") { + cmd.arg("--sysroot").arg(&sysroot); + } + } + + cmd.args(&args); + cmd.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); // Force all crates compiled by this compiler to (a) be unstable and (b) // allow the `rustc_private` feature to link to other unstable crates diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 71b8f3c4553..0b6bdf47419 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -678,7 +678,7 @@ class RustBuild(object): # The latter one does not exist on NixOS when using tmpfs as root. try: with open("/etc/os-release", "r") as f: - if not any(line.strip() == "ID=nixos" for line in f): + if not any(l.strip() in ["ID=nixos", "ID='nixos'", 'ID="nixos"'] for l in f): return except FileNotFoundError: return diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index bd64e2b03ce..a32b9caa30f 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -143,6 +143,8 @@ crate fn new_handler( source_map: Option<Lrc<source_map::SourceMap>>, debugging_opts: &DebuggingOptions, ) -> rustc_errors::Handler { + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle"); let emitter: Box<dyn Emitter + sync::Send> = match error_format { ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); @@ -150,6 +152,8 @@ crate fn new_handler( EmitterWriter::stderr( color_config, source_map.map(|sm| sm as _), + None, + fallback_bundle, short, debugging_opts.teach, debugging_opts.terminal_width, @@ -166,6 +170,8 @@ crate fn new_handler( JsonEmitter::stderr( None, source_map, + None, + fallback_bundle, pretty, json_rendered, debugging_opts.terminal_width, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c4201e22212..63b744133a2 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -537,12 +537,31 @@ crate fn make_test( // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - supports_color = - EmitterWriter::stderr(ColorConfig::Auto, None, false, false, Some(80), false) - .supports_color(); - - let emitter = - EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) + .expect("failed to load fallback fluent bundle"); + supports_color = EmitterWriter::stderr( + ColorConfig::Auto, + None, + None, + fallback_bundle.clone(), + false, + false, + Some(80), + false, + ) + .supports_color(); + + let emitter = EmitterWriter::new( + box io::sink(), + None, + None, + fallback_bundle, + false, + false, + false, + None, + false, + ); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let handler = Handler::with_emitter(false, None, box emitter); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index f59222b780d..1d7a790bdb7 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -771,6 +771,7 @@ fn main_options(options: config::Options) -> MainResult { let externs = options.externs.clone(); let render_options = options.render_options.clone(); let scrape_examples_options = options.scrape_examples_options.clone(); + let document_private = options.render_options.document_private; let config = core::create_config(options); interface::create_compiler_and_run(config, |compiler| { @@ -791,7 +792,12 @@ fn main_options(options: config::Options) -> MainResult { let (resolver, resolver_caches) = { let (krate, resolver, _) = &*abort_on_err(queries.expansion(), sess).peek(); let resolver_caches = resolver.borrow_mut().access(|resolver| { - collect_intra_doc_links::early_resolve_intra_doc_links(resolver, krate, externs) + collect_intra_doc_links::early_resolve_intra_doc_links( + resolver, + krate, + externs, + document_private, + ) }); (resolver.clone(), resolver_caches) }; diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 8d9b3377a69..465dd523ff4 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -32,7 +32,9 @@ struct SyntaxChecker<'a, 'tcx> { impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) { let buffer = Lrc::new(Lock::new(Buffer::default())); - let emitter = BufferEmitter { buffer: Lrc::clone(&buffer) }; + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) + .expect("failed to load fallback fluent bundle"); + let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle }; let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let handler = Handler::with_emitter(false, None, Box::new(emitter)); @@ -171,12 +173,14 @@ struct Buffer { struct BufferEmitter { buffer: Lrc<Lock<Buffer>>, + fallback_bundle: Lrc<rustc_errors::FluentBundle>, } impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: &Diagnostic) { let mut buffer = self.buffer.borrow_mut(); - buffer.messages.push(format!("error from rustc: {}", diag.message[0].0)); + // FIXME(davidtwco): need to support translation here eventually + buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.expect_str())); if diag.is_error() { buffer.has_errors = true; } @@ -185,4 +189,12 @@ impl Emitter for BufferEmitter { fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } + + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + &self.fallback_bundle + } } diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 39900270ccb..44bf86b082a 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -22,6 +22,7 @@ crate fn early_resolve_intra_doc_links( resolver: &mut Resolver<'_>, krate: &ast::Crate, externs: Externs, + document_private_items: bool, ) -> ResolverCaches { let mut loader = IntraLinkCrateLoader { resolver, @@ -30,6 +31,7 @@ crate fn early_resolve_intra_doc_links( traits_in_scope: Default::default(), all_traits: Default::default(), all_trait_impls: Default::default(), + document_private_items, }; // Overridden `visit_item` below doesn't apply to the crate root, @@ -61,6 +63,7 @@ struct IntraLinkCrateLoader<'r, 'ra> { traits_in_scope: DefIdMap<Vec<TraitCandidate>>, all_traits: Vec<DefId>, all_trait_impls: Vec<DefId>, + document_private_items: bool, } impl IntraLinkCrateLoader<'_, '_> { @@ -167,7 +170,7 @@ impl IntraLinkCrateLoader<'_, '_> { } for child in self.resolver.module_children_or_reexports(module_id) { - if child.vis == Visibility::Public { + if child.vis == Visibility::Public || self.document_private_items { if let Some(def_id) = child.res.opt_def_id() { self.add_traits_in_parent_scope(def_id); } diff --git a/src/test/run-make/translation/Makefile b/src/test/run-make/translation/Makefile new file mode 100644 index 00000000000..22a3bf57ecf --- /dev/null +++ b/src/test/run-make/translation/Makefile @@ -0,0 +1,33 @@ +include ../../run-make-fulldeps/tools.mk + +# This test uses `ln -s` rather than copying to save testing time, but its +# usage doesn't work on Windows. +# ignore-windows + +SYSROOT:=$(shell $(RUSTC) --print sysroot) +FAKEROOT=$(TMPDIR)/fakeroot + +all: normal custom sysroot + +normal: basic-translation.rs + $(RUSTC) $< 2>&1 | grep "struct literal body without path" + +custom: basic-translation.rs basic-translation.ftl + $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/basic-translation.ftl 2>&1 | grep "this is a test message" + +# Make a local copy of the sysroot and add the custom locale to it. +sysroot: basic-translation.rs basic-translation.ftl + mkdir $(FAKEROOT) + ln -s $(SYSROOT)/* $(FAKEROOT) + rm -f $(FAKEROOT)/lib + mkdir $(FAKEROOT)/lib + ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib + rm -f $(FAKEROOT)/lib/rustlib + mkdir $(FAKEROOT)/lib/rustlib + ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib + rm -f $(FAKEROOT)/lib/rustlib/src + mkdir $(FAKEROOT)/lib/rustlib/src + ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src + mkdir -p $(FAKEROOT)/share/locale/zh-CN/ + ln -s $(CURDIR)/basic-translation.ftl $(FAKEROOT)/share/locale/zh-CN/basic-translation.ftl + $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | grep "this is a test message" diff --git a/src/test/run-make/translation/basic-translation.ftl b/src/test/run-make/translation/basic-translation.ftl new file mode 100644 index 00000000000..4681b879cda --- /dev/null +++ b/src/test/run-make/translation/basic-translation.ftl @@ -0,0 +1,2 @@ +parser-struct-literal-body-without-path = this is a test message + .suggestion = this is a test suggestion diff --git a/src/test/run-make/translation/basic-translation.rs b/src/test/run-make/translation/basic-translation.rs new file mode 100644 index 00000000000..b8f5bff3153 --- /dev/null +++ b/src/test/run-make/translation/basic-translation.rs @@ -0,0 +1,18 @@ +// Exact error being tested isn't relevant, it just needs to be known that it uses Fluent-backed +// diagnostics. + +struct Foo { + val: (), +} + +fn foo() -> Foo { + val: (), +} + +fn main() { + let x = foo(); + x.val == 42; + let x = { + val: (), + }; +} diff --git a/src/test/rustdoc/issue-95633.rs b/src/test/rustdoc/issue-95633.rs new file mode 100644 index 00000000000..a71d0a03731 --- /dev/null +++ b/src/test/rustdoc/issue-95633.rs @@ -0,0 +1,7 @@ +// compile-flags: --document-private-items + +// This ensures that no ICE is triggered when rustdoc is run on this code. + +mod stdlib { + pub (crate) use std::i8; +} diff --git a/src/test/ui-fulldeps/session-derive-errors.rs b/src/test/ui-fulldeps/session-derive-errors.rs index 140aaad3b38..adec548b390 100644 --- a/src/test/ui-fulldeps/session-derive-errors.rs +++ b/src/test/ui-fulldeps/session-derive-errors.rs @@ -11,8 +11,8 @@ #![crate_type = "lib"] extern crate rustc_span; -use rustc_span::Span; use rustc_span::symbol::Ident; +use rustc_span::Span; extern crate rustc_macros; use rustc_macros::SessionDiagnostic; @@ -26,12 +26,15 @@ use rustc_errors::Applicability; extern crate rustc_session; #[derive(SessionDiagnostic)] -#[message = "Hello, world!"] -#[error = "E0123"] +#[error(code = "E0123", slug = "hello-world")] struct Hello {} #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[warning(code = "E0123", slug = "hello-world")] +struct HelloWarn {} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] //~^ ERROR `#[derive(SessionDiagnostic)]` can only be used on structs enum SessionDiagnosticOnEnum { Foo, @@ -39,228 +42,387 @@ enum SessionDiagnosticOnEnum { } #[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] #[error = "E0123"] -#[label = "This is in the wrong place"] -//~^ ERROR `#[label = ...]` is not a valid SessionDiagnostic struct attribute -struct WrongPlace {} +//~^ ERROR `#[error = ...]` is not a valid `SessionDiagnostic` struct attribute +struct WrongStructAttrStyle {} #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[nonsense(code = "E0123", slug = "foo")] +//~^ ERROR `#[nonsense(...)]` is not a valid `SessionDiagnostic` struct attribute +//~^^ ERROR diagnostic kind not specified +//~^^^ ERROR cannot find attribute `nonsense` in this scope +struct InvalidStructAttr {} + +#[derive(SessionDiagnostic)] +#[error("E0123")] +//~^ ERROR `#[error("...")]` is not a valid `SessionDiagnostic` struct attribute +//~^^ ERROR `slug` not specified +struct InvalidLitNestedAttr {} + +#[derive(SessionDiagnostic)] +#[error(nonsense, code = "E0123", slug = "foo")] +//~^ ERROR `#[error(nonsense)]` is not a valid `SessionDiagnostic` struct attribute +struct InvalidNestedStructAttr {} + +#[derive(SessionDiagnostic)] +#[error(nonsense("foo"), code = "E0123", slug = "foo")] +//~^ ERROR `#[error(nonsense(...))]` is not a valid `SessionDiagnostic` struct attribute +struct InvalidNestedStructAttr1 {} + +#[derive(SessionDiagnostic)] +#[error(nonsense = "...", code = "E0123", slug = "foo")] +//~^ ERROR `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute +struct InvalidNestedStructAttr2 {} + +#[derive(SessionDiagnostic)] +#[error(nonsense = 4, code = "E0123", slug = "foo")] +//~^ ERROR `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute +struct InvalidNestedStructAttr3 {} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] struct WrongPlaceField { - #[suggestion = "this is the wrong kind of attribute"] -//~^ ERROR `#[suggestion = ...]` is not a valid SessionDiagnostic field attribute + #[suggestion = "bar"] + //~^ ERROR `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute sp: Span, } #[derive(SessionDiagnostic)] -#[message = "Hello, world!"] -#[error = "E0123"] -#[error = "E0456"] //~ ERROR `error` specified multiple times +#[error(code = "E0123", slug = "foo")] +#[error(code = "E0456", slug = "bar")] //~ ERROR `error` specified multiple times struct ErrorSpecifiedTwice {} #[derive(SessionDiagnostic)] -#[message = "Hello, world!"] -#[error = "E0123"] -#[lint = "some_useful_lint"] //~ ERROR `lint` specified when `error` was already specified -struct LintSpecifiedAfterError {} +#[error(code = "E0123", slug = "foo")] +#[warning(code = "E0293", slug = "bar")] +//~^ ERROR `warning` specified when `error` was already specified +struct WarnSpecifiedAfterError {} #[derive(SessionDiagnostic)] -#[message = "Some lint message"] -#[error = "E0123"] -struct LintButHasErrorCode {} +#[error(code = "E0456", code = "E0457", slug = "bar")] //~ ERROR `code` specified multiple times +struct CodeSpecifiedTwice {} #[derive(SessionDiagnostic)] -struct ErrorCodeNotProvided {} //~ ERROR `code` not specified +#[error(code = "E0456", slug = "foo", slug = "bar")] //~ ERROR `slug` specified multiple times +struct SlugSpecifiedTwice {} -// FIXME: Uncomment when emitting lints is supported. -/* #[derive(SessionDiagnostic)] -#[message = "Hello, world!"] -#[lint = "clashing_extern_declarations"] -#[lint = "improper_ctypes"] // FIXME: ERROR `lint` specified multiple times -struct LintSpecifiedTwice {} +struct KindNotProvided {} //~ ERROR diagnostic kind not specified #[derive(SessionDiagnostic)] -#[lint = "Some lint message"] -#[message = "Some error message"] -#[error = "E0123"] // ERROR `error` specified when `lint` was already specified -struct ErrorSpecifiedAfterLint {} -*/ +#[error(code = "E0456")] //~ ERROR `slug` not specified +struct SlugNotProvided {} #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(slug = "foo")] +struct CodeNotProvided {} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct MessageWrongType { + #[primary_span] + //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` + foo: String, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct InvalidPathFieldAttr { + #[nonsense] + //~^ ERROR `#[nonsense]` is not a valid `SessionDiagnostic` field attribute + //~^^ ERROR cannot find attribute `nonsense` in this scope + foo: String, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] struct ErrorWithField { name: String, - #[message = "This error has a field, and references {name}"] - span: Span + #[label = "bar"] + span: Span, } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct ErrorWithMessageAppliedToField { - #[message = "this message is applied to a String field"] - //~^ ERROR the `#[message = "..."]` attribute can only be applied to fields of type Span + #[label = "bar"] + //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span` name: String, } #[derive(SessionDiagnostic)] -#[error = "E0123"] -#[message = "This error has a field, and references {name}"] -//~^ ERROR `name` doesn't refer to a field on this type +#[error(code = "E0123", slug = "foo")] struct ErrorWithNonexistentField { - span: Span + #[suggestion(message = "bar", code = "{name}")] + //~^ ERROR `name` doesn't refer to a field on this type + suggestion: (Span, Applicability), } #[derive(SessionDiagnostic)] -#[error = "E0123"] -#[message = "This is missing a closing brace: {name"] //~^ ERROR invalid format string: expected `'}'` +#[error(code = "E0123", slug = "foo")] struct ErrorMissingClosingBrace { + #[suggestion(message = "bar", code = "{name")] + suggestion: (Span, Applicability), name: String, - span: Span + val: usize, } #[derive(SessionDiagnostic)] -#[error = "E0123"] -#[message = "This is missing an opening brace: name}"] //~^ ERROR invalid format string: unmatched `}` +#[error(code = "E0123", slug = "foo")] struct ErrorMissingOpeningBrace { + #[suggestion(message = "bar", code = "name}")] + suggestion: (Span, Applicability), name: String, - span: Span + val: usize, } #[derive(SessionDiagnostic)] -#[error = "E0123"] -#[message = "Something something"] +#[error(code = "E0123", slug = "foo")] struct LabelOnSpan { - #[label = "See here"] - sp: Span + #[label = "bar"] + sp: Span, } #[derive(SessionDiagnostic)] -#[error = "E0123"] -#[message = "Something something"] +#[error(code = "E0123", slug = "foo")] struct LabelOnNonSpan { - #[label = "See here"] - //~^ ERROR The `#[label = ...]` attribute can only be applied to fields of type Span + #[label = "bar"] + //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span` id: u32, } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct Suggest { - #[suggestion(message = "This is a suggestion", code = "This is the suggested code")] - #[suggestion_short(message = "This is a suggestion", code = "This is the suggested code")] - #[suggestion_hidden(message = "This is a suggestion", code = "This is the suggested code")] - #[suggestion_verbose(message = "This is a suggestion", code = "This is the suggested code")] + #[suggestion(message = "bar", code = "This is the suggested code")] + #[suggestion_short(message = "qux", code = "This is the suggested code")] + #[suggestion_hidden(message = "foobar", code = "This is the suggested code")] + #[suggestion_verbose(message = "fooqux", code = "This is the suggested code")] suggestion: (Span, Applicability), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithoutCode { - #[suggestion(message = "This is a suggestion")] + #[suggestion(message = "bar")] suggestion: (Span, Applicability), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithBadKey { - #[suggestion(nonsense = "This is nonsense")] - //~^ ERROR `nonsense` is not a valid key for `#[suggestion(...)]` + #[suggestion(nonsense = "bar")] + //~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute suggestion: (Span, Applicability), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithShorthandMsg { - #[suggestion(msg = "This is a suggestion")] - //~^ ERROR `msg` is not a valid key for `#[suggestion(...)]` + #[suggestion(msg = "bar")] + //~^ ERROR `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute suggestion: (Span, Applicability), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithoutMsg { - #[suggestion(code = "This is suggested code")] - //~^ ERROR missing suggestion message + #[suggestion(code = "bar")] suggestion: (Span, Applicability), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithTypesSwapped { - #[suggestion(message = "This is a message", code = "This is suggested code")] + #[suggestion(message = "bar", code = "This is suggested code")] suggestion: (Applicability, Span), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithWrongTypeApplicabilityOnly { - #[suggestion(message = "This is a message", code = "This is suggested code")] + #[suggestion(message = "bar", code = "This is suggested code")] //~^ ERROR wrong field type for suggestion suggestion: Applicability, } #[derive(SessionDiagnostic)] -#[error = "E0123"] -struct SuggestWithSpanOnly{ - #[suggestion(message = "This is a message", code = "This is suggested code")] +#[error(code = "E0123", slug = "foo")] +struct SuggestWithSpanOnly { + #[suggestion(message = "bar", code = "This is suggested code")] suggestion: Span, } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithDuplicateSpanAndApplicability { - #[suggestion(message = "This is a message", code = "This is suggested code")] - //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one Span + #[suggestion(message = "bar", code = "This is suggested code")] + //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one `Span` suggestion: (Span, Span, Applicability), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct SuggestWithDuplicateApplicabilityAndSpan { - #[suggestion(message = "This is a message", code = "This is suggested code")] + #[suggestion(message = "bar", code = "This is suggested code")] //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one suggestion: (Applicability, Applicability, Span), } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct WrongKindOfAnnotation { - #[label("wrong kind of annotation for label")] - //~^ ERROR invalid annotation list `#[label(...)]` + #[label("bar")] + //~^ ERROR `#[label(...)]` is not a valid `SessionDiagnostic` field attribute z: Span, } #[derive(SessionDiagnostic)] -#[error = "E0123"] -#[message = "Something something else"] +#[error(code = "E0123", slug = "foo")] struct OptionsInErrors { - #[label = "Label message"] + #[label = "bar"] label: Option<Span>, - #[suggestion(message = "suggestion message")] + #[suggestion(message = "bar")] opt_sugg: Option<(Span, Applicability)>, } #[derive(SessionDiagnostic)] -#[error = "E0456"] +#[error(code = "E0456", slug = "foo")] struct MoveOutOfBorrowError<'tcx> { name: Ident, ty: Ty<'tcx>, - #[message = "cannot move {ty} out of borrow"] - #[label = "cannot move out of borrow"] + #[primary_span] + #[label = "bar"] span: Span, - #[label = "`{ty}` first borrowed here"] + #[label = "qux"] other_span: Span, - #[suggestion(message = "consider cloning here", code = "{name}.clone()")] + #[suggestion(message = "bar", code = "{name}.clone()")] opt_sugg: Option<(Span, Applicability)>, } #[derive(SessionDiagnostic)] -#[error = "E0123"] +#[error(code = "E0123", slug = "foo")] struct ErrorWithLifetime<'a> { - #[message = "Some message that references {name}"] + #[label = "bar"] span: Span, name: &'a str, } + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ErrorWithDefaultLabelAttr<'a> { + #[label] + span: Span, + name: &'a str, +} + +#[derive(SessionDiagnostic)] +//~^ ERROR no method named `into_diagnostic_arg` found for struct `Hello` in the current scope +#[error(code = "E0123", slug = "foo")] +struct ArgFieldWithoutSkip { + #[primary_span] + span: Span, + other: Hello, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ArgFieldWithSkip { + #[primary_span] + span: Span, + // `Hello` does not implement `IntoDiagnosticArg` so this would result in an error if + // not for `#[skip_arg]`. + #[skip_arg] + other: Hello, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ErrorWithSpannedNote { + #[note] + span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ErrorWithSpannedNoteCustom { + #[note = "bar"] + span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +#[note] +struct ErrorWithNote { + val: String, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +#[note = "bar"] +struct ErrorWithNoteCustom { + val: String, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ErrorWithSpannedHelp { + #[help] + span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ErrorWithSpannedHelpCustom { + #[help = "bar"] + span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +#[help] +struct ErrorWithHelp { + val: String, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +#[help = "bar"] +struct ErrorWithHelpCustom { + val: String, +} + +#[derive(SessionDiagnostic)] +#[help] +//~^ ERROR `#[help]` must come after `#[error(..)]` or `#[warn(..)]` +#[error(code = "E0123", slug = "foo")] +struct ErrorWithHelpWrongOrder { + val: String, +} + +#[derive(SessionDiagnostic)] +#[help = "bar"] +//~^ ERROR `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]` +#[error(code = "E0123", slug = "foo")] +struct ErrorWithHelpCustomWrongOrder { + val: String, +} + +#[derive(SessionDiagnostic)] +#[note] +//~^ ERROR `#[note]` must come after `#[error(..)]` or `#[warn(..)]` +#[error(code = "E0123", slug = "foo")] +struct ErrorWithNoteWrongOrder { + val: String, +} + +#[derive(SessionDiagnostic)] +#[note = "bar"] +//~^ ERROR `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]` +#[error(code = "E0123", slug = "foo")] +struct ErrorWithNoteCustomWrongOrder { + val: String, +} diff --git a/src/test/ui-fulldeps/session-derive-errors.stderr b/src/test/ui-fulldeps/session-derive-errors.stderr index c7853f5275e..a528ae1607f 100644 --- a/src/test/ui-fulldeps/session-derive-errors.stderr +++ b/src/test/ui-fulldeps/session-derive-errors.stderr @@ -1,7 +1,7 @@ error: `#[derive(SessionDiagnostic)]` can only be used on structs - --> $DIR/session-derive-errors.rs:34:1 + --> $DIR/session-derive-errors.rs:37:1 | -LL | / #[error = "E0123"] +LL | / #[error(code = "E0123", slug = "foo")] LL | | LL | | enum SessionDiagnosticOnEnum { LL | | Foo, @@ -9,132 +9,285 @@ LL | | Bar, LL | | } | |_^ -error: `#[label = ...]` is not a valid SessionDiagnostic struct attribute - --> $DIR/session-derive-errors.rs:43:1 +error: `#[error = ...]` is not a valid `SessionDiagnostic` struct attribute + --> $DIR/session-derive-errors.rs:46:1 | -LL | #[label = "This is in the wrong place"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[error = "E0123"] + | ^^^^^^^^^^^^^^^^^^ + +error: `#[nonsense(...)]` is not a valid `SessionDiagnostic` struct attribute + --> $DIR/session-derive-errors.rs:51:1 + | +LL | #[nonsense(code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: diagnostic kind not specified + --> $DIR/session-derive-errors.rs:51:1 + | +LL | / #[nonsense(code = "E0123", slug = "foo")] +LL | | +LL | | +LL | | +LL | | struct InvalidStructAttr {} + | |___________________________^ + | + = help: use the `#[error(...)]` attribute to create an error + +error: `#[error("...")]` is not a valid `SessionDiagnostic` struct attribute + --> $DIR/session-derive-errors.rs:58:9 + | +LL | #[error("E0123")] + | ^^^^^^^ + +error: `slug` not specified + --> $DIR/session-derive-errors.rs:58:1 + | +LL | / #[error("E0123")] +LL | | +LL | | +LL | | struct InvalidLitNestedAttr {} + | |______________________________^ + | + = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug + +error: `#[error(nonsense)]` is not a valid `SessionDiagnostic` struct attribute + --> $DIR/session-derive-errors.rs:64:9 + | +LL | #[error(nonsense, code = "E0123", slug = "foo")] + | ^^^^^^^^ + +error: `#[error(nonsense(...))]` is not a valid `SessionDiagnostic` struct attribute + --> $DIR/session-derive-errors.rs:69:9 + | +LL | #[error(nonsense("foo"), code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^^^^ + +error: `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute + --> $DIR/session-derive-errors.rs:74:9 + | +LL | #[error(nonsense = "...", code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^^^^^ -error: `#[suggestion = ...]` is not a valid SessionDiagnostic field attribute - --> $DIR/session-derive-errors.rs:50:5 +error: `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute + --> $DIR/session-derive-errors.rs:79:9 | -LL | #[suggestion = "this is the wrong kind of attribute"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[error(nonsense = 4, code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^ + | + = help: value must be a string + +error: `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute + --> $DIR/session-derive-errors.rs:86:5 + | +LL | #[suggestion = "bar"] + | ^^^^^^^^^^^^^^^^^^^^^ error: `error` specified multiple times - --> $DIR/session-derive-errors.rs:58:11 + --> $DIR/session-derive-errors.rs:93:1 + | +LL | #[error(code = "E0456", slug = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/session-derive-errors.rs:92:1 + | +LL | #[error(code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `warning` specified when `error` was already specified + --> $DIR/session-derive-errors.rs:98:1 + | +LL | #[warning(code = "E0293", slug = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/session-derive-errors.rs:97:1 + | +LL | #[error(code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `code` specified multiple times + --> $DIR/session-derive-errors.rs:103:32 | -LL | #[error = "E0456"] - | ^^^^^^^ +LL | #[error(code = "E0456", code = "E0457", slug = "bar")] + | ^^^^^^^ + | +note: previously specified here + --> $DIR/session-derive-errors.rs:103:16 + | +LL | #[error(code = "E0456", code = "E0457", slug = "bar")] + | ^^^^^^^ -error: `lint` specified when `error` was already specified - --> $DIR/session-derive-errors.rs:64:10 +error: `slug` specified multiple times + --> $DIR/session-derive-errors.rs:107:46 + | +LL | #[error(code = "E0456", slug = "foo", slug = "bar")] + | ^^^^^ | -LL | #[lint = "some_useful_lint"] - | ^^^^^^^^^^^^^^^^^^ +note: previously specified here + --> $DIR/session-derive-errors.rs:107:32 + | +LL | #[error(code = "E0456", slug = "foo", slug = "bar")] + | ^^^^^ -error: `code` not specified - --> $DIR/session-derive-errors.rs:73:1 +error: diagnostic kind not specified + --> $DIR/session-derive-errors.rs:111:1 + | +LL | struct KindNotProvided {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | struct ErrorCodeNotProvided {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use the `#[error(...)]` attribute to create an error + +error: `slug` not specified + --> $DIR/session-derive-errors.rs:114:1 + | +LL | / #[error(code = "E0456")] +LL | | struct SlugNotProvided {} + | |_________________________^ + | + = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug + +error: the `#[primary_span]` attribute can only be applied to fields of type `Span` + --> $DIR/session-derive-errors.rs:124:5 | - = help: use the [code = "..."] attribute to set this diagnostic's error code +LL | #[primary_span] + | ^^^^^^^^^^^^^^^ -error: the `#[message = "..."]` attribute can only be applied to fields of type Span - --> $DIR/session-derive-errors.rs:101:5 +error: `#[nonsense]` is not a valid `SessionDiagnostic` field attribute + --> $DIR/session-derive-errors.rs:132:5 | -LL | #[message = "this message is applied to a String field"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[nonsense] + | ^^^^^^^^^^^ + +error: the `#[label = ...]` attribute can only be applied to fields of type `Span` + --> $DIR/session-derive-errors.rs:149:5 + | +LL | #[label = "bar"] + | ^^^^^^^^^^^^^^^^ error: `name` doesn't refer to a field on this type - --> $DIR/session-derive-errors.rs:108:1 + --> $DIR/session-derive-errors.rs:157:42 | -LL | #[message = "This error has a field, and references {name}"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[suggestion(message = "bar", code = "{name}")] + | ^^^^^^^^ error: invalid format string: expected `'}'` but string was terminated - --> $DIR/session-derive-errors.rs:116:1 + --> $DIR/session-derive-errors.rs:162:16 | LL | #[derive(SessionDiagnostic)] - | ----------------- in this derive macro expansion -LL | #[error = "E0123"] - | - because of this opening brace -LL | #[message = "This is missing a closing brace: {name"] - | ^ expected `'}'` in format string + | - ^ expected `'}'` in format string + | | + | because of this opening brace | = note: if you intended to print `{`, you can escape it using `{{` = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid format string: unmatched `}` found - --> $DIR/session-derive-errors.rs:125:1 + --> $DIR/session-derive-errors.rs:172:15 | LL | #[derive(SessionDiagnostic)] - | ----------------- in this derive macro expansion -LL | #[error = "E0123"] -LL | #[message = "This is missing an opening brace: name}"] - | ^ unmatched `}` in format string + | ^ unmatched `}` in format string | = note: if you intended to print `}`, you can escape it using `}}` = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: The `#[label = ...]` attribute can only be applied to fields of type Span - --> $DIR/session-derive-errors.rs:144:5 +error: the `#[label = ...]` attribute can only be applied to fields of type `Span` + --> $DIR/session-derive-errors.rs:192:5 | -LL | #[label = "See here"] - | ^^^^^^^^^^^^^^^^^^^^^ - -error: `nonsense` is not a valid key for `#[suggestion(...)]` - --> $DIR/session-derive-errors.rs:169:18 - | -LL | #[suggestion(nonsense = "This is nonsense")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[label = "bar"] + | ^^^^^^^^^^^^^^^^ -error: `msg` is not a valid key for `#[suggestion(...)]` - --> $DIR/session-derive-errors.rs:177:18 +error: `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute + --> $DIR/session-derive-errors.rs:217:18 | -LL | #[suggestion(msg = "This is a suggestion")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[suggestion(nonsense = "bar")] + | ^^^^^^^^^^^^^^^^ -error: missing suggestion message - --> $DIR/session-derive-errors.rs:185:7 - | -LL | #[suggestion(code = "This is suggested code")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute + --> $DIR/session-derive-errors.rs:225:18 | - = help: provide a suggestion message using #[suggestion(message = "...")] +LL | #[suggestion(msg = "bar")] + | ^^^^^^^^^^^ error: wrong field type for suggestion - --> $DIR/session-derive-errors.rs:200:5 + --> $DIR/session-derive-errors.rs:247:5 | -LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | / #[suggestion(message = "bar", code = "This is suggested code")] LL | | LL | | suggestion: Applicability, | |_____________________________^ | - = help: #[suggestion(...)] should be applied to fields of type Span or (Span, Applicability) + = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)` -error: type of field annotated with `#[suggestion(...)]` contains more than one Span - --> $DIR/session-derive-errors.rs:215:5 +error: type of field annotated with `#[suggestion(...)]` contains more than one `Span` + --> $DIR/session-derive-errors.rs:262:5 | -LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | / #[suggestion(message = "bar", code = "This is suggested code")] LL | | LL | | suggestion: (Span, Span, Applicability), | |___________________________________________^ error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability - --> $DIR/session-derive-errors.rs:223:5 + --> $DIR/session-derive-errors.rs:270:5 | -LL | / #[suggestion(message = "This is a message", code = "This is suggested code")] +LL | / #[suggestion(message = "bar", code = "This is suggested code")] LL | | LL | | suggestion: (Applicability, Applicability, Span), | |____________________________________________________^ -error: invalid annotation list `#[label(...)]` - --> $DIR/session-derive-errors.rs:231:7 +error: `#[label(...)]` is not a valid `SessionDiagnostic` field attribute + --> $DIR/session-derive-errors.rs:278:5 | -LL | #[label("wrong kind of annotation for label")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[label("bar")] + | ^^^^^^^^^^^^^^^ + +error: `#[help]` must come after `#[error(..)]` or `#[warn(..)]` + --> $DIR/session-derive-errors.rs:399:1 + | +LL | #[help] + | ^^^^^^^ + +error: `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]` + --> $DIR/session-derive-errors.rs:407:1 + | +LL | #[help = "bar"] + | ^^^^^^^^^^^^^^^ + +error: `#[note]` must come after `#[error(..)]` or `#[warn(..)]` + --> $DIR/session-derive-errors.rs:415:1 + | +LL | #[note] + | ^^^^^^^ + +error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]` + --> $DIR/session-derive-errors.rs:423:1 + | +LL | #[note = "bar"] + | ^^^^^^^^^^^^^^^ + +error: cannot find attribute `nonsense` in this scope + --> $DIR/session-derive-errors.rs:51:3 + | +LL | #[nonsense(code = "E0123", slug = "foo")] + | ^^^^^^^^ + +error: cannot find attribute `nonsense` in this scope + --> $DIR/session-derive-errors.rs:132:7 + | +LL | #[nonsense] + | ^^^^^^^^ + +error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope + --> $DIR/session-derive-errors.rs:322:10 + | +LL | struct Hello {} + | ------------ method `into_diagnostic_arg` not found for this +... +LL | #[derive(SessionDiagnostic)] + | ^^^^^^^^^^^^^^^^^ method not found in `Hello` + | + = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 18 previous errors +error: aborting due to 37 previous errors +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.nll.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.nll.stderr index 01f800811ab..55532d6b9b3 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.nll.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.nll.stderr @@ -10,8 +10,8 @@ LL | (a, b) | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of the type Type<'_>, which makes the generic argument '_ invariant - = note: the struct Type<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -26,8 +26,8 @@ LL | (a, b) | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of the type Type<'_>, which makes the generic argument '_ invariant - = note: the struct Type<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance help: `'a` and `'b` must be the same: replace one with the other diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr index e925a424c37..fd9eb05473a 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr @@ -10,8 +10,8 @@ LL | let a = bar(f, x); | ^^^^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of the type Type<'_>, which makes the generic argument '_ invariant - = note: the struct Type<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -26,8 +26,8 @@ LL | let b = bar(f, y); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of the type Type<'_>, which makes the generic argument '_ invariant - = note: the struct Type<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance help: `'a` and `'b` must be the same: replace one with the other diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr index 0457f142e19..5a7ead72949 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr @@ -7,8 +7,8 @@ LL | fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { LL | bar(foo, x) | ^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` | - = note: requirement occurs because of the type Type<'_>, which makes the generic argument '_ invariant - = note: the struct Type<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/c-variadic/variadic-ffi-4.stderr b/src/test/ui/c-variadic/variadic-ffi-4.stderr index ff4da5251a9..6f8e53298ac 100644 --- a/src/test/ui/c-variadic/variadic-ffi-4.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-4.stderr @@ -8,8 +8,8 @@ LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f LL | ap | ^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'f` | - = note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant - = note: the struct VaListImpl<'f> is invariant over the parameter 'f + = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -22,8 +22,8 @@ LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f LL | ap | ^^ function was supposed to return data with lifetime `'f` but it is returning data with lifetime `'1` | - = note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant - = note: the struct VaListImpl<'f> is invariant over the parameter 'f + = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -34,8 +34,8 @@ LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'stati LL | ap | ^^ returning this value requires that `'1` must outlive `'static` | - = note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant - = note: the struct VaListImpl<'f> is invariant over the parameter 'f + = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -57,8 +57,8 @@ LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut LL | *ap0 = ap1; | ^^^^ assignment requires that `'1` must outlive `'2` | - = note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant - = note: the struct VaListImpl<'f> is invariant over the parameter 'f + = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -71,8 +71,8 @@ LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut LL | *ap0 = ap1; | ^^^^ assignment requires that `'2` must outlive `'1` | - = note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant - = note: the struct VaListImpl<'f> is invariant over the parameter 'f + = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -85,7 +85,7 @@ LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` | - = note: requirement occurs because of a mutable reference to VaListImpl<'_> + = note: requirement occurs because of a mutable reference to `VaListImpl<'_>` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -99,7 +99,7 @@ LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` | - = note: requirement occurs because of a mutable reference to VaListImpl<'_> + = note: requirement occurs because of a mutable reference to `VaListImpl<'_>` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -127,8 +127,8 @@ LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut LL | *ap0 = ap1.clone(); | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` | - = note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant - = note: the struct VaListImpl<'f> is invariant over the parameter 'f + = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -141,8 +141,8 @@ LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut LL | *ap0 = ap1.clone(); | ^^^^^^^^^^^ argument requires that `'2` must outlive `'1` | - = note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant - = note: the struct VaListImpl<'f> is invariant over the parameter 'f + = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant + = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to 11 previous errors diff --git a/src/test/ui/error-codes/E0184.stderr b/src/test/ui/error-codes/E0184.stderr index 5bfeaa58bdf..bb3017b6ec2 100644 --- a/src/test/ui/error-codes/E0184.stderr +++ b/src/test/ui/error-codes/E0184.stderr @@ -2,7 +2,7 @@ error[E0184]: the trait `Copy` may not be implemented for this type; the type ha --> $DIR/E0184.rs:1:10 | LL | #[derive(Copy)] - | ^^^^ Copy not allowed on types with destructors + | ^^^^ `Copy` not allowed on types with destructors | = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/exclusive-drop-and-copy.stderr b/src/test/ui/exclusive-drop-and-copy.stderr index 36ee6570e42..8649c8abbfa 100644 --- a/src/test/ui/exclusive-drop-and-copy.stderr +++ b/src/test/ui/exclusive-drop-and-copy.stderr @@ -2,7 +2,7 @@ error[E0184]: the trait `Copy` may not be implemented for this type; the type ha --> $DIR/exclusive-drop-and-copy.rs:3:10 | LL | #[derive(Copy, Clone)] - | ^^^^ Copy not allowed on types with destructors + | ^^^^ `Copy` not allowed on types with destructors | = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -10,7 +10,7 @@ error[E0184]: the trait `Copy` may not be implemented for this type; the type ha --> $DIR/exclusive-drop-and-copy.rs:10:10 | LL | #[derive(Copy, Clone)] - | ^^^^ Copy not allowed on types with destructors + | ^^^^ `Copy` not allowed on types with destructors | = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr index b4c54d52e5c..f5db68e8be1 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr @@ -13,8 +13,8 @@ LL | | fn(Inv<'y>)) } | |______________- in this macro invocation | = help: consider adding the following bound: `'x: 'y` - = note: requirement occurs because of the type Inv<'_>, which makes the generic argument '_ invariant - = note: the struct Inv<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Inv<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -33,8 +33,8 @@ LL | | fn(Inv<'y>)) } | |______________- in this macro invocation | = help: consider adding the following bound: `'x: 'y` - = note: requirement occurs because of the type Inv<'_>, which makes the generic argument '_ invariant - = note: the struct Inv<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Inv<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/match/match-ref-mut-invariance.nll.stderr b/src/test/ui/match/match-ref-mut-invariance.nll.stderr index c8a7876dc54..3b7e53cd527 100644 --- a/src/test/ui/match/match-ref-mut-invariance.nll.stderr +++ b/src/test/ui/match/match-ref-mut-invariance.nll.stderr @@ -9,7 +9,7 @@ LL | match self.0 { ref mut x => x } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a mutable reference to &i32 + = note: requirement occurs because of a mutable reference to `&i32` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr b/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr index 11ddf1487dd..f4d1cea670b 100644 --- a/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr +++ b/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr @@ -10,7 +10,7 @@ LL | x | ^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a mutable reference to &i32 + = note: requirement occurs because of a mutable reference to `&i32` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index cf563072dff..ec2c220b6b8 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -52,8 +52,8 @@ LL | | }); | |______`cell_a` escapes the function body here | argument requires that `'a` must outlive `'static` | - = note: requirement occurs because of the type Cell<&'_#10r u32>, which makes the generic argument &'_#10r u32 invariant - = note: the struct Cell<T> is invariant over the parameter T + = note: requirement occurs because of the type `Cell<&'_#10r u32>`, which makes the generic argument `&'_#10r u32` invariant + = note: the struct `Cell<T>` is invariant over the parameter `T` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 453f6801d7e..234212c8876 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -52,8 +52,8 @@ LL | | }); | |______`cell_a` escapes the function body here | argument requires that `'a` must outlive `'static` | - = note: requirement occurs because of the type Cell<&'_#11r u32>, which makes the generic argument &'_#11r u32 invariant - = note: the struct Cell<T> is invariant over the parameter T + = note: requirement occurs because of the type `Cell<&'_#11r u32>`, which makes the generic argument `&'_#11r u32` invariant + = note: the struct `Cell<T>` is invariant over the parameter `T` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/nll/issue-95272.rs b/src/test/ui/nll/issue-95272.rs new file mode 100644 index 00000000000..5b5308fb8c2 --- /dev/null +++ b/src/test/ui/nll/issue-95272.rs @@ -0,0 +1,17 @@ +#![feature(nll)] + +use std::cell::Cell; + +fn check<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) +where + 'a: 'b, +{ +} + +fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { + let f = check; + //~^ ERROR lifetime may not live long enough + f(x, y); +} + +fn main() {} diff --git a/src/test/ui/nll/issue-95272.stderr b/src/test/ui/nll/issue-95272.stderr new file mode 100644 index 00000000000..41346a4c699 --- /dev/null +++ b/src/test/ui/nll/issue-95272.stderr @@ -0,0 +1,17 @@ +error: lifetime may not live long enough + --> $DIR/issue-95272.rs:12:13 + | +LL | fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let f = check; + | ^^^^^ assignment requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a function pointer to `check` + = note: the function `check` is invariant over the parameter `'a` + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: aborting due to previous error + diff --git a/src/test/ui/nll/type-check-pointer-coercions.stderr b/src/test/ui/nll/type-check-pointer-coercions.stderr index b392c2007d3..24b07cabbac 100644 --- a/src/test/ui/nll/type-check-pointer-coercions.stderr +++ b/src/test/ui/nll/type-check-pointer-coercions.stderr @@ -34,7 +34,7 @@ LL | x | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable pointer to &i32 + = note: requirement occurs because of a mutable pointer to `&i32` = note: mutable pointers are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -50,7 +50,7 @@ LL | x | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a mutable pointer to &i32 + = note: requirement occurs because of a mutable pointer to `&i32` = note: mutable pointers are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/nll/type-check-pointer-comparisons.stderr b/src/test/ui/nll/type-check-pointer-comparisons.stderr index b488af820b8..8c88b229039 100644 --- a/src/test/ui/nll/type-check-pointer-comparisons.stderr +++ b/src/test/ui/nll/type-check-pointer-comparisons.stderr @@ -9,7 +9,7 @@ LL | x == y; | ^ requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a mutable reference to &i32 + = note: requirement occurs because of a mutable reference to `&i32` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -24,7 +24,7 @@ LL | x == y; | ^ requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable reference to &i32 + = note: requirement occurs because of a mutable reference to `&i32` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -41,7 +41,7 @@ LL | x == y; | ^ requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a mutable pointer to &i32 + = note: requirement occurs because of a mutable pointer to `&i32` = note: mutable pointers are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -56,7 +56,7 @@ LL | x == y; | ^ requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable pointer to &i32 + = note: requirement occurs because of a mutable pointer to `&i32` = note: mutable pointers are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -73,7 +73,7 @@ LL | f == g; | ^ requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a mutable reference to &i32 + = note: requirement occurs because of a mutable reference to `&i32` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -88,7 +88,7 @@ LL | f == g; | ^ requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable reference to &i32 + = note: requirement occurs because of a mutable reference to `&i32` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/nll/where_clauses_in_structs.stderr b/src/test/ui/nll/where_clauses_in_structs.stderr index 952667d518d..b88c90e8f54 100644 --- a/src/test/ui/nll/where_clauses_in_structs.stderr +++ b/src/test/ui/nll/where_clauses_in_structs.stderr @@ -9,8 +9,8 @@ LL | Foo { x, y }; | ^ this usage requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of the type Cell<&u32>, which makes the generic argument &u32 invariant - = note: the struct Cell<T> is invariant over the parameter T + = note: requirement occurs because of the type `Cell<&u32>`, which makes the generic argument `&u32` invariant + = note: the struct `Cell<T>` is invariant over the parameter `T` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr b/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr index 376534bf573..6e7eb734a50 100644 --- a/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr +++ b/src/test/ui/regions/region-invariant-static-error-reporting.nll.stderr @@ -12,8 +12,8 @@ LL | x.unwrap() | `x` escapes the function body here | argument requires that `'a` must outlive `'static` | - = note: requirement occurs because of the type Invariant<'_>, which makes the generic argument '_ invariant - = note: the struct Invariant<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr index a64ad46ef46..233a040491c 100644 --- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr +++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr @@ -23,7 +23,7 @@ LL | a(x, y); | ^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable reference to &isize + = note: requirement occurs because of a mutable reference to `&isize` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr index ce5e7d01723..00119743acd 100644 --- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr +++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr @@ -23,7 +23,7 @@ LL | a(x, y, z); | ^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable reference to &isize + = note: requirement occurs because of a mutable reference to `&isize` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr index 32f3080ea37..6193bf02f6d 100644 --- a/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr +++ b/src/test/ui/regions/regions-bounded-method-type-parameters-cross-crate.nll.stderr @@ -10,8 +10,8 @@ LL | a.bigger_region(b) | ^^^^^^^^^^^^^^^^^^ argument requires that `'y` must outlive `'x` | = help: consider adding the following bound: `'y: 'x` - = note: requirement occurs because of the type Inv<'_>, which makes the generic argument '_ invariant - = note: the struct Inv<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Inv<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr index 246b6483c21..0e0086be9ea 100644 --- a/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr +++ b/src/test/ui/regions/regions-bounded-method-type-parameters-trait-bound.nll.stderr @@ -10,8 +10,8 @@ LL | f.method(b); | ^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of the type Inv<'_>, which makes the generic argument '_ invariant - = note: the struct Inv<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Inv<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr index fede5f2d779..c8c7808e06c 100644 --- a/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr +++ b/src/test/ui/regions/regions-infer-invariance-due-to-decl.nll.stderr @@ -6,8 +6,8 @@ LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> { LL | b_isize | ^^^^^^^ returning this value requires that `'r` must outlive `'static` | - = note: requirement occurs because of the type Invariant<'_>, which makes the generic argument '_ invariant - = note: the struct Invariant<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr index 8f5f3667453..1165011c1f4 100644 --- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr +++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-3.nll.stderr @@ -6,8 +6,8 @@ LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> { LL | b_isize | ^^^^^^^ returning this value requires that `'r` must outlive `'static` | - = note: requirement occurs because of the type Invariant<'_>, which makes the generic argument '_ invariant - = note: the struct Invariant<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr index 8079fb0ef0d..f3973a93bad 100644 --- a/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr +++ b/src/test/ui/regions/regions-infer-invariance-due-to-mutability-4.nll.stderr @@ -6,8 +6,8 @@ LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> { LL | b_isize | ^^^^^^^ returning this value requires that `'r` must outlive `'static` | - = note: requirement occurs because of the type Invariant<'_>, which makes the generic argument '_ invariant - = note: the struct Invariant<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/regions-infer-not-param.nll.stderr b/src/test/ui/regions/regions-infer-not-param.nll.stderr index 3183aee23d9..f4875e49c3d 100644 --- a/src/test/ui/regions/regions-infer-not-param.nll.stderr +++ b/src/test/ui/regions/regions-infer-not-param.nll.stderr @@ -17,8 +17,8 @@ LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } | lifetime `'a` defined here | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of the type Indirect2<'_>, which makes the generic argument '_ invariant - = note: the struct Indirect2<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Indirect2<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Indirect2<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -30,8 +30,8 @@ LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } | lifetime `'a` defined here | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of the type Indirect2<'_>, which makes the generic argument '_ invariant - = note: the struct Indirect2<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Indirect2<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Indirect2<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance help: `'b` and `'a` must be the same: replace one with the other diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr index cae692ad2f6..ee3dcef1cb5 100644 --- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr +++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr @@ -23,7 +23,7 @@ LL | a(x, y); | ^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable reference to &isize + = note: requirement occurs because of a mutable reference to `&isize` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr b/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr index 26f0fcae638..1b3a116d508 100644 --- a/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr +++ b/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr @@ -10,7 +10,7 @@ LL | x | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a mutable reference to dyn Dummy + = note: requirement occurs because of a mutable reference to `dyn Dummy` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance @@ -26,7 +26,7 @@ LL | x | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of a mutable reference to dyn Dummy + = note: requirement occurs because of a mutable reference to `dyn Dummy` = note: mutable references are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance diff --git a/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr b/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr index 8e8ca8e47cc..b35a2cb905d 100644 --- a/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr +++ b/src/test/ui/regions/regions-variance-invariant-use-contravariant.nll.stderr @@ -10,8 +10,8 @@ LL | let _: Invariant<'short> = c; | ^^^^^^^^^^^^^^^^^ type annotation requires that `'short` must outlive `'long` | = help: consider adding the following bound: `'short: 'long` - = note: requirement occurs because of the type Invariant<'_>, which makes the generic argument '_ invariant - = note: the struct Invariant<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr b/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr index f9a3d727f7a..761e78d179e 100644 --- a/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr +++ b/src/test/ui/regions/regions-variance-invariant-use-covariant.nll.stderr @@ -7,8 +7,8 @@ LL | fn use_<'b>(c: Invariant<'b>) { LL | let _: Invariant<'static> = c; | ^^^^^^^^^^^^^^^^^^ type annotation requires that `'b` must outlive `'static` | - = note: requirement occurs because of the type Invariant<'_>, which makes the generic argument '_ invariant - = note: the struct Invariant<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Invariant<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/variance/variance-btree-invariant-types.nll.stderr b/src/test/ui/variance/variance-btree-invariant-types.nll.stderr index 867d9f8238a..0d9815cf788 100644 --- a/src/test/ui/variance/variance-btree-invariant-types.nll.stderr +++ b/src/test/ui/variance/variance-btree-invariant-types.nll.stderr @@ -6,8 +6,8 @@ LL | fn iter_cov_key<'a, 'new>(v: IterMut<'a, &'static (), ()>) -> IterMut<'a, & LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::IterMut<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct std::collections::btree_map::IterMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::IterMut<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `std::collections::btree_map::IterMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -18,8 +18,8 @@ LL | fn iter_cov_val<'a, 'new>(v: IterMut<'a, (), &'static ()>) -> IterMut<'a, ( LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::IterMut<'_, (), &()>, which makes the generic argument () invariant - = note: the struct std::collections::btree_map::IterMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::IterMut<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `std::collections::btree_map::IterMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -30,8 +30,8 @@ LL | fn iter_contra_key<'a, 'new>(v: IterMut<'a, &'new (), ()>) -> IterMut<'a, & LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::IterMut<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct std::collections::btree_map::IterMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::IterMut<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `std::collections::btree_map::IterMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -42,8 +42,8 @@ LL | fn iter_contra_val<'a, 'new>(v: IterMut<'a, (), &'new ()>) -> IterMut<'a, ( LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::IterMut<'_, (), &()>, which makes the generic argument () invariant - = note: the struct std::collections::btree_map::IterMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::IterMut<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `std::collections::btree_map::IterMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -54,8 +54,8 @@ LL | fn range_cov_key<'a, 'new>(v: RangeMut<'a, &'static (), ()>) -> RangeMut<'a LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type RangeMut<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct RangeMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `RangeMut<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `RangeMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -66,8 +66,8 @@ LL | fn range_cov_val<'a, 'new>(v: RangeMut<'a, (), &'static ()>) -> RangeMut<'a LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type RangeMut<'_, (), &()>, which makes the generic argument () invariant - = note: the struct RangeMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `RangeMut<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `RangeMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -78,8 +78,8 @@ LL | fn range_contra_key<'a, 'new>(v: RangeMut<'a, &'new (), ()>) -> RangeMut<'a LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type RangeMut<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct RangeMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `RangeMut<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `RangeMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -90,8 +90,8 @@ LL | fn range_contra_val<'a, 'new>(v: RangeMut<'a, (), &'new ()>) -> RangeMut<'a LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type RangeMut<'_, (), &()>, which makes the generic argument () invariant - = note: the struct RangeMut<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `RangeMut<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `RangeMut<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -103,8 +103,8 @@ LL | -> OccupiedEntry<'a, &'new (), ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::OccupiedEntry<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct std::collections::btree_map::OccupiedEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::OccupiedEntry<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `std::collections::btree_map::OccupiedEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -116,8 +116,8 @@ LL | -> OccupiedEntry<'a, (), &'new ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::OccupiedEntry<'_, (), &()>, which makes the generic argument () invariant - = note: the struct std::collections::btree_map::OccupiedEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::OccupiedEntry<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `std::collections::btree_map::OccupiedEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -129,8 +129,8 @@ LL | -> OccupiedEntry<'a, &'static (), ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::OccupiedEntry<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct std::collections::btree_map::OccupiedEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::OccupiedEntry<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `std::collections::btree_map::OccupiedEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -142,8 +142,8 @@ LL | -> OccupiedEntry<'a, (), &'static ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::OccupiedEntry<'_, (), &()>, which makes the generic argument () invariant - = note: the struct std::collections::btree_map::OccupiedEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::OccupiedEntry<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `std::collections::btree_map::OccupiedEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -155,8 +155,8 @@ LL | -> VacantEntry<'a, &'new (), ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::VacantEntry<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct std::collections::btree_map::VacantEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::VacantEntry<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `std::collections::btree_map::VacantEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -168,8 +168,8 @@ LL | -> VacantEntry<'a, (), &'new ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::VacantEntry<'_, (), &()>, which makes the generic argument () invariant - = note: the struct std::collections::btree_map::VacantEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::VacantEntry<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `std::collections::btree_map::VacantEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -181,8 +181,8 @@ LL | -> VacantEntry<'a, &'static (), ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::VacantEntry<'_, &(), ()>, which makes the generic argument &() invariant - = note: the struct std::collections::btree_map::VacantEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::VacantEntry<'_, &(), ()>`, which makes the generic argument `&()` invariant + = note: the struct `std::collections::btree_map::VacantEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -194,8 +194,8 @@ LL | -> VacantEntry<'a, (), &'static ()> { LL | v | ^ returning this value requires that `'new` must outlive `'static` | - = note: requirement occurs because of the type std::collections::btree_map::VacantEntry<'_, (), &()>, which makes the generic argument () invariant - = note: the struct std::collections::btree_map::VacantEntry<'a, K, V> is invariant over the parameter K + = note: requirement occurs because of the type `std::collections::btree_map::VacantEntry<'_, (), &()>`, which makes the generic argument `()` invariant + = note: the struct `std::collections::btree_map::VacantEntry<'a, K, V>` is invariant over the parameter `K` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to 16 previous errors diff --git a/src/test/ui/variance/variance-cell-is-invariant.nll.stderr b/src/test/ui/variance/variance-cell-is-invariant.nll.stderr index d6a85680141..ab5435d1656 100644 --- a/src/test/ui/variance/variance-cell-is-invariant.nll.stderr +++ b/src/test/ui/variance/variance-cell-is-invariant.nll.stderr @@ -10,8 +10,8 @@ LL | let _: Foo<'long> = c; | ^^^^^^^^^^ type annotation requires that `'short` must outlive `'long` | = help: consider adding the following bound: `'short: 'long` - = note: requirement occurs because of the type Foo<'_>, which makes the generic argument '_ invariant - = note: the struct Foo<'a> is invariant over the parameter 'a + = note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Foo<'a>` is invariant over the parameter `'a` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to previous error diff --git a/src/test/ui/variance/variance-use-invariant-struct-1.nll.stderr b/src/test/ui/variance/variance-use-invariant-struct-1.nll.stderr index f1df2a88b6b..600b245c1f7 100644 --- a/src/test/ui/variance/variance-use-invariant-struct-1.nll.stderr +++ b/src/test/ui/variance/variance-use-invariant-struct-1.nll.stderr @@ -10,8 +10,8 @@ LL | v | ^ function was supposed to return data with lifetime `'max` but it is returning data with lifetime `'min` | = help: consider adding the following bound: `'min: 'max` - = note: requirement occurs because of the type SomeStruct<&()>, which makes the generic argument &() invariant - = note: the struct SomeStruct<T> is invariant over the parameter T + = note: requirement occurs because of the type `SomeStruct<&()>`, which makes the generic argument `&()` invariant + = note: the struct `SomeStruct<T>` is invariant over the parameter `T` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: lifetime may not live long enough @@ -26,8 +26,8 @@ LL | v | ^ function was supposed to return data with lifetime `'max` but it is returning data with lifetime `'min` | = help: consider adding the following bound: `'min: 'max` - = note: requirement occurs because of the type SomeStruct<&()>, which makes the generic argument &() invariant - = note: the struct SomeStruct<T> is invariant over the parameter T + = note: requirement occurs because of the type `SomeStruct<&()>`, which makes the generic argument `&()` invariant + = note: the struct `SomeStruct<T>` is invariant over the parameter `T` = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance error: aborting due to 2 previous errors diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs index c71e9f10f79..cc354b50afa 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs @@ -3,11 +3,12 @@ use clippy_utils::higher::IfLetOrMatch; use clippy_utils::visitors::is_local_used; use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; +use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -129,8 +130,8 @@ fn check_arm<'tcx>( &msg, |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); - help_span.push_span_label(binding_span, "replace this binding".into()); - help_span.push_span_label(inner_then_pat.span, "with this pattern".into()); + help_span.push_span_label(binding_span, "replace this binding"); + help_span.push_span_label(inner_then_pat.span, "with this pattern"); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 703aa458f44..92cf82bcd6a 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Applicability, Handler, SuggestionStyle}; +use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; @@ -25,7 +25,7 @@ use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::edition::Edition; -use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; +use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span}; use rustc_span::{sym, FileName, Pos}; use std::io; use std::ops::Range; @@ -621,7 +621,19 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) + .expect("failed to load fallback fluent bundle"); + let emitter = EmitterWriter::new( + Box::new(io::sink()), + None, + None, + fallback_bundle, + false, + false, + false, + None, + false, + ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs index 06190850bb0..ddaffc75188 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Ty}; use rustc_span::sym; -use rustc_span::{MultiSpan, Span}; +use rustc_span::Span; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; @@ -102,7 +102,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(method_name.ident.span); - span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); + span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index ecc9acf4445..06209bfe7b0 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -148,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { - cx.tcx.sess.span_err(span, &err); + cx.tcx.sess.span_err(span, err.as_ref()); } } else { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 5eb7b0f0521..d29d07da7b0 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -235,11 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - &snippet_opt(cx, span) + snippet_opt(cx, span) .map_or( "change the call to".into(), |x| Cow::from(format!("change `{}` to", x)), - ), + ) + .as_ref(), suggestion.into(), Applicability::Unspecified, ); @@ -264,11 +265,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - &snippet_opt(cx, span) + snippet_opt(cx, span) .map_or( "change the call to".into(), |x| Cow::from(format!("change `{}` to", x)) - ), + ) + .as_ref(), suggestion.into(), Applicability::Unspecified, ); diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 9d4313827f7..5f453dc1655 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::expr_sig; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths}; use if_chain::if_chain; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -19,8 +19,8 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::sym; use rustc_span::symbol::Symbol; -use rustc_span::{sym, MultiSpan}; use std::fmt; use std::iter; diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 625a53899df..b142397f71b 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,10 +8,10 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic}; +use rustc_errors::{emitter::MAX_SUGGESTION_HIGHLIGHT_LINES, Applicability, Diagnostic, MultiSpan}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; -use rustc_span::source_map::{MultiSpan, Span}; +use rustc_span::source_map::Span; use std::env; fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { @@ -155,7 +155,13 @@ where }); } -pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { +pub fn span_lint_hir( + cx: &LateContext<'_>, + lint: &'static Lint, + hir_id: HirId, + sp: Span, + msg: &str, +) { cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| { let mut diag = diag.build(msg); docs_link(&mut diag, lint); @@ -272,9 +278,14 @@ pub fn span_lint_and_sugg_for_edges( let sugg_lines_count = sugg.lines().count(); if sugg_lines_count > MAX_SUGGESTION_HIGHLIGHT_LINES { let sm = cx.sess().source_map(); - if let (Ok(line_upper), Ok(line_bottom)) = (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) { + if let (Ok(line_upper), Ok(line_bottom)) = + (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) + { let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2; - let span_upper = sm.span_until_char(sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), '\n'); + let span_upper = sm.span_until_char( + sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), + '\n', + ); let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]); let sugg_lines_vec = sugg.lines().collect::<Vec<&str>>(); diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 855a6a6ef6a..bc1b0d74575 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -165,9 +165,13 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) + .expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + None, + fallback_bundle, false, false, None, @@ -191,7 +195,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { ]; for note in &xs { - handler.note_without_error(note); + handler.note_without_error(note.as_ref()); } // If backtraces are enabled, also print the query stack diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 7571e6d078a..7125b2ee685 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -33,6 +33,12 @@ impl Emitter for SilentEmitter { None } fn emit_diagnostic(&mut self, _db: &Diagnostic) {} + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + panic!("silent emitter attempted to translate a diagnostic"); + } } fn silent_emitter() -> Box<dyn Emitter + Send> { @@ -82,6 +88,14 @@ impl Emitter for SilentOnIgnoredFilesEmitter { } self.handle_non_ignoreable_error(db); } + + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + self.emitter.fluent_bundle() + } + + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + self.emitter.fallback_fluent_bundle() + } } fn default_handler( @@ -100,9 +114,13 @@ fn default_handler( let emitter = if hide_parse_errors { silent_emitter() } else { + let fallback_bundle = rustc_errors::fallback_fluent_bundle(false) + .expect("failed to load fallback fluent bundle"); Box::new(EmitterWriter::stderr( color_cfg, Some(source_map.clone()), + None, + fallback_bundle, false, false, None, @@ -313,7 +331,8 @@ mod tests { use super::*; use crate::config::IgnoreList; use crate::utils::mk_sp; - use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName}; + use rustc_errors::MultiSpan; + use rustc_span::{FileName as SourceMapFileName, RealFileName}; use std::path::PathBuf; use std::sync::atomic::AtomicU32; @@ -328,6 +347,12 @@ mod tests { fn emit_diagnostic(&mut self, _db: &Diagnostic) { self.num_emitted_errors.fetch_add(1, Ordering::Release); } + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + panic!("test emitter attempted to translate a diagnostic"); + } } fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic { diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index ab4be43e495..ea6e01e577c 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -38,6 +38,8 @@ const EXCEPTIONS: &[(&str, &str)] = &[ ("bitmaps", "MPL-2.0+"), // cargo via im-rc ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot ("snap", "BSD-3-Clause"), // rustc + ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) + ("self_cell", "Apache-2.0"), // rustc (fluent translations) // FIXME: this dependency violates the documentation comment above: ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target ]; @@ -113,6 +115,9 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "filetime", "fixedbitset", "flate2", + "fluent-bundle", + "fluent-langneg", + "fluent-syntax", "fortanix-sgx-abi", "generic-array", "getopts", @@ -125,6 +130,8 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "if_chain", "indexmap", "instant", + "intl-memoizer", + "intl_pluralrules", "itertools", "itoa", "jobserver", @@ -157,6 +164,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "pkg-config", "polonius-engine", "ppv-lite86", + "proc-macro-hack", "proc-macro2", "psm", "punycode", @@ -184,6 +192,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "ryu", "scoped-tls", "scopeguard", + "self_cell", "semver", "serde", "serde_derive", @@ -200,9 +209,12 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "tempfile", "termcolor", "termize", + "thiserror", + "thiserror-impl", "thorin-dwp", "thread_local", "time", + "tinystr", "tinyvec", "tracing", "tracing-attributes", @@ -210,11 +222,16 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "tracing-log", "tracing-subscriber", "tracing-tree", + "type-map", "typenum", "unic-char-property", "unic-char-range", "unic-common", "unic-emoji-char", + "unic-langid", + "unic-langid-impl", + "unic-langid-macros", + "unic-langid-macros-impl", "unic-ucd-version", "unicode-normalization", "unicode-script", @@ -228,7 +245,8 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "winapi-i686-pc-windows-gnu", "winapi-util", "winapi-x86_64-pc-windows-gnu", - // this is a false-positive: it's only used by rustfmt, but because it's enabled through a feature, tidy thinks it's used by rustc as well. + // this is a false-positive: it's only used by rustfmt, but because it's enabled through a + // feature, tidy thinks it's used by rustc as well. "yansi-term", ]; |
