diff options
Diffstat (limited to 'compiler')
20 files changed, 564 insertions, 527 deletions
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 9e4a22e1fa3..fd5aa939197 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -555,7 +555,7 @@ impl TokenStreamBuilder { // Get the first stream, which will become the result stream. // If it's `None`, create an empty stream. - let mut iter = streams.drain(..); + let mut iter = streams.into_iter(); let mut res_stream_lrc = iter.next().unwrap().0; // Append the subsequent elements to the result stream, after diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index 9efea1228ab..937cb671573 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -164,7 +164,7 @@ impl<K: Ord, V> SortedMap<K, V> { /// It is up to the caller to make sure that the elements are sorted by key /// and that there are no duplicates. #[inline] - pub fn insert_presorted(&mut self, mut elements: Vec<(K, V)>) { + pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) { if elements.is_empty() { return; } @@ -173,28 +173,28 @@ impl<K: Ord, V> SortedMap<K, V> { let start_index = self.lookup_index_for(&elements[0].0); - let drain = match start_index { + let elements = match start_index { Ok(index) => { - let mut drain = elements.drain(..); - self.data[index] = drain.next().unwrap(); - drain + let mut elements = elements.into_iter(); + self.data[index] = elements.next().unwrap(); + elements } Err(index) => { if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 { // We can copy the whole range without having to mix with // existing elements. - self.data.splice(index..index, elements.drain(..)); + self.data.splice(index..index, elements.into_iter()); return; } - let mut drain = elements.drain(..); - self.data.insert(index, drain.next().unwrap()); - drain + let mut elements = elements.into_iter(); + self.data.insert(index, elements.next().unwrap()); + elements } }; // Insert the rest - for (k, v) in drain { + for (k, v) in elements { self.insert(k, v); } } diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index f66b1a2976f..faeacd3e410 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs @@ -1,5 +1,6 @@ //! The various pretty-printing routines. +use crate::session_diagnostics::UnprettyDumpFail; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_errors::ErrorGuaranteed; @@ -357,12 +358,15 @@ fn get_source(input: &Input, sess: &Session) -> (String, FileName) { (src, src_name) } -fn write_or_print(out: &str, ofile: Option<&Path>) { +fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) { match ofile { None => print!("{}", out), Some(p) => { if let Err(e) = std::fs::write(p, out) { - panic!("print-print failed to write {} due to {}", p.display(), e); + sess.emit_fatal(UnprettyDumpFail { + path: p.display().to_string(), + err: e.to_string(), + }); } } } @@ -402,7 +406,7 @@ pub fn print_after_parsing( _ => unreachable!(), }; - write_or_print(&out, ofile); + write_or_print(&out, ofile, sess); } pub fn print_after_hir_lowering<'tcx>( @@ -468,7 +472,7 @@ pub fn print_after_hir_lowering<'tcx>( _ => unreachable!(), }; - write_or_print(&out, ofile); + write_or_print(&out, ofile, tcx.sess); } // In an ideal world, this would be a public function called by the driver after @@ -512,7 +516,7 @@ fn print_with_analysis( _ => unreachable!(), }; - write_or_print(&out, ofile); + write_or_print(&out, ofile, tcx.sess); Ok(()) } diff --git a/compiler/rustc_driver/src/session_diagnostics.rs b/compiler/rustc_driver/src/session_diagnostics.rs index fe64d0fca9b..e9696792d05 100644 --- a/compiler/rustc_driver/src/session_diagnostics.rs +++ b/compiler/rustc_driver/src/session_diagnostics.rs @@ -31,3 +31,10 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> { #[derive(SessionDiagnostic)] #[diag(driver::rlink_no_a_file)] pub(crate) struct RlinkNotAFile; + +#[derive(SessionDiagnostic)] +#[diag(driver::unpretty_dump_fail)] +pub(crate) struct UnprettyDumpFail { + pub path: String, + pub err: String, +} diff --git a/compiler/rustc_error_messages/locales/en-US/driver.ftl b/compiler/rustc_error_messages/locales/en-US/driver.ftl index 73f084cf329..8ad198c86c9 100644 --- a/compiler/rustc_error_messages/locales/en-US/driver.ftl +++ b/compiler/rustc_error_messages/locales/en-US/driver.ftl @@ -9,3 +9,5 @@ driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}` driver_rlink_no_a_file = rlink must be a file + +driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}` diff --git a/compiler/rustc_error_messages/locales/en-US/query_system.ftl b/compiler/rustc_error_messages/locales/en-US/query_system.ftl new file mode 100644 index 00000000000..167704e46c0 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/query_system.ftl @@ -0,0 +1,25 @@ +query_system_reentrant = internal compiler error: re-entrant incremental verify failure, suppressing message + +query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node} + .help = This is a known issue with the compiler. Run {$run_cmd} to allow your project to compile + +query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information +query_system_increment_compilation_note2 = See <https://github.com/rust-lang/rust/issues/84970> for more information + +query_system_cycle = cycle detected when {$stack_bottom} + +query_system_cycle_usage = cycle used when {$usage} + +query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again + +query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle + +query_system_cycle_recursive_ty_alias = type aliases cannot be recursive +query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle +query_system_cycle_recursive_ty_alias_help2 = see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information + +query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive + +query_system_cycle_which_requires = ...which requires {$desc}... + +query_system_query_overflow = queries overflow the depth limit! diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 42fb2d538b0..8f47be25db9 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -50,6 +50,7 @@ fluent_messages! { passes => "../locales/en-US/passes.ftl", plugin_impl => "../locales/en-US/plugin_impl.ftl", privacy => "../locales/en-US/privacy.ftl", + query_system => "../locales/en-US/query_system.ftl", save_analysis => "../locales/en-US/save_analysis.ftl", ty_utils => "../locales/en-US/ty_utils.ftl", typeck => "../locales/en-US/typeck.ftl", diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index f75e2596f36..95ae9765a48 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -974,12 +974,12 @@ impl Diagnostic { fn sub_with_highlights<M: Into<SubdiagnosticMessage>>( &mut self, level: Level, - mut message: Vec<(M, Style)>, + message: Vec<(M, Style)>, span: MultiSpan, render_span: Option<MultiSpan>, ) { let message = message - .drain(..) + .into_iter() .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) .collect(); let sub = SubDiagnostic { level, message, span, render_span }; diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 65338f56d9c..4f407badb3f 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -21,7 +21,7 @@ pub trait Translate { /// 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(..)) + FromIterator::from_iter(args.iter().cloned()) } /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index c26d7824758..6d451f3090a 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -393,8 +393,14 @@ impl LateLintPass<'_> for Diagnostics { return; } + let mut found_parent_with_attr = false; let mut found_impl = false; - for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + if let Some(owner_did) = hir_id.as_owner() { + found_parent_with_attr = found_parent_with_attr + || cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics); + } + debug!(?parent); if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent && let Impl { of_trait: Some(of_trait), .. } = impl_ && @@ -407,7 +413,7 @@ impl LateLintPass<'_> for Diagnostics { } } debug!(?found_impl); - if !found_impl { + if !found_parent_with_attr && !found_impl { cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| { lint.build(fluent::lint::diag_out_of_impl).emit(); }) @@ -425,7 +431,7 @@ impl LateLintPass<'_> for Diagnostics { } } debug!(?found_diagnostic_message); - if !found_diagnostic_message { + if !found_parent_with_attr && !found_diagnostic_message { cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| { lint.build(fluent::lint::untranslatable_diag).emit(); }) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index a4ccfcace19..2a4fe48a8ac 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -239,7 +239,7 @@ impl DiagnosticDeriveBuilder { } } - Ok(tokens.drain(..).collect()) + Ok(tokens.into_iter().collect()) } fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 8b40e295bd8..c1b82abc1e0 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -12,7 +12,7 @@ use quote::{format_ident, quote}; use std::collections::HashMap; use std::fmt; use std::str::FromStr; -use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path}; +use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; /// Which kind of suggestion is being created? @@ -28,41 +28,8 @@ enum SubdiagnosticSuggestionKind { Verbose, } -impl FromStr for SubdiagnosticSuggestionKind { - type Err = (); - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "" => Ok(SubdiagnosticSuggestionKind::Normal), - "_short" => Ok(SubdiagnosticSuggestionKind::Short), - "_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden), - "_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose), - _ => Err(()), - } - } -} - -impl SubdiagnosticSuggestionKind { - pub fn to_suggestion_style(&self) -> TokenStream { - match self { - SubdiagnosticSuggestionKind::Normal => { - quote! { rustc_errors::SuggestionStyle::ShowCode } - } - SubdiagnosticSuggestionKind::Short => { - quote! { rustc_errors::SuggestionStyle::HideCodeInline } - } - SubdiagnosticSuggestionKind::Hidden => { - quote! { rustc_errors::SuggestionStyle::HideCodeAlways } - } - SubdiagnosticSuggestionKind::Verbose => { - quote! { rustc_errors::SuggestionStyle::ShowAlways } - } - } - } -} - /// Which kind of subdiagnostic is being created from a variant? -#[derive(Clone)] +#[derive(Clone, Copy)] enum SubdiagnosticKind { /// `#[label(...)]` Label, @@ -73,9 +40,31 @@ enum SubdiagnosticKind { /// `#[warning(...)]` Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` - Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream }, - /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` - MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind }, + Suggestion(SubdiagnosticSuggestionKind), +} + +impl FromStr for SubdiagnosticKind { + type Err = (); + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "label" => Ok(SubdiagnosticKind::Label), + "note" => Ok(SubdiagnosticKind::Note), + "help" => Ok(SubdiagnosticKind::Help), + "warning" => Ok(SubdiagnosticKind::Warn), + "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), + "suggestion_short" => { + Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) + } + "suggestion_hidden" => { + Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden)) + } + "suggestion_verbose" => { + Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose)) + } + _ => Err(()), + } + } } impl quote::IdentFragment for SubdiagnosticKind { @@ -85,9 +74,17 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), SubdiagnosticKind::Warn => write!(f, "warn"), - SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"), - SubdiagnosticKind::MultipartSuggestion { .. } => { - write!(f, "multipart_suggestion_with_style") + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { + write!(f, "suggestion") + } + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => { + write!(f, "suggestion_short") + } + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => { + write!(f, "suggestion_hidden") + } + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => { + write!(f, "suggestion_verbose") } } } @@ -151,9 +148,11 @@ impl<'a> SessionSubdiagnosticDerive<'a> { variant, span, fields: fields_map, + kinds: Vec::new(), + slugs: Vec::new(), + code: None, span_field: None, applicability: None, - has_suggestion_parts: false, }; builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) }); @@ -194,15 +193,21 @@ struct SessionSubdiagnosticDeriveBuilder<'a> { /// derive builder. fields: HashMap<String, TokenStream>, + /// Subdiagnostic kind of the type/variant. + kinds: Vec<(SubdiagnosticKind, proc_macro::Span)>, + + /// Slugs of the subdiagnostic - corresponds to the Fluent identifier for the message - from the + /// `#[kind(slug)]` attribute on the type or variant. + slugs: Vec<(Path, proc_macro::Span)>, + /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]` + /// attribute on the type or variant. + code: Option<(TokenStream, proc_macro::Span)>, + /// Identifier for the binding to the `#[primary_span]` field. span_field: Option<(proc_macro2::Ident, proc_macro::Span)>, /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a /// `rustc_errors::Applicability::*` variant directly. applicability: Option<(TokenStream, proc_macro::Span)>, - - /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error - /// during finalization if still `false`. - has_suggestion_parts: bool, } impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> { @@ -212,133 +217,124 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> { } impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { - fn identify_kind( - &mut self, - ) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> { - let mut kind_slug = None; - - for attr in self.variant.ast().attrs { + fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> { + for (i, attr) in self.variant.ast().attrs.iter().enumerate() { 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()?; - let Meta::List(MetaList { ref nested, .. }) = meta else { - throw_invalid_attr!(attr, &meta); - }; - - let mut kind = match name { - "label" => SubdiagnosticKind::Label, - "note" => SubdiagnosticKind::Note, - "help" => SubdiagnosticKind::Help, - "warning" => SubdiagnosticKind::Warn, - _ => { - if let Some(suggestion_kind) = - name.strip_prefix("suggestion").and_then(|s| s.parse().ok()) - { - SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() } - } else if let Some(suggestion_kind) = - name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) - { - SubdiagnosticKind::MultipartSuggestion { suggestion_kind } - } else { - throw_invalid_attr!(attr, &meta); + let kind = match meta { + Meta::List(MetaList { ref nested, .. }) => { + let mut nested_iter = nested.into_iter(); + if let Some(nested_attr) = nested_iter.next() { + match nested_attr { + NestedMeta::Meta(Meta::Path(path)) => { + self.slugs.push((path.clone(), span)); + } + NestedMeta::Meta(meta @ Meta::NameValue(_)) + if matches!( + meta.path().segments.last().unwrap().ident.to_string().as_str(), + "code" | "applicability" + ) => + { + // don't error for valid follow-up attributes + } + nested_attr => { + throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "first argument of the attribute should be the diagnostic \ + slug", + ) + }) + } + }; } - } - }; - let mut slug = None; - let mut code = None; - - let mut nested_iter = nested.into_iter(); - if let Some(nested_attr) = nested_iter.next() { - match nested_attr { - NestedMeta::Meta(Meta::Path(path)) => { - slug.set_once((path.clone(), span)); - } - NestedMeta::Meta(meta @ Meta::NameValue(_)) - if matches!( - meta.path().segments.last().unwrap().ident.to_string().as_str(), - "code" | "applicability" - ) => - { - // Don't error for valid follow-up attributes. - } - nested_attr => { - throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "first argument of the attribute should be the diagnostic \ - slug", - ) - }) + for nested_attr in nested_iter { + let meta = match nested_attr { + NestedMeta::Meta(ref meta) => meta, + _ => throw_invalid_nested_attr!(attr, &nested_attr), + }; + + 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 { + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + match nested_name { + "code" => { + let formatted_str = self.build_format(&s.value(), s.span()); + self.code.set_once((formatted_str, span)); + } + "applicability" => { + let value = match Applicability::from_str(&s.value()) { + Ok(v) => v, + Err(()) => { + span_err(span, "invalid applicability").emit(); + Applicability::Unspecified + } + }; + self.applicability.set_once((quote! { #value }, span)); + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "only `code` and `applicability` are valid nested \ + attributes", + ) + }), + } + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + if matches!(meta, Meta::Path(_)) { + diag.help( + "a diagnostic slug must be the first argument to the \ + attribute", + ) + } else { + diag + } + }), + } } - }; - } - for nested_attr in nested_iter { - let meta = match nested_attr { - NestedMeta::Meta(ref meta) => meta, - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; + let Ok(kind) = SubdiagnosticKind::from_str(name) else { + throw_invalid_attr!(attr, &meta) + }; - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); + kind + } + _ => throw_invalid_attr!(attr, &meta), + }; - let value = match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value, - Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("a diagnostic slug must be the first argument to the attribute") - }), - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; + if matches!( + kind, + SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note + ) && self.code.is_some() + { + throw_span_err!( + span, + &format!("`code` is not a valid nested attribute of a `{}` attribute", name) + ); + } - match nested_name { - "code" => { - if matches!(kind, SubdiagnosticKind::Suggestion { .. }) { - let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once((formatted_str, span)); - } else { - span_err( - span, - &format!( - "`code` is not a valid nested attribute of a `{}` attribute", - name - ), - ) - .emit(); - } - } - "applicability" => { - if matches!( - kind, - SubdiagnosticKind::Suggestion { .. } - | SubdiagnosticKind::MultipartSuggestion { .. } - ) { - let value = - Applicability::from_str(&value.value()).unwrap_or_else(|()| { - span_err(span, "invalid applicability").emit(); - Applicability::Unspecified - }); - self.applicability.set_once((quote! { #value }, span)); - } else { - span_err( - span, - &format!( - "`applicability` is not a valid nested attribute of a `{}` attribute", - name - ) - ).emit(); - } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("only `code` and `applicability` are valid nested attributes") - }), - } + if matches!( + kind, + SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note + ) && self.applicability.is_some() + { + throw_span_err!( + span, + &format!( + "`applicability` is not a valid nested attribute of a `{}` attribute", + name + ) + ); } - let Some((slug, _)) = slug else { + if self.slugs.len() != i + 1 { throw_span_err!( span, &format!( @@ -346,338 +342,146 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { name ) ); - }; - - match kind { - SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => { - let Some((code, _)) = code else { - throw_span_err!(span, "suggestion without `code = \"...\"`"); - }; - *code_field = code; - } - SubdiagnosticKind::Label - | SubdiagnosticKind::Note - | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::MultipartSuggestion { .. } => {} } - kind_slug.set_once(((kind, slug), span)) + self.kinds.push((kind, span)); } - Ok(kind_slug.map(|(kind_slug, _)| kind_slug)) - } - - /// Generates the code for a field with no attributes. - fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream { - let ast = binding.ast(); - assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg"); - - let diag = &self.diag; - let ident = ast.ident.as_ref().unwrap(); - quote! { - #diag.set_arg( - stringify!(#ident), - #binding - ); - } + Ok(()) } - /// Generates the necessary code for all attributes on a field. - fn generate_field_attr_code( + fn generate_field_code( &mut self, binding: &BindingInfo<'_>, - kind: &SubdiagnosticKind, - ) -> TokenStream { + have_suggestion: bool, + ) -> Result<TokenStream, DiagnosticDeriveError> { let ast = binding.ast(); - assert!(ast.attrs.len() > 0, "field without attributes generating attr code"); - // Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will - // apply the generated code on each element in the `Vec` or `Option`. let inner_ty = FieldInnerTy::from_type(&ast.ty); - ast.attrs - .iter() - .map(|attr| { - let info = FieldInfo { - binding, - ty: inner_ty.inner_type().unwrap_or(&ast.ty), - span: &ast.span(), - }; - - let generated = self - .generate_field_code_inner(kind, attr, info) - .unwrap_or_else(|v| v.to_compile_error()); - - inner_ty.with(binding, generated) - }) - .collect() - } - - fn generate_field_code_inner( - &mut self, - kind: &SubdiagnosticKind, - attr: &Attribute, - info: FieldInfo<'_>, - ) -> Result<TokenStream, DiagnosticDeriveError> { - let meta = attr.parse_meta()?; - match meta { - Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path), - Meta::List(list @ MetaList { .. }) => { - self.generate_field_code_inner_list(kind, attr, info, list) - } - _ => throw_invalid_attr!(attr, &meta), - } - } - - /// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`). - fn generate_field_code_inner_path( - &mut self, - kind: &SubdiagnosticKind, - attr: &Attribute, - info: FieldInfo<'_>, - path: Path, - ) -> Result<TokenStream, DiagnosticDeriveError> { - let span = attr.span().unwrap(); - let ident = &path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - - match name { - "skip_arg" => Ok(quote! {}), - "primary_span" => { - if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) { - throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - diag.help( - "multipart suggestions use one or more `#[suggestion_part]`s rather \ - than one `#[primary_span]`", - ) - }) - } - - report_error_if_not_applied_to_span(attr, &info)?; + let info = FieldInfo { + binding: binding, + ty: inner_ty.inner_type().unwrap_or(&ast.ty), + span: &ast.span(), + }; - let binding = info.binding.binding.clone(); - self.span_field.set_once((binding, span)); + for attr in &ast.attrs { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + let span = attr.span().unwrap(); - Ok(quote! {}) - } - "suggestion_part" => { - self.has_suggestion_parts = true; - - match kind { - SubdiagnosticKind::MultipartSuggestion { .. } => { - span_err( - span, - "`#[suggestion_part(...)]` attribute without `code = \"...\"`", - ) - .emit(); - Ok(quote! {}) + let meta = attr.parse_meta()?; + match meta { + Meta::Path(_) => match name { + "primary_span" => { + report_error_if_not_applied_to_span(attr, &info)?; + self.span_field.set_once((binding.binding.clone(), span)); + return Ok(quote! {}); } - SubdiagnosticKind::Label - | SubdiagnosticKind::Note - | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::Suggestion { .. } => { - throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - diag.help( - "`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead", - ) - }); + "applicability" if have_suggestion => { + report_error_if_not_applied_to_applicability(attr, &info)?; + let binding = binding.binding.clone(); + self.applicability.set_once((quote! { #binding }, span)); + return Ok(quote! {}); } - } - } - "applicability" => { - if let SubdiagnosticKind::Suggestion { .. } - | SubdiagnosticKind::MultipartSuggestion { .. } = kind - { - report_error_if_not_applied_to_applicability(attr, &info)?; - - let binding = info.binding.binding.clone(); - self.applicability.set_once((quote! { #binding }, span)); - } else { - span_err(span, "`#[applicability]` is only valid on suggestions").emit(); - } - - Ok(quote! {}) - } - _ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind { - "suggestion_part" - } else { - "primary_span" - }; - diag.help(format!( - "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes", - )) - }), - } - } - - /// Generates the code for a `[Meta::List]`-like attribute on a field (e.g. - /// `#[suggestion_part(code = "...")]`). - fn generate_field_code_inner_list( - &mut self, - kind: &SubdiagnosticKind, - attr: &Attribute, - info: FieldInfo<'_>, - list: MetaList, - ) -> Result<TokenStream, DiagnosticDeriveError> { - let span = attr.span().unwrap(); - let ident = &list.path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - - match name { - "suggestion_part" => { - if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) { - throw_invalid_attr!(attr, &Meta::List(list), |diag| { + "applicability" => { + span_err(span, "`#[applicability]` is only valid on suggestions").emit(); + return Ok(quote! {}); + } + "skip_arg" => { + return Ok(quote! {}); + } + _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help( - "`#[suggestion_part(...)]` is only valid in multipart suggestions", + "only `primary_span`, `applicability` and `skip_arg` are valid field \ + attributes", ) - }) - } - - self.has_suggestion_parts = true; - - report_error_if_not_applied_to_span(attr, &info)?; - - let mut code = None; - for nested_attr in list.nested.iter() { - let NestedMeta::Meta(ref meta) = nested_attr else { - throw_invalid_nested_attr!(attr, &nested_attr); - }; - - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - - let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else { - throw_invalid_nested_attr!(attr, &nested_attr); - }; + }), + }, + _ => throw_invalid_attr!(attr, &meta), + } + } - match nested_name { - "code" => { - let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once((formatted_str, span)); - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("`code` is the only valid nested attribute") - }), - } - } + let ident = ast.ident.as_ref().unwrap(); - let Some((code, _)) = code else { - span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") - .emit(); - return Ok(quote! {}); - }; - let binding = info.binding; + let diag = &self.diag; + let generated = quote! { + #diag.set_arg( + stringify!(#ident), + #binding + ); + }; - Ok(quote! { suggestions.push((#binding, #code)); }) - } - _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| { - let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind { - "suggestion_part" - } else { - "primary_span" - }; - diag.help(format!( - "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes", - )) - }), - } + Ok(inner_ty.with(binding, generated)) } - pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { - let Some((kind, slug)) = self.identify_kind()? else { + fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { + self.identify_kind()?; + if self.kinds.is_empty() { throw_span_err!( self.variant.ast().ident.span().unwrap(), "subdiagnostic kind not specified" ); }; + let have_suggestion = + self.kinds.iter().any(|(k, _)| matches!(k, SubdiagnosticKind::Suggestion(_))); + let mut args = TokenStream::new(); + for binding in self.variant.bindings() { + let arg = self + .generate_field_code(binding, have_suggestion) + .unwrap_or_else(|v| v.to_compile_error()); + args.extend(arg); + } + let mut tokens = TokenStream::new(); + for ((kind, _), (slug, _)) in self.kinds.iter().zip(&self.slugs) { + let code = match self.code.as_ref() { + Some((code, _)) => Some(quote! { #code }), + None if have_suggestion => { + span_err(self.span, "suggestion without `code = \"...\"`").emit(); + Some(quote! { /* macro error */ "..." }) + } + None => None, + }; - let init = match &kind { - SubdiagnosticKind::Label - | SubdiagnosticKind::Note - | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::Suggestion { .. } => quote! {}, - SubdiagnosticKind::MultipartSuggestion { .. } => { - quote! { let mut suggestions = Vec::new(); } - } - }; - - let attr_args: TokenStream = self - .variant - .bindings() - .iter() - .filter(|binding| !binding.ast().attrs.is_empty()) - .map(|binding| self.generate_field_attr_code(binding, &kind)) - .collect(); - - let span_field = self.span_field.as_ref().map(|(span, _)| span); - let applicability = self.applicability.take().map_or_else( - || quote! { rustc_errors::Applicability::Unspecified }, - |(applicability, _)| applicability, - ); + let span_field = self.span_field.as_ref().map(|(span, _)| span); + let applicability = match self.applicability.clone() { + Some((applicability, _)) => Some(applicability), + None if have_suggestion => { + span_err(self.span, "suggestion without `applicability`").emit(); + Some(quote! { rustc_errors::Applicability::Unspecified }) + } + None => None, + }; - let diag = &self.diag; - let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); - let message = quote! { rustc_errors::fluent::#slug }; - let call = match kind { - SubdiagnosticKind::Suggestion { suggestion_kind, code } => { + let diag = &self.diag; + let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); + let message = quote! { rustc_errors::fluent::#slug }; + let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) { if let Some(span) = span_field { - let style = suggestion_kind.to_suggestion_style(); - - quote! { #diag.#name(#span, #message, #code, #applicability, #style); } + quote! { #diag.#name(#span, #message, #code, #applicability); } } else { span_err(self.span, "suggestion without `#[primary_span]` field").emit(); quote! { unreachable!(); } } - } - SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => { - if !self.has_suggestion_parts { - span_err( - self.span, - "multipart suggestion without any `#[suggestion_part(...)]` fields", - ) - .emit(); - } - - let style = suggestion_kind.to_suggestion_style(); - - quote! { #diag.#name(#message, suggestions, #applicability, #style); } - } - SubdiagnosticKind::Label => { + } else if matches!(kind, SubdiagnosticKind::Label) { if let Some(span) = span_field { quote! { #diag.#name(#span, #message); } } else { span_err(self.span, "label without `#[primary_span]` field").emit(); quote! { unreachable!(); } } - } - _ => { + } else { if let Some(span) = span_field { quote! { #diag.#name(#span, #message); } } else { quote! { #diag.#name(#message); } } - } - }; + }; + tokens.extend(quote! { + #call + #args + }); + } - let plain_args: TokenStream = self - .variant - .bindings() - .iter() - .filter(|binding| binding.ast().attrs.is_empty()) - .map(|binding| self.generate_field_set_arg(binding)) - .collect(); - - Ok(quote! { - #init - #attr_args - #call - #plain_args - }) + Ok(tokens) } } diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs new file mode 100644 index 00000000000..5f992ec9e21 --- /dev/null +++ b/compiler/rustc_query_system/src/error.rs @@ -0,0 +1,73 @@ +use rustc_errors::AddSubdiagnostic; +use rustc_span::Span; + +pub struct CycleStack { + pub span: Span, + pub desc: String, +} + +impl AddSubdiagnostic for CycleStack { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + diag.span_note(self.span, &format!("...which requires {}...", self.desc)); + } +} + +#[derive(SessionSubdiagnostic)] +pub enum StackCount { + #[note(query_system::cycle_stack_single)] + Single, + #[note(query_system::cycle_stack_multiple)] + Multiple, +} + +#[derive(SessionSubdiagnostic)] +pub enum Alias { + #[note(query_system::cycle_recursive_ty_alias)] + #[help(query_system::cycle_recursive_ty_alias_help1)] + #[help(query_system::cycle_recursive_ty_alias_help2)] + Ty, + #[note(query_system::cycle_recursive_trait_alias)] + Trait, +} + +#[derive(SessionSubdiagnostic)] +#[note(query_system::cycle_usage)] +pub struct CycleUsage { + #[primary_span] + pub span: Span, + pub usage: String, +} + +#[derive(SessionDiagnostic)] +#[diag(query_system::cycle, code = "E0391")] +pub struct Cycle { + #[primary_span] + pub span: Span, + pub stack_bottom: String, + #[subdiagnostic] + pub cycle_stack: Vec<CycleStack>, + #[subdiagnostic] + pub stack_count: StackCount, + #[subdiagnostic] + pub alias: Option<Alias>, + #[subdiagnostic] + pub cycle_usage: Option<CycleUsage>, +} + +#[derive(SessionDiagnostic)] +#[diag(query_system::reentrant)] +pub struct Reentrant; + +#[derive(SessionDiagnostic)] +#[diag(query_system::increment_compilation)] +#[help] +#[note(query_system::increment_compilation_note1)] +#[note(query_system::increment_compilation_note2)] +pub struct IncrementCompilation { + pub run_cmd: String, + pub dep_node: String, +} + +#[derive(SessionDiagnostic)] +#[diag(query_system::query_overflow)] +pub struct QueryOverflow; diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 68284dcaa0b..7067bc5f09c 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -5,6 +5,8 @@ #![feature(min_specialization)] #![feature(extern_types)] #![allow(rustc::potential_query_instability)] +// #![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; @@ -15,5 +17,6 @@ extern crate rustc_macros; pub mod cache; pub mod dep_graph; +mod error; pub mod ich; pub mod query; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 6d2aff38172..ddb5cd06344 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,12 +1,11 @@ +use crate::error::CycleStack; use crate::query::plumbing::CycleError; use crate::query::{QueryContext, QueryStackFrame}; -use rustc_hir::def::DefKind; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{ - struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level, -}; -use rustc_session::Session; +use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level}; +use rustc_hir::def::DefKind; +use rustc_session::{Session, SessionDiagnostic}; use rustc_span::Span; use std::hash::Hash; @@ -536,46 +535,44 @@ pub(crate) fn report_cycle<'a>( assert!(!stack.is_empty()); let span = stack[0].query.default_span(stack[1 % stack.len()].span); - let mut err = - struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description); + + let mut cycle_stack = Vec::new(); + + use crate::error::StackCount; + let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; for i in 1..stack.len() { let query = &stack[i].query; let span = query.default_span(stack[(i + 1) % stack.len()].span); - err.span_note(span, &format!("...which requires {}...", query.description)); - } - - if stack.len() == 1 { - err.note(&format!("...which immediately requires {} again", stack[0].query.description)); - } else { - err.note(&format!( - "...which again requires {}, completing the cycle", - stack[0].query.description - )); - } - - if stack.iter().all(|entry| { - entry - .query - .def_kind - .map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias)) - }) { - if stack.iter().all(|entry| { - entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias)) - }) { - err.note("type aliases cannot be recursive"); - err.help("consider using a struct, enum, or union instead to break the cycle"); - err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information"); - } else { - err.note("trait aliases cannot be recursive"); - } + cycle_stack.push(CycleStack { span, desc: query.description.to_owned() }); } + let mut cycle_usage = None; if let Some((span, query)) = usage { - err.span_note(query.default_span(span), &format!("cycle used when {}", query.description)); + cycle_usage = Some(crate::error::CycleUsage { + span: query.default_span(span), + usage: query.description, + }); } - err + let alias = if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TyAlias)) { + Some(crate::error::Alias::Ty) + } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) { + Some(crate::error::Alias::Trait) + } else { + None + }; + + let cycle_diag = crate::error::Cycle { + span, + cycle_stack, + stack_bottom: stack[0].query.description.to_owned(), + alias, + cycle_usage: cycle_usage, + stack_count, + }; + + cycle_diag.into_diagnostic(&sess.parse_sess) } pub fn print_query_stack<CTX: QueryContext>( diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index a1f2b081d43..c6197b9fedb 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -125,6 +125,6 @@ pub trait QueryContext: HasDepContext { ) -> R; fn depth_limit_error(&self) { - self.dep_context().sess().fatal("queries overflow the depth limit!"); + self.dep_context().sess().emit_fatal(crate::error::QueryOverflow); } } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 6ae9147ff77..e97411b777b 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -618,16 +618,12 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true)); if old_in_panic { - sess.struct_err( - "internal compiler error: re-entrant incremental verify failure, suppressing message", - ) - .emit(); + sess.emit_err(crate::error::Reentrant); } 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") - .emit(); + sess.emit_err(crate::error::IncrementCompilation { + run_cmd, + dep_node: format!("{:?}", dep_node), + }); panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index d41edea6a25..9fc637ddbbf 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1792,7 +1792,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } }; - let mut suggestable_variants = variants + let suggestable_variants = variants .iter() .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind)) .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) @@ -1802,8 +1802,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { CtorKind::Fictive => format!("({} {{}})", variant), }) .collect::<Vec<_>>(); + let no_suggestable_variant = suggestable_variants.is_empty(); - if !suggestable_variants.is_empty() { + if !no_suggestable_variant { let msg = if suggestable_variants.len() == 1 { "you might have meant to use the following enum variant" } else { @@ -1813,7 +1814,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants.drain(..), + suggestable_variants.into_iter(), Applicability::MaybeIncorrect, ); } @@ -1830,15 +1831,15 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .collect::<Vec<_>>(); if !suggestable_variants_with_placeholders.is_empty() { - let msg = match ( - suggestable_variants.is_empty(), - suggestable_variants_with_placeholders.len(), - ) { - (true, 1) => "the following enum variant is available", - (true, _) => "the following enum variants are available", - (false, 1) => "alternatively, the following enum variant is available", - (false, _) => "alternatively, the following enum variants are also available", - }; + let msg = + match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) { + (true, 1) => "the following enum variant is available", + (true, _) => "the following enum variants are available", + (false, 1) => "alternatively, the following enum variant is available", + (false, _) => { + "alternatively, the following enum variants are also available" + } + }; err.span_suggestions( span, diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index ba687bc4da4..9ecf34e9ad3 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -501,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if !errors_buffer.is_empty() { errors_buffer.sort_by_key(|diag| diag.span.primary_span()); - for mut diag in errors_buffer.drain(..) { + for mut diag in errors_buffer { self.tcx().sess.diagnostic().emit_diagnostic(&mut diag); } } 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 6aa1c915542..d4b5e5e2fe4 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 @@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { if self.not_enough_args_provided() { self.suggest_adding_args(err); } else if self.too_many_args_provided() { + self.suggest_moving_args_from_assoc_fn_to_trait(err); self.suggest_removing_args_or_generics(err); } else { unreachable!(); @@ -653,6 +654,123 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } + /// Suggests moving redundant argument(s) of an associate function to the + /// trait it belongs to. + /// + /// ```compile_fail + /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)` + /// ``` + fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) { + let trait_ = match self.tcx.trait_of_item(self.def_id) { + Some(def_id) => def_id, + None => return, + }; + + // Skip suggestion when the associated function is itself generic, it is unclear + // how to split the provided parameters between those to suggest to the trait and + // those to remain on the associated type. + let num_assoc_fn_expected_args = + self.num_expected_type_or_const_args() + self.num_expected_lifetime_args(); + if num_assoc_fn_expected_args > 0 { + return; + } + + let num_assoc_fn_excess_args = + self.num_excess_type_or_const_args() + self.num_excess_lifetime_args(); + + let trait_generics = self.tcx.generics_of(trait_); + let num_trait_generics_except_self = + trait_generics.count() - if trait_generics.has_self { 1 } else { 0 }; + + let msg = format!( + "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}", + these = pluralize!("this", num_assoc_fn_excess_args), + s = pluralize!(num_assoc_fn_excess_args), + name = self.tcx.item_name(trait_), + num = num_trait_generics_except_self, + ); + + if let Some(hir_id) = self.path_segment.hir_id + && let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id) + && let Some(parent_node) = self.tcx.hir().find(parent_node) + && let hir::Node::Expr(expr) = parent_node { + match expr.kind { + hir::ExprKind::Path(ref qpath) => { + self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( + err, + qpath, + msg, + num_assoc_fn_excess_args, + num_trait_generics_except_self + ) + }, + hir::ExprKind::MethodCall(..) => { + self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call( + err, + trait_, + expr, + msg, + num_assoc_fn_excess_args, + num_trait_generics_except_self + ) + }, + _ => return, + } + } + } + + fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( + &self, + err: &mut Diagnostic, + qpath: &'tcx hir::QPath<'tcx>, + msg: String, + num_assoc_fn_excess_args: usize, + num_trait_generics_except_self: usize, + ) { + if let hir::QPath::Resolved(_, path) = qpath + && let Some(trait_path_segment) = path.segments.get(0) { + let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params(); + + if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait { + if let Some(span) = self.gen_args.span_ext() + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let sugg = vec![ + (self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)), + (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned()) + ]; + + err.multipart_suggestion( + msg, + sugg, + Applicability::MaybeIncorrect + ); + } + } + } + } + + fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call( + &self, + err: &mut Diagnostic, + trait_: DefId, + expr: &'tcx hir::Expr<'tcx>, + msg: String, + num_assoc_fn_excess_args: usize, + num_trait_generics_except_self: usize, + ) { + if let hir::ExprKind::MethodCall(_, args, _) = expr.kind { + assert_eq!(args.len(), 1); + if num_assoc_fn_excess_args == num_trait_generics_except_self { + if let Some(gen_args) = self.gen_args.span_ext() + && let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args) + && let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) { + let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args); + err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect); + } + } + } + } + /// Suggests to remove redundant argument(s): /// /// ```text |
