summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_analysis/src/structured_errors
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:11:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:13:23 +0000
commit20431706a863f92cb37dc512fef6e48d192aaf2c (patch)
tree2867f13f5fd5437ba628c67d7f87309ccadcd286 /compiler/rustc_hir_analysis/src/structured_errors
parentReleasing progress-linux version 1.65.0+dfsg1-2~progress7.99u1. (diff)
downloadrustc-20431706a863f92cb37dc512fef6e48d192aaf2c.tar.xz
rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.zip
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_analysis/src/structured_errors')
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs61
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs62
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs1023
3 files changed, 1146 insertions, 0 deletions
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
new file mode 100644
index 000000000..324df313e
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
@@ -0,0 +1,61 @@
+use crate::structured_errors::StructuredDiagnostic;
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed};
+use rustc_middle::ty::{Ty, TypeVisitable};
+use rustc_session::Session;
+use rustc_span::Span;
+
+pub struct MissingCastForVariadicArg<'tcx, 's> {
+ pub sess: &'tcx Session,
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub cast_ty: &'s str,
+}
+
+impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx, '_> {
+ fn session(&self) -> &Session {
+ self.sess
+ }
+
+ fn code(&self) -> DiagnosticId {
+ rustc_errors::error_code!(E0617)
+ }
+
+ fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let mut err = self.sess.struct_span_err_with_code(
+ self.span,
+ &format!("can't pass `{}` to variadic function", self.ty),
+ self.code(),
+ );
+
+ if self.ty.references_error() {
+ err.downgrade_to_delayed_bug();
+ }
+
+ if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) {
+ err.span_suggestion(
+ self.span,
+ &format!("cast the value to `{}`", self.cast_ty),
+ format!("{} as {}", snippet, self.cast_ty),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.help(&format!("cast the value to `{}`", self.cast_ty));
+ }
+
+ err
+ }
+
+ fn diagnostic_extended(
+ &self,
+ mut err: DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ err.note(&format!(
+ "certain types, like `{}`, must be casted before passing them to a \
+ variadic function, because of arcane ABI rules dictated by the C \
+ standard",
+ self.ty
+ ));
+
+ err
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
new file mode 100644
index 000000000..bb6088054
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
@@ -0,0 +1,62 @@
+use crate::structured_errors::StructuredDiagnostic;
+use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed};
+use rustc_middle::ty::{Ty, TypeVisitable};
+use rustc_session::Session;
+use rustc_span::Span;
+
+pub struct SizedUnsizedCast<'tcx> {
+ pub sess: &'tcx Session,
+ pub span: Span,
+ pub expr_ty: Ty<'tcx>,
+ pub cast_ty: String,
+}
+
+impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCast<'tcx> {
+ fn session(&self) -> &Session {
+ self.sess
+ }
+
+ fn code(&self) -> DiagnosticId {
+ rustc_errors::error_code!(E0607)
+ }
+
+ fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let mut err = self.sess.struct_span_err_with_code(
+ self.span,
+ &format!(
+ "cannot cast thin pointer `{}` to fat pointer `{}`",
+ self.expr_ty, self.cast_ty
+ ),
+ self.code(),
+ );
+
+ if self.expr_ty.references_error() {
+ err.downgrade_to_delayed_bug();
+ }
+
+ err
+ }
+
+ fn diagnostic_extended(
+ &self,
+ mut err: DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ err.help(
+ "Thin pointers are \"simple\" pointers: they are purely a reference to a
+memory address.
+
+Fat pointers are pointers referencing \"Dynamically Sized Types\" (also
+called DST). DST don't have a statically known size, therefore they can
+only exist behind some kind of pointers that contain additional
+information. Slices and trait objects are DSTs. In the case of slices,
+the additional information the fat pointer holds is their size.
+
+To fix this error, don't try to cast directly between thin and fat
+pointers.
+
+For more information about casts, take a look at The Book:
+https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions",
+ );
+ err
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
new file mode 100644
index 000000000..435912464
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
@@ -0,0 +1,1023 @@
+use crate::structured_errors::StructuredDiagnostic;
+use rustc_errors::{
+ pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed,
+ MultiSpan,
+};
+use rustc_hir as hir;
+use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
+use rustc_session::Session;
+use rustc_span::def_id::DefId;
+use std::iter;
+
+use GenericArgsInfo::*;
+
+/// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
+pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
+ pub(crate) tcx: TyCtxt<'tcx>,
+
+ pub(crate) angle_brackets: AngleBrackets,
+
+ pub(crate) gen_args_info: GenericArgsInfo,
+
+ /// Offending path segment
+ pub(crate) path_segment: &'a hir::PathSegment<'a>,
+
+ /// Generic parameters as expected by type or trait
+ pub(crate) gen_params: &'a ty::Generics,
+
+ /// Index offset into parameters. Depends on whether `Self` is included and on
+ /// number of lifetime parameters in case we're processing missing or redundant
+ /// type or constant arguments.
+ pub(crate) params_offset: usize,
+
+ /// Generic arguments as provided by user
+ pub(crate) gen_args: &'a hir::GenericArgs<'a>,
+
+ /// DefId of the generic type
+ pub(crate) def_id: DefId,
+}
+
+// Provides information about the kind of arguments that were provided for
+// the PathSegment, for which missing generic arguments were detected
+#[derive(Debug)]
+pub(crate) enum AngleBrackets {
+ // No angle brackets were provided, but generic arguments exist in elided form
+ Implied,
+
+ // No angle brackets were provided
+ Missing,
+
+ // Angle brackets are available, but missing some generic arguments
+ Available,
+}
+
+// Information about the kind of arguments that are either missing or are unexpected
+#[derive(Debug)]
+pub enum GenericArgsInfo {
+ MissingLifetimes {
+ num_missing_args: usize,
+ },
+ ExcessLifetimes {
+ num_redundant_args: usize,
+ },
+ MissingTypesOrConsts {
+ num_missing_args: usize,
+
+ // type or const generic arguments can have default values
+ num_default_params: usize,
+
+ // lifetime arguments precede type and const parameters, this
+ // field gives the number of generic lifetime arguments to let
+ // us infer the position of type and const generic arguments
+ // in the angle brackets
+ args_offset: usize,
+ },
+
+ ExcessTypesOrConsts {
+ num_redundant_args: usize,
+
+ // type or const generic arguments can have default values
+ num_default_params: usize,
+
+ // lifetime arguments precede type and const parameters, this
+ // field gives the number of generic lifetime arguments to let
+ // us infer the position of type and const generic arguments
+ // in the angle brackets
+ args_offset: usize,
+
+ // if synthetic type arguments (e.g. `impl Trait`) are specified
+ synth_provided: bool,
+ },
+}
+
+impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
+ pub fn new(
+ tcx: TyCtxt<'tcx>,
+ gen_args_info: GenericArgsInfo,
+ path_segment: &'a hir::PathSegment<'_>,
+ gen_params: &'a ty::Generics,
+ params_offset: usize,
+ gen_args: &'a hir::GenericArgs<'a>,
+ def_id: DefId,
+ ) -> Self {
+ let angle_brackets = if gen_args.span_ext().is_none() {
+ if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied }
+ } else {
+ AngleBrackets::Available
+ };
+
+ Self {
+ tcx,
+ angle_brackets,
+ gen_args_info,
+ path_segment,
+ gen_params,
+ params_offset,
+ gen_args,
+ def_id,
+ }
+ }
+
+ fn missing_lifetimes(&self) -> bool {
+ match self.gen_args_info {
+ MissingLifetimes { .. } | ExcessLifetimes { .. } => true,
+ MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false,
+ }
+ }
+
+ fn kind(&self) -> &str {
+ if self.missing_lifetimes() { "lifetime" } else { "generic" }
+ }
+
+ fn num_provided_args(&self) -> usize {
+ if self.missing_lifetimes() {
+ self.num_provided_lifetime_args()
+ } else {
+ self.num_provided_type_or_const_args()
+ }
+ }
+
+ fn num_provided_lifetime_args(&self) -> usize {
+ match self.angle_brackets {
+ AngleBrackets::Missing => 0,
+ // Only lifetime arguments can be implied
+ AngleBrackets::Implied => self.gen_args.args.len(),
+ AngleBrackets::Available => self.gen_args.num_lifetime_params(),
+ }
+ }
+
+ fn num_provided_type_or_const_args(&self) -> usize {
+ match self.angle_brackets {
+ AngleBrackets::Missing => 0,
+ // Only lifetime arguments can be implied
+ AngleBrackets::Implied => 0,
+ AngleBrackets::Available => self.gen_args.num_generic_params(),
+ }
+ }
+
+ fn num_expected_lifetime_args(&self) -> usize {
+ let num_provided_args = self.num_provided_lifetime_args();
+ match self.gen_args_info {
+ MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args,
+ ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args,
+ _ => 0,
+ }
+ }
+
+ fn num_expected_type_or_const_args(&self) -> usize {
+ let num_provided_args = self.num_provided_type_or_const_args();
+ match self.gen_args_info {
+ MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args,
+ ExcessTypesOrConsts { num_redundant_args, .. } => {
+ num_provided_args - num_redundant_args
+ }
+ _ => 0,
+ }
+ }
+
+ // Gives the number of expected arguments taking into account default arguments
+ fn num_expected_type_or_const_args_including_defaults(&self) -> usize {
+ let provided_args = self.num_provided_type_or_const_args();
+ match self.gen_args_info {
+ MissingTypesOrConsts { num_missing_args, num_default_params, .. } => {
+ provided_args + num_missing_args - num_default_params
+ }
+ ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => {
+ provided_args - num_redundant_args - num_default_params
+ }
+ _ => 0,
+ }
+ }
+
+ fn num_missing_lifetime_args(&self) -> usize {
+ let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args();
+ assert!(missing_args > 0);
+ missing_args
+ }
+
+ fn num_missing_type_or_const_args(&self) -> usize {
+ let missing_args = self.num_expected_type_or_const_args_including_defaults()
+ - self.num_provided_type_or_const_args();
+ assert!(missing_args > 0);
+ missing_args
+ }
+
+ fn num_excess_lifetime_args(&self) -> usize {
+ match self.gen_args_info {
+ ExcessLifetimes { num_redundant_args } => num_redundant_args,
+ _ => 0,
+ }
+ }
+
+ fn num_excess_type_or_const_args(&self) -> usize {
+ match self.gen_args_info {
+ ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args,
+ _ => 0,
+ }
+ }
+
+ fn too_many_args_provided(&self) -> bool {
+ match self.gen_args_info {
+ MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false,
+ ExcessLifetimes { num_redundant_args }
+ | ExcessTypesOrConsts { num_redundant_args, .. } => {
+ assert!(num_redundant_args > 0);
+ true
+ }
+ }
+ }
+
+ fn not_enough_args_provided(&self) -> bool {
+ match self.gen_args_info {
+ MissingLifetimes { num_missing_args }
+ | MissingTypesOrConsts { num_missing_args, .. } => {
+ assert!(num_missing_args > 0);
+ true
+ }
+ ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false,
+ }
+ }
+
+ // Helper method to get the index offset in angle brackets, at which type or const arguments
+ // start appearing
+ fn get_lifetime_args_offset(&self) -> usize {
+ match self.gen_args_info {
+ MissingLifetimes { .. } | ExcessLifetimes { .. } => 0,
+ MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => {
+ args_offset
+ }
+ }
+ }
+
+ fn get_num_default_params(&self) -> usize {
+ match self.gen_args_info {
+ MissingTypesOrConsts { num_default_params, .. }
+ | ExcessTypesOrConsts { num_default_params, .. } => num_default_params,
+ _ => 0,
+ }
+ }
+
+ fn is_synth_provided(&self) -> bool {
+ match self.gen_args_info {
+ ExcessTypesOrConsts { synth_provided, .. } => synth_provided,
+ _ => false,
+ }
+ }
+
+ // Helper function to choose a quantifier word for the number of expected arguments
+ // and to give a bound for the number of expected arguments
+ fn get_quantifier_and_bound(&self) -> (&'static str, usize) {
+ if self.get_num_default_params() == 0 {
+ match self.gen_args_info {
+ MissingLifetimes { .. } | ExcessLifetimes { .. } => {
+ ("", self.num_expected_lifetime_args())
+ }
+ MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => {
+ ("", self.num_expected_type_or_const_args())
+ }
+ }
+ } else {
+ match self.gen_args_info {
+ MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()),
+ MissingTypesOrConsts { .. } => {
+ ("at least ", self.num_expected_type_or_const_args_including_defaults())
+ }
+ ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()),
+ ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()),
+ }
+ }
+ }
+
+ // Creates lifetime name suggestions from the lifetime parameter names
+ fn get_lifetime_args_suggestions_from_param_names(
+ &self,
+ path_hir_id: hir::HirId,
+ num_params_to_take: usize,
+ ) -> String {
+ debug!(?path_hir_id);
+
+ let mut ret = Vec::new();
+ for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
+ debug!(?id);
+ let params = if let Some(generics) = node.generics() {
+ generics.params
+ } else if let hir::Node::Ty(ty) = node
+ && let hir::TyKind::BareFn(bare_fn) = ty.kind
+ {
+ bare_fn.generic_params
+ } else {
+ &[]
+ };
+ ret.extend(params.iter().filter_map(|p| {
+ let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
+ = p.kind
+ else { return None };
+ let hir::ParamName::Plain(name) = p.name else { return None };
+ Some(name.to_string())
+ }));
+ // Suggest `'static` when in const/static item-like.
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Static { .. },
+ ..
+ })
+ | hir::Node::AnonConst(..) = node
+ {
+ ret.extend(
+ std::iter::repeat("'static".to_owned())
+ .take(num_params_to_take.saturating_sub(ret.len())),
+ );
+ }
+ if ret.len() >= num_params_to_take {
+ return ret[..num_params_to_take].join(", ");
+ }
+ // We cannot refer to lifetimes defined in an outer function.
+ if let hir::Node::Item(_) = node {
+ break;
+ }
+ }
+
+ // We could not gather enough lifetime parameters in the scope.
+ // We use the parameter names from the target type's definition instead.
+ self.gen_params
+ .params
+ .iter()
+ .skip(self.params_offset + self.num_provided_lifetime_args())
+ .take(num_params_to_take)
+ .map(|param| param.name.to_string())
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+
+ // Creates type or constant name suggestions from the provided parameter names
+ fn get_type_or_const_args_suggestions_from_param_names(
+ &self,
+ num_params_to_take: usize,
+ ) -> String {
+ let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig);
+ let is_used_in_input = |def_id| {
+ fn_sig.map_or(false, |fn_sig| {
+ fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
+ hir::TyKind::Path(hir::QPath::Resolved(
+ None,
+ hir::Path { res: hir::def::Res::Def(_, id), .. },
+ )) => *id == def_id,
+ _ => false,
+ })
+ })
+ };
+ self.gen_params
+ .params
+ .iter()
+ .skip(self.params_offset + self.num_provided_type_or_const_args())
+ .take(num_params_to_take)
+ .map(|param| match param.kind {
+ // This is being inferred from the item's inputs, no need to set it.
+ ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => {
+ "_".to_string()
+ }
+ _ => param.name.to_string(),
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+
+ fn get_unbound_associated_types(&self) -> Vec<String> {
+ if self.tcx.is_trait(self.def_id) {
+ let items: &AssocItems<'_> = self.tcx.associated_items(self.def_id);
+ items
+ .in_definition_order()
+ .filter(|item| item.kind == AssocKind::Type)
+ .filter(|item| {
+ !self.gen_args.bindings.iter().any(|binding| binding.ident.name == item.name)
+ })
+ .map(|item| item.name.to_ident_string())
+ .collect()
+ } else {
+ Vec::default()
+ }
+ }
+
+ fn create_error_message(&self) -> String {
+ let def_path = self.tcx.def_path_str(self.def_id);
+ let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
+ let (quantifier, bound) = self.get_quantifier_and_bound();
+ let kind = self.kind();
+ let provided_lt_args = self.num_provided_lifetime_args();
+ let provided_type_or_const_args = self.num_provided_type_or_const_args();
+
+ let (provided_args_str, verb) = match self.gen_args_info {
+ MissingLifetimes { .. } | ExcessLifetimes { .. } => (
+ format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
+ pluralize!("was", provided_lt_args),
+ ),
+ MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
+ format!(
+ "{} generic argument{}",
+ provided_type_or_const_args,
+ pluralize!(provided_type_or_const_args)
+ ),
+ pluralize!("was", provided_type_or_const_args),
+ ),
+ };
+
+ if self.gen_args.span_ext().is_some() {
+ format!(
+ "this {} takes {}{} {} argument{} but {} {} supplied",
+ def_kind,
+ quantifier,
+ bound,
+ kind,
+ pluralize!(bound),
+ provided_args_str.as_str(),
+ verb
+ )
+ } else {
+ format!("missing generics for {} `{}`", def_kind, def_path)
+ }
+ }
+
+ fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let span = self.path_segment.ident.span;
+ let msg = self.create_error_message();
+
+ self.tcx.sess.struct_span_err_with_code(span, &msg, self.code())
+ }
+
+ /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
+ fn notify(&self, err: &mut Diagnostic) {
+ let (quantifier, bound) = self.get_quantifier_and_bound();
+ let provided_args = self.num_provided_args();
+
+ err.span_label(
+ self.path_segment.ident.span,
+ format!(
+ "expected {}{} {} argument{}",
+ quantifier,
+ bound,
+ self.kind(),
+ pluralize!(bound),
+ ),
+ );
+
+ // When too many arguments were provided, we don't highlight each of them, because it
+ // would overlap with the suggestion to remove them:
+ //
+ // ```
+ // type Foo = Bar<usize, usize>;
+ // ----- ----- supplied 2 type arguments
+ // ^^^^^^^ remove this type argument
+ // ```
+ if self.too_many_args_provided() {
+ return;
+ }
+
+ let args = self
+ .gen_args
+ .args
+ .iter()
+ .skip(self.get_lifetime_args_offset())
+ .take(provided_args)
+ .enumerate();
+
+ for (i, arg) in args {
+ err.span_label(
+ arg.span(),
+ if i + 1 == provided_args {
+ format!(
+ "supplied {} {} argument{}",
+ provided_args,
+ self.kind(),
+ pluralize!(provided_args)
+ )
+ } else {
+ String::new()
+ },
+ );
+ }
+ }
+
+ fn suggest(&self, err: &mut Diagnostic) {
+ debug!(
+ "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
+ self.num_provided_args(),
+ self.gen_args.span(),
+ );
+
+ match self.angle_brackets {
+ AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err),
+ AngleBrackets::Available => {
+ 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!();
+ }
+ }
+ }
+ }
+
+ /// Suggests to add missing argument(s) when current invocation site already contains some
+ /// generics:
+ ///
+ /// ```text
+ /// type Map = HashMap<String>;
+ /// ```
+ fn suggest_adding_args(&self, err: &mut Diagnostic) {
+ if self.gen_args.parenthesized {
+ return;
+ }
+
+ match self.gen_args_info {
+ MissingLifetimes { .. } => {
+ self.suggest_adding_lifetime_args(err);
+ }
+ MissingTypesOrConsts { .. } => {
+ self.suggest_adding_type_and_const_args(err);
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn suggest_adding_lifetime_args(&self, err: &mut Diagnostic) {
+ debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
+ let num_missing_args = self.num_missing_lifetime_args();
+ let num_params_to_take = num_missing_args;
+ let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
+
+ let suggested_args = self.get_lifetime_args_suggestions_from_param_names(
+ self.path_segment.hir_id,
+ num_params_to_take,
+ );
+ debug!("suggested_args: {:?}", &suggested_args);
+
+ match self.angle_brackets {
+ AngleBrackets::Missing => {
+ let span = self.path_segment.ident.span;
+
+ // insert a suggestion of the form "Y<'a, 'b>"
+ let ident = self.path_segment.ident.name.to_ident_string();
+ let sugg = format!("{}<{}>", ident, suggested_args);
+ debug!("sugg: {:?}", sugg);
+
+ err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
+ }
+
+ AngleBrackets::Available => {
+ let (sugg_span, is_first) = if self.num_provided_lifetime_args() == 0 {
+ (self.gen_args.span().unwrap().shrink_to_lo(), true)
+ } else {
+ let last_lt = &self.gen_args.args[self.num_provided_lifetime_args() - 1];
+ (last_lt.span().shrink_to_hi(), false)
+ };
+ let has_non_lt_args = self.num_provided_type_or_const_args() != 0;
+ let has_bindings = !self.gen_args.bindings.is_empty();
+
+ let sugg_prefix = if is_first { "" } else { ", " };
+ let sugg_suffix =
+ if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" };
+
+ let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
+ debug!("sugg: {:?}", sugg);
+
+ err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
+ }
+ AngleBrackets::Implied => {
+ // We never encounter missing lifetimes in situations in which lifetimes are elided
+ unreachable!();
+ }
+ }
+ }
+
+ fn suggest_adding_type_and_const_args(&self, err: &mut Diagnostic) {
+ let num_missing_args = self.num_missing_type_or_const_args();
+ let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
+
+ let suggested_args =
+ self.get_type_or_const_args_suggestions_from_param_names(num_missing_args);
+ debug!("suggested_args: {:?}", suggested_args);
+
+ match self.angle_brackets {
+ AngleBrackets::Missing | AngleBrackets::Implied => {
+ let span = self.path_segment.ident.span;
+
+ // insert a suggestion of the form "Y<T, U>"
+ let ident = self.path_segment.ident.name.to_ident_string();
+ let sugg = format!("{}<{}>", ident, suggested_args);
+ debug!("sugg: {:?}", sugg);
+
+ err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
+ }
+ AngleBrackets::Available => {
+ let gen_args_span = self.gen_args.span().unwrap();
+ let sugg_offset =
+ self.get_lifetime_args_offset() + self.num_provided_type_or_const_args();
+
+ let (sugg_span, is_first) = if sugg_offset == 0 {
+ (gen_args_span.shrink_to_lo(), true)
+ } else {
+ let arg_span = self.gen_args.args[sugg_offset - 1].span();
+ // If we came here then inferred lifetime's spans can only point
+ // to either the opening bracket or to the space right after.
+ // Both of these spans have an `hi` lower than or equal to the span
+ // of the generics excluding the brackets.
+ // This allows us to check if `arg_span` is the artificial span of
+ // an inferred lifetime, in which case the generic we're suggesting to
+ // add will be the first visible, even if it isn't the actual first generic.
+ (arg_span.shrink_to_hi(), arg_span.hi() <= gen_args_span.lo())
+ };
+
+ let sugg_prefix = if is_first { "" } else { ", " };
+ let sugg_suffix =
+ if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" };
+
+ let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix);
+ debug!("sugg: {:?}", sugg);
+
+ err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders);
+ }
+ }
+ }
+
+ /// 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(parent_node) = self.tcx.hir().find_parent_node(self.path_segment.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_def_id: DefId,
+ expr: &'tcx hir::Expr<'tcx>,
+ msg: String,
+ num_assoc_fn_excess_args: usize,
+ num_trait_generics_except_self: usize,
+ ) {
+ let sm = self.tcx.sess.source_map();
+ let hir::ExprKind::MethodCall(_, rcvr, args, _) = expr.kind else { return; };
+ if num_assoc_fn_excess_args != num_trait_generics_except_self {
+ return;
+ }
+ let Some(gen_args) = self.gen_args.span_ext() else { return; };
+ let Ok(generics) = sm.span_to_snippet(gen_args) else { return; };
+ let Ok(rcvr) = sm.span_to_snippet(
+ rcvr.span.find_ancestor_inside(expr.span).unwrap_or(rcvr.span)
+ ) else { return; };
+ let Ok(rest) =
+ (match args {
+ [] => Ok(String::new()),
+ [arg] => sm.span_to_snippet(
+ arg.span.find_ancestor_inside(expr.span).unwrap_or(arg.span),
+ ),
+ [first, .., last] => {
+ let first_span =
+ first.span.find_ancestor_inside(expr.span).unwrap_or(first.span);
+ let last_span =
+ last.span.find_ancestor_inside(expr.span).unwrap_or(last.span);
+ sm.span_to_snippet(first_span.to(last_span))
+ }
+ }) else { return; };
+ let comma = if args.len() > 0 { ", " } else { "" };
+ let trait_path = self.tcx.def_path_str(trait_def_id);
+ let method_name = self.tcx.item_name(self.def_id);
+ err.span_suggestion(
+ expr.span,
+ msg,
+ format!("{trait_path}::{generics}::{method_name}({rcvr}{comma}{rest})"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ /// Suggests to remove redundant argument(s):
+ ///
+ /// ```text
+ /// type Map = HashMap<String, String, String, String>;
+ /// ```
+ fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) {
+ let num_provided_lt_args = self.num_provided_lifetime_args();
+ let num_provided_type_const_args = self.num_provided_type_or_const_args();
+ let unbound_types = self.get_unbound_associated_types();
+ let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
+ assert!(num_provided_args > 0);
+
+ let num_redundant_lt_args = self.num_excess_lifetime_args();
+ let num_redundant_type_or_const_args = self.num_excess_type_or_const_args();
+ let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args;
+
+ let redundant_lifetime_args = num_redundant_lt_args > 0;
+ let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
+
+ let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
+ let provided_args_matches_unbound_traits =
+ unbound_types.len() == num_redundant_type_or_const_args;
+
+ let remove_lifetime_args = |err: &mut Diagnostic| {
+ let mut lt_arg_spans = Vec::new();
+ let mut found_redundant = false;
+ for arg in self.gen_args.args {
+ if let hir::GenericArg::Lifetime(_) = arg {
+ lt_arg_spans.push(arg.span());
+ if lt_arg_spans.len() > self.num_expected_lifetime_args() {
+ found_redundant = true;
+ }
+ } else if found_redundant {
+ // Argument which is redundant and separated like this `'c`
+ // is not included to avoid including `Bar` in span.
+ // ```
+ // type Foo<'a, T> = &'a T;
+ // let _: Foo<'a, 'b, Bar, 'c>;
+ // ```
+ break;
+ }
+ }
+
+ let span_lo_redundant_lt_args = lt_arg_spans[self.num_expected_lifetime_args()];
+ let span_hi_redundant_lt_args = lt_arg_spans[lt_arg_spans.len() - 1];
+
+ let span_redundant_lt_args = span_lo_redundant_lt_args.to(span_hi_redundant_lt_args);
+ debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args);
+
+ let num_redundant_lt_args = lt_arg_spans.len() - self.num_expected_lifetime_args();
+ let msg_lifetimes = format!(
+ "remove {these} lifetime argument{s}",
+ these = pluralize!("this", num_redundant_lt_args),
+ s = pluralize!(num_redundant_lt_args),
+ );
+
+ err.span_suggestion(
+ span_redundant_lt_args,
+ &msg_lifetimes,
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ };
+
+ let remove_type_or_const_args = |err: &mut Diagnostic| {
+ let mut gen_arg_spans = Vec::new();
+ let mut found_redundant = false;
+ for arg in self.gen_args.args {
+ match arg {
+ hir::GenericArg::Type(_)
+ | hir::GenericArg::Const(_)
+ | hir::GenericArg::Infer(_) => {
+ gen_arg_spans.push(arg.span());
+ if gen_arg_spans.len() > self.num_expected_type_or_const_args() {
+ found_redundant = true;
+ }
+ }
+ _ if found_redundant => break,
+ _ => {}
+ }
+ }
+
+ let span_lo_redundant_type_or_const_args =
+ gen_arg_spans[self.num_expected_type_or_const_args()];
+ let span_hi_redundant_type_or_const_args = gen_arg_spans[gen_arg_spans.len() - 1];
+
+ let span_redundant_type_or_const_args =
+ span_lo_redundant_type_or_const_args.to(span_hi_redundant_type_or_const_args);
+ debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args);
+
+ let num_redundant_gen_args =
+ gen_arg_spans.len() - self.num_expected_type_or_const_args();
+ let msg_types_or_consts = format!(
+ "remove {these} generic argument{s}",
+ these = pluralize!("this", num_redundant_gen_args),
+ s = pluralize!(num_redundant_gen_args),
+ );
+
+ err.span_suggestion(
+ span_redundant_type_or_const_args,
+ &msg_types_or_consts,
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ };
+
+ // If there is a single unbound associated type and a single excess generic param
+ // suggest replacing the generic param with the associated type bound
+ if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
+ let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
+ let suggestions = iter::zip(unused_generics, &unbound_types)
+ .map(|(potential, name)| (potential.span().shrink_to_lo(), format!("{name} = ")))
+ .collect::<Vec<_>>();
+
+ if !suggestions.is_empty() {
+ err.multipart_suggestion_verbose(
+ &format!(
+ "replace the generic bound{s} with the associated type{s}",
+ s = pluralize!(unbound_types.len())
+ ),
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ } else if remove_entire_generics {
+ let span = self
+ .path_segment
+ .args
+ .unwrap()
+ .span_ext()
+ .unwrap()
+ .with_lo(self.path_segment.ident.span.hi());
+
+ let msg = format!(
+ "remove these {}generics",
+ if self.gen_args.parenthesized { "parenthetical " } else { "" },
+ );
+
+ err.span_suggestion(span, &msg, "", Applicability::MaybeIncorrect);
+ } else if redundant_lifetime_args && redundant_type_or_const_args {
+ remove_lifetime_args(err);
+ remove_type_or_const_args(err);
+ } else if redundant_lifetime_args {
+ remove_lifetime_args(err);
+ } else {
+ assert!(redundant_type_or_const_args);
+ remove_type_or_const_args(err);
+ }
+ }
+
+ /// Builds the `type defined here` message.
+ fn show_definition(&self, err: &mut Diagnostic) {
+ let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
+ if self.tcx.sess.source_map().is_span_accessible(def_span) {
+ def_span.into()
+ } else {
+ return;
+ }
+ } else {
+ return;
+ };
+
+ let msg = {
+ let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
+ let (quantifier, bound) = self.get_quantifier_and_bound();
+
+ let params = if bound == 0 {
+ String::new()
+ } else {
+ let params = self
+ .gen_params
+ .params
+ .iter()
+ .skip(self.params_offset)
+ .take(bound)
+ .map(|param| {
+ let span = self.tcx.def_span(param.def_id);
+ spans.push_span_label(span, "");
+ param
+ })
+ .map(|param| format!("`{}`", param.name))
+ .collect::<Vec<_>>()
+ .join(", ");
+
+ format!(": {}", params)
+ };
+
+ format!(
+ "{} defined here, with {}{} {} parameter{}{}",
+ def_kind,
+ quantifier,
+ bound,
+ self.kind(),
+ pluralize!(bound),
+ params,
+ )
+ };
+
+ err.span_note(spans, &msg);
+ }
+
+ /// Add note if `impl Trait` is explicitly specified.
+ fn note_synth_provided(&self, err: &mut Diagnostic) {
+ if !self.is_synth_provided() {
+ return;
+ }
+
+ err.note("`impl Trait` cannot be explicitly specified as a generic argument");
+ }
+}
+
+impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
+ fn session(&self) -> &Session {
+ self.tcx.sess
+ }
+
+ fn code(&self) -> DiagnosticId {
+ rustc_errors::error_code!(E0107)
+ }
+
+ fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let mut err = self.start_diagnostics();
+
+ self.notify(&mut err);
+ self.suggest(&mut err);
+ self.show_definition(&mut err);
+ self.note_synth_provided(&mut err);
+
+ err
+ }
+}