improve diagnosts for GATs
This commit is contained in:
parent
fe62c6e295
commit
e4d9bc66f6
113 changed files with 1625 additions and 890 deletions
|
@ -4,7 +4,7 @@ use crate::astconv::{
|
|||
GenericArgCountResult, GenericArgPosition,
|
||||
};
|
||||
use crate::errors::AssocTypeBindingNotAllowed;
|
||||
use crate::structured_errors::{StructuredDiagnostic, WrongNumberOfGenericArgs};
|
||||
use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
|
||||
use rustc_ast::ast::ParamKindOrd;
|
||||
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
|
@ -438,6 +438,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
has_self: bool,
|
||||
infer_args: bool,
|
||||
) -> GenericArgCountResult {
|
||||
debug!(
|
||||
"check_generic_arg_count(span: {:?}, def_id: {:?}, seg: {:?}, gen_params: {:?}, gen_args: {:?})",
|
||||
span, def_id, seg, gen_params, gen_args
|
||||
);
|
||||
|
||||
let default_counts = gen_params.own_defaults();
|
||||
let param_counts = gen_params.own_counts();
|
||||
let named_type_param_count = param_counts.types - has_self as usize;
|
||||
|
@ -453,63 +458,116 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
let mut invalid_args = vec![];
|
||||
|
||||
let mut check_generics =
|
||||
|kind, expected_min, expected_max, provided, params_offset, args_offset, silent| {
|
||||
let mut check_lifetime_args = |min_expected_args: usize,
|
||||
max_expected_args: usize,
|
||||
provided_args: usize,
|
||||
late_bounds_ignore: bool|
|
||||
-> bool {
|
||||
if (min_expected_args..=max_expected_args).contains(&provided_args) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if late_bounds_ignore {
|
||||
return true;
|
||||
}
|
||||
|
||||
if provided_args > max_expected_args {
|
||||
invalid_args.extend(
|
||||
gen_args.args[max_expected_args..provided_args].iter().map(|arg| arg.span()),
|
||||
);
|
||||
};
|
||||
|
||||
let gen_args_info = if provided_args > min_expected_args {
|
||||
invalid_args.extend(
|
||||
gen_args.args[min_expected_args..provided_args].iter().map(|arg| arg.span()),
|
||||
);
|
||||
let num_redundant_args = provided_args - min_expected_args;
|
||||
GenericArgsInfo::ExcessLifetimes { num_redundant_args }
|
||||
} else {
|
||||
let num_missing_args = min_expected_args - provided_args;
|
||||
GenericArgsInfo::MissingLifetimes { num_missing_args }
|
||||
};
|
||||
|
||||
WrongNumberOfGenericArgs::new(
|
||||
tcx,
|
||||
gen_args_info,
|
||||
seg,
|
||||
gen_params,
|
||||
has_self as usize,
|
||||
gen_args,
|
||||
def_id,
|
||||
)
|
||||
.diagnostic()
|
||||
.emit();
|
||||
|
||||
false
|
||||
};
|
||||
|
||||
let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes };
|
||||
let max_expected_lifetime_args = param_counts.lifetimes;
|
||||
let num_provided_lifetime_args = arg_counts.lifetimes;
|
||||
|
||||
let lifetimes_correct = check_lifetime_args(
|
||||
min_expected_lifetime_args,
|
||||
max_expected_lifetime_args,
|
||||
num_provided_lifetime_args,
|
||||
explicit_late_bound == ExplicitLateBound::Yes,
|
||||
);
|
||||
|
||||
let mut check_types_and_consts =
|
||||
|expected_min, expected_max, provided, params_offset, args_offset| {
|
||||
debug!(
|
||||
"check_types_and_consts(expected_min: {:?}, expected_max: {:?}, \
|
||||
provided: {:?}, params_offset: {:?}, args_offset: {:?}",
|
||||
expected_min, expected_max, provided, params_offset, args_offset
|
||||
);
|
||||
if (expected_min..=expected_max).contains(&provided) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if silent {
|
||||
return true;
|
||||
}
|
||||
let num_default_params = expected_max - expected_min;
|
||||
|
||||
if provided > expected_max {
|
||||
let gen_args_info = if provided > expected_max {
|
||||
invalid_args.extend(
|
||||
gen_args.args[args_offset + expected_max..args_offset + provided]
|
||||
.iter()
|
||||
.map(|arg| arg.span()),
|
||||
);
|
||||
let num_redundant_args = provided - expected_max;
|
||||
|
||||
GenericArgsInfo::ExcessTypesOrConsts {
|
||||
num_redundant_args,
|
||||
num_default_params,
|
||||
args_offset,
|
||||
}
|
||||
} else {
|
||||
let num_missing_args = expected_max - provided;
|
||||
|
||||
GenericArgsInfo::MissingTypesOrConsts {
|
||||
num_missing_args,
|
||||
num_default_params,
|
||||
args_offset,
|
||||
}
|
||||
};
|
||||
|
||||
WrongNumberOfGenericArgs {
|
||||
debug!("gen_args_info: {:?}", gen_args_info);
|
||||
|
||||
WrongNumberOfGenericArgs::new(
|
||||
tcx,
|
||||
kind,
|
||||
expected_min,
|
||||
expected_max,
|
||||
provided,
|
||||
params_offset,
|
||||
args_offset,
|
||||
path_segment: seg,
|
||||
gen_args_info,
|
||||
seg,
|
||||
gen_params,
|
||||
params_offset,
|
||||
gen_args,
|
||||
def_id,
|
||||
span,
|
||||
}
|
||||
)
|
||||
.diagnostic()
|
||||
.emit();
|
||||
|
||||
false
|
||||
};
|
||||
|
||||
let lifetimes_correct = check_generics(
|
||||
"lifetime",
|
||||
if infer_lifetimes { 0 } else { param_counts.lifetimes },
|
||||
param_counts.lifetimes,
|
||||
arg_counts.lifetimes,
|
||||
has_self as usize,
|
||||
0,
|
||||
explicit_late_bound == ExplicitLateBound::Yes,
|
||||
);
|
||||
|
||||
let args_correct = {
|
||||
let kind = if param_counts.consts + arg_counts.consts == 0 {
|
||||
"type"
|
||||
} else if named_type_param_count + arg_counts.types == 0 {
|
||||
"const"
|
||||
} else {
|
||||
"generic"
|
||||
};
|
||||
|
||||
let expected_min = if infer_args {
|
||||
0
|
||||
} else {
|
||||
|
@ -517,15 +575,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
- default_counts.types
|
||||
- default_counts.consts
|
||||
};
|
||||
debug!("expected_min: {:?}", expected_min);
|
||||
debug!("arg_counts.lifetimes: {:?}", arg_counts.lifetimes);
|
||||
|
||||
check_generics(
|
||||
kind,
|
||||
check_types_and_consts(
|
||||
expected_min,
|
||||
param_counts.consts + named_type_param_count,
|
||||
arg_counts.consts + arg_counts.types,
|
||||
param_counts.lifetimes + has_self as usize,
|
||||
arg_counts.lifetimes,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ pub enum SizedByDefault {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct ConvertedBinding<'a, 'tcx> {
|
||||
hir_id: hir::HirId,
|
||||
item_name: Ident,
|
||||
kind: ConvertedBindingKind<'a, 'tcx>,
|
||||
gen_args: &'a GenericArgs<'a>,
|
||||
|
@ -590,6 +591,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
}
|
||||
};
|
||||
ConvertedBinding {
|
||||
hir_id: binding.hir_id,
|
||||
item_name: binding.ident,
|
||||
kind,
|
||||
gen_args: binding.gen_args,
|
||||
|
@ -609,6 +611,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
item_segment: &hir::PathSegment<'_>,
|
||||
parent_substs: SubstsRef<'tcx>,
|
||||
) -> SubstsRef<'tcx> {
|
||||
debug!(
|
||||
"create_substs_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}",
|
||||
span, item_def_id, item_segment
|
||||
);
|
||||
if tcx.generics_of(item_def_id).params.is_empty() {
|
||||
self.prohibit_generics(slice::from_ref(item_segment));
|
||||
|
||||
|
@ -1071,9 +1077,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
// Include substitutions for generic parameters of associated types
|
||||
let projection_ty = candidate.map_bound(|trait_ref| {
|
||||
let ident = Ident::new(assoc_ty.ident.name, binding.item_name.span);
|
||||
let item_segment = hir::PathSegment {
|
||||
ident: assoc_ty.ident,
|
||||
hir_id: None,
|
||||
ident,
|
||||
hir_id: Some(binding.hir_id),
|
||||
res: None,
|
||||
args: Some(binding.gen_args),
|
||||
infer_args: false,
|
||||
|
|
|
@ -1,34 +1,20 @@
|
|||
use crate::structured_errors::StructuredDiagnostic;
|
||||
use hir::def::DefKind;
|
||||
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
|
||||
use rustc_middle::ty::{self as ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{def_id::DefId, MultiSpan};
|
||||
|
||||
use GenericArgsInfo::*;
|
||||
|
||||
/// Handles the `wrong number of type / lifetime / ... arguments` family of error messages.
|
||||
pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||
crate tcx: TyCtxt<'tcx>,
|
||||
|
||||
/// "type", "lifetime" etc., put verbatim into the message
|
||||
crate kind: &'static str,
|
||||
crate angle_brackets: AngleBrackets,
|
||||
|
||||
/// Minimum number of expected generic arguments (e.g. `2` for `HashMap`)
|
||||
crate expected_min: usize,
|
||||
|
||||
/// Maximum number of expected generic arguments (e.g. `3` for `HashMap`)
|
||||
crate expected_max: usize,
|
||||
|
||||
/// Number of generic arguments provided by the user
|
||||
crate provided: usize,
|
||||
|
||||
/// Offset into `gen_params` - depends on the `kind`; might be different than `args_offset` when
|
||||
/// user passed e.g. more arguments than was actually expected
|
||||
crate params_offset: usize,
|
||||
|
||||
/// Offset into `gen_args` - depends on the `kind`
|
||||
crate args_offset: usize,
|
||||
crate gen_args_info: GenericArgsInfo,
|
||||
|
||||
/// Offending path segment
|
||||
crate path_segment: &'a hir::PathSegment<'a>,
|
||||
|
@ -36,64 +22,348 @@ pub struct WrongNumberOfGenericArgs<'a, 'tcx> {
|
|||
/// Generic parameters as expected by type or trait
|
||||
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.
|
||||
crate params_offset: usize,
|
||||
|
||||
/// Generic arguments as provided by user
|
||||
crate gen_args: &'a hir::GenericArgs<'a>,
|
||||
|
||||
/// DefId of the generic type
|
||||
crate def_id: DefId,
|
||||
|
||||
/// Offending place where the generic type has been misused
|
||||
crate span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
||||
fn quantifier_and_bound(&self) -> (&'static str, usize) {
|
||||
if self.expected_min == self.expected_max {
|
||||
("", self.expected_min)
|
||||
} else if self.provided < self.expected_min {
|
||||
("at least ", self.expected_min)
|
||||
// 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,
|
||||
},
|
||||
}
|
||||
|
||||
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.is_empty() {
|
||||
AngleBrackets::Missing
|
||||
} else {
|
||||
("at most ", self.expected_max)
|
||||
if gen_args.span().is_none() {
|
||||
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) -> String {
|
||||
if self.missing_lifetimes() { "lifetime".to_string() } else { "generic".to_string() }
|
||||
}
|
||||
|
||||
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.args.iter().fold(0, |acc, arg| match arg {
|
||||
hir::GenericArg::Lifetime(_) => acc + 1,
|
||||
_ => acc,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
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.args.iter().fold(0, |acc, arg| match arg {
|
||||
hir::GenericArg::Type(_) | hir::GenericArg::Const(_) => acc + 1,
|
||||
_ => acc,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// 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, num_params_to_take: usize) -> String {
|
||||
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 {
|
||||
self.gen_params
|
||||
.params
|
||||
.iter()
|
||||
.skip(self.params_offset + self.num_provided_type_or_const_args())
|
||||
.take(num_params_to_take)
|
||||
.map(|param| param.name.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
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 get_verb = |num_args| if num_args == 1 { "was" } else { "were" };
|
||||
|
||||
let (provided_args_str, verb) = match self.gen_args_info {
|
||||
MissingLifetimes { .. } | ExcessLifetimes { .. } => (
|
||||
format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)),
|
||||
get_verb(provided_lt_args),
|
||||
),
|
||||
MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => (
|
||||
format!(
|
||||
"{} generic argument{}",
|
||||
provided_type_or_const_args,
|
||||
pluralize!(provided_type_or_const_args)
|
||||
),
|
||||
get_verb(provided_type_or_const_args),
|
||||
),
|
||||
};
|
||||
|
||||
if self.gen_args.span().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> {
|
||||
let span = self.path_segment.ident.span;
|
||||
|
||||
let msg = {
|
||||
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.quantifier_and_bound();
|
||||
|
||||
if self.gen_args.span().is_some() {
|
||||
format!(
|
||||
"this {} takes {}{} {} argument{} but {}{} {} argument{} {} supplied",
|
||||
def_kind,
|
||||
quantifier,
|
||||
bound,
|
||||
self.kind,
|
||||
pluralize!(bound),
|
||||
if self.provided > 0 && self.provided < self.expected_min {
|
||||
"only "
|
||||
} else {
|
||||
""
|
||||
},
|
||||
self.provided,
|
||||
self.kind,
|
||||
pluralize!(self.provided),
|
||||
if self.provided == 1 { "was" } else { "were" },
|
||||
)
|
||||
} else {
|
||||
format!("missing generics for {} `{}`", def_kind, def_path)
|
||||
}
|
||||
};
|
||||
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 DiagnosticBuilder<'_>) {
|
||||
let (quantifier, bound) = self.quantifier_and_bound();
|
||||
let (quantifier, bound) = self.get_quantifier_and_bound();
|
||||
let provided_args = self.num_provided_args();
|
||||
|
||||
err.span_label(
|
||||
self.path_segment.ident.span,
|
||||
|
@ -101,12 +371,12 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
"expected {}{} {} argument{}",
|
||||
quantifier,
|
||||
bound,
|
||||
self.kind,
|
||||
self.kind(),
|
||||
pluralize!(bound),
|
||||
),
|
||||
);
|
||||
|
||||
// When user's provided too many arguments, we don't highlight each of them, because it
|
||||
// When too many arguments were provided, we don't highlight each of them, because it
|
||||
// would overlap with the suggestion to remove them:
|
||||
//
|
||||
// ```
|
||||
|
@ -114,21 +384,27 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
// ----- ----- supplied 2 type arguments
|
||||
// ^^^^^^^ remove this type argument
|
||||
// ```
|
||||
if self.provided > self.expected_max {
|
||||
if self.too_many_args_provided() {
|
||||
return;
|
||||
}
|
||||
|
||||
let args = self.gen_args.args.iter().skip(self.args_offset).take(self.provided).enumerate();
|
||||
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 == self.provided {
|
||||
if i + 1 == provided_args {
|
||||
format!(
|
||||
"supplied {} {} argument{}",
|
||||
self.provided,
|
||||
self.kind,
|
||||
pluralize!(self.provided)
|
||||
provided_args,
|
||||
self.kind(),
|
||||
pluralize!(provided_args)
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
|
@ -138,58 +414,26 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn suggest(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
if self.provided == 0 {
|
||||
if self.gen_args.span().is_some() {
|
||||
self.suggest_adding_args(err);
|
||||
} else {
|
||||
self.suggest_creating_generics(err);
|
||||
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_removing_args_or_generics(err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
} else if self.provided < self.expected_min {
|
||||
self.suggest_adding_args(err);
|
||||
} else {
|
||||
self.suggest_removing_args_or_generics(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggests to create generics (`<...>`) when current invocation site contains no generics at
|
||||
/// all:
|
||||
///
|
||||
/// ```text
|
||||
/// type Map = HashMap;
|
||||
/// ```
|
||||
fn suggest_creating_generics(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
let params = self
|
||||
.gen_params
|
||||
.params
|
||||
.iter()
|
||||
.skip(self.params_offset)
|
||||
.take(self.expected_min)
|
||||
.map(|param| param.name.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
let def_kind = self.tcx.def_kind(self.def_id);
|
||||
|
||||
let sugg = if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
|
||||
format!("::<{}>", params)
|
||||
} else {
|
||||
format!("<{}>", params)
|
||||
};
|
||||
|
||||
let msg = format!(
|
||||
"use angle brackets to add missing {} argument{}",
|
||||
self.kind,
|
||||
pluralize!(self.expected_min),
|
||||
);
|
||||
|
||||
err.span_suggestion_verbose(
|
||||
self.path_segment.ident.span.shrink_to_hi(),
|
||||
&msg,
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
|
||||
/// Suggests to add missing argument(s) when current invocation site already contains some
|
||||
/// generics:
|
||||
///
|
||||
|
@ -197,38 +441,167 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
/// type Map = HashMap<String>;
|
||||
/// ```
|
||||
fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
assert!(!self.gen_args.is_empty());
|
||||
|
||||
if self.gen_args.parenthesized {
|
||||
return;
|
||||
}
|
||||
|
||||
let missing_arg_count = self.expected_min - self.provided;
|
||||
match self.gen_args_info {
|
||||
MissingLifetimes { .. } => {
|
||||
self.suggest_adding_lifetime_args(err);
|
||||
}
|
||||
MissingTypesOrConsts { .. } => {
|
||||
self.suggest_adding_type_and_const_args(err);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let (span, sugg_prefix) = if self.args_offset + self.provided == 0 {
|
||||
let span = self.gen_args.args[0].span().shrink_to_lo();
|
||||
(span, "")
|
||||
fn suggest_adding_lifetime_args(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
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));
|
||||
|
||||
// we first try to get lifetime name suggestions from scope or elision information. If none is
|
||||
// available we use the parameter defintions
|
||||
let suggested_args = if let Some(hir_id) = self.path_segment.hir_id {
|
||||
if let Some(lifetimes_in_scope) = self.tcx.lifetime_scope(hir_id) {
|
||||
match lifetimes_in_scope {
|
||||
LifetimeScopeForPath::NonElided(param_names) => {
|
||||
debug!("NonElided(param_names: {:?})", param_names);
|
||||
|
||||
if param_names.len() >= num_params_to_take {
|
||||
// use lifetime parameters in scope for suggestions
|
||||
param_names
|
||||
.iter()
|
||||
.take(num_params_to_take)
|
||||
.map(|p| (*p).clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
} else {
|
||||
// Not enough lifetime arguments in scope -> create suggestions from
|
||||
// lifetime parameter names in definition. An error for the incorrect
|
||||
// lifetime scope will be output later.
|
||||
self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
|
||||
}
|
||||
}
|
||||
LifetimeScopeForPath::Elided => {
|
||||
debug!("Elided");
|
||||
// use suggestions of the form `<'_, '_>` in case lifetime can be elided
|
||||
["'_"].repeat(num_params_to_take).join(",")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
|
||||
}
|
||||
} else {
|
||||
let span =
|
||||
self.gen_args.args[self.args_offset + self.provided - 1].span().shrink_to_hi();
|
||||
(span, ", ")
|
||||
self.get_lifetime_args_suggestions_from_param_names(num_params_to_take)
|
||||
};
|
||||
|
||||
let msg = format!("add missing {} argument{}", self.kind, pluralize!(missing_arg_count));
|
||||
debug!("suggested_args: {:?}", &suggested_args);
|
||||
|
||||
let sugg = self
|
||||
.gen_params
|
||||
.params
|
||||
.iter()
|
||||
.skip(self.params_offset + self.provided)
|
||||
.take(missing_arg_count)
|
||||
.map(|param| param.name.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
match self.angle_brackets {
|
||||
AngleBrackets::Missing => {
|
||||
let span = self.path_segment.ident.span;
|
||||
|
||||
let sugg = format!("{}{}", sugg_prefix, sugg);
|
||||
// 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);
|
||||
err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
|
||||
}
|
||||
|
||||
AngleBrackets::Available => {
|
||||
// angle brackets exist, so we insert missing arguments after the existing args
|
||||
|
||||
assert!(!self.gen_args.args.is_empty());
|
||||
|
||||
if self.num_provided_lifetime_args() > 0 {
|
||||
let last_lt_arg_span = self.gen_args.args
|
||||
[self.num_provided_lifetime_args() - 1]
|
||||
.span()
|
||||
.shrink_to_hi();
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
|
||||
if let Ok(last_gen_arg) = source_map.span_to_snippet(last_lt_arg_span) {
|
||||
let sugg = format!("{}, {}", last_gen_arg, suggested_args);
|
||||
|
||||
err.span_suggestion_verbose(
|
||||
last_lt_arg_span,
|
||||
&msg,
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Non-lifetime arguments included in `gen_args` -> insert missing lifetimes before
|
||||
// existing arguments
|
||||
let first_arg_span = self.gen_args.args[0].span().shrink_to_lo();
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
|
||||
if let Ok(first_gen_arg) = source_map.span_to_snippet(first_arg_span) {
|
||||
let sugg = format!("{}, {}", suggested_args, first_gen_arg);
|
||||
|
||||
err.span_suggestion_verbose(
|
||||
first_arg_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 DiagnosticBuilder<'_>) {
|
||||
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 => {
|
||||
// angle brackets exist, so we just insert missing arguments after the existing
|
||||
// type or const args
|
||||
|
||||
let index_last_provided_arg =
|
||||
self.get_lifetime_args_offset() + self.num_provided_type_or_const_args() - 1;
|
||||
if index_last_provided_arg < self.gen_args.args.len() {
|
||||
let first_arg_span =
|
||||
self.gen_args.args[index_last_provided_arg].span().shrink_to_hi();
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
if let Ok(first_gen_arg) = source_map.span_to_snippet(first_arg_span) {
|
||||
let sugg = format!("{}, {}", first_gen_arg, suggested_args);
|
||||
debug!("sugg: {:?}", sugg);
|
||||
|
||||
err.span_suggestion_verbose(
|
||||
first_arg_span,
|
||||
&msg,
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggests to remove redundant argument(s):
|
||||
|
@ -237,12 +610,91 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
/// type Map = HashMap<String, String, String, String>;
|
||||
/// ```
|
||||
fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
assert!(self.provided > 0);
|
||||
let num_provided_lt_args = self.num_provided_lifetime_args();
|
||||
let num_provided_type_const_args = self.num_provided_type_or_const_args();
|
||||
let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
|
||||
assert!(num_provided_args > 0);
|
||||
|
||||
let redundant_args_count = self.provided - self.expected_max;
|
||||
let remove_entire_generics = redundant_args_count >= self.gen_args.args.len();
|
||||
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 (span, msg) = if remove_entire_generics {
|
||||
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 remove_lifetime_args = |err: &mut DiagnosticBuilder<'_>| {
|
||||
let idx_first_redundant_lt_args = self.num_expected_lifetime_args();
|
||||
let span_lo_redundant_lt_args =
|
||||
self.gen_args.args[idx_first_redundant_lt_args].span().shrink_to_lo();
|
||||
let span_hi_redundant_lt_args = self.gen_args.args
|
||||
[idx_first_redundant_lt_args + num_redundant_lt_args - 1]
|
||||
.span()
|
||||
.shrink_to_hi();
|
||||
let eat_comma =
|
||||
idx_first_redundant_lt_args + num_redundant_lt_args - 1 != self.gen_args.args.len();
|
||||
|
||||
let span_redundant_lt_args = if eat_comma {
|
||||
let span_hi = self.gen_args.args
|
||||
[idx_first_redundant_lt_args + num_redundant_lt_args - 1]
|
||||
.span()
|
||||
.shrink_to_hi();
|
||||
span_lo_redundant_lt_args.to(span_hi)
|
||||
} else {
|
||||
span_lo_redundant_lt_args.to(span_hi_redundant_lt_args)
|
||||
};
|
||||
debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args);
|
||||
|
||||
let msg_lifetimes = format!(
|
||||
"remove {} {} argument{}",
|
||||
if num_redundant_args == 1 { "this" } else { "these" },
|
||||
"lifetime",
|
||||
pluralize!(num_redundant_lt_args),
|
||||
);
|
||||
|
||||
err.span_suggestion(
|
||||
span_redundant_lt_args,
|
||||
&msg_lifetimes,
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
|
||||
let remove_type_or_const_args = |err: &mut DiagnosticBuilder<'_>| {
|
||||
let idx_first_redundant_type_or_const_args = self.get_lifetime_args_offset()
|
||||
+ num_redundant_lt_args
|
||||
+ self.num_expected_type_or_const_args();
|
||||
|
||||
let span_lo_redundant_type_or_const_args =
|
||||
self.gen_args.args[idx_first_redundant_type_or_const_args].span().shrink_to_lo();
|
||||
|
||||
let span_hi_redundant_type_or_const_args = self.gen_args.args
|
||||
[idx_first_redundant_type_or_const_args + num_redundant_type_or_const_args - 1]
|
||||
.span()
|
||||
.shrink_to_hi();
|
||||
|
||||
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 msg_types_or_consts = format!(
|
||||
"remove {} {} argument{}",
|
||||
if num_redundant_args == 1 { "this" } else { "these" },
|
||||
"generic",
|
||||
pluralize!(num_redundant_type_or_const_args),
|
||||
);
|
||||
|
||||
err.span_suggestion(
|
||||
span_redundant_type_or_const_args,
|
||||
&msg_types_or_consts,
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
|
||||
if remove_entire_generics {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
|
||||
let span = self
|
||||
|
@ -258,70 +710,16 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
if self.gen_args.parenthesized { "parenthetical " } else { "" },
|
||||
);
|
||||
|
||||
(span, msg)
|
||||
err.span_suggestion(span, &msg, String::new(), 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 {
|
||||
// When it comes to removing particular argument(s) from the generics, there are two
|
||||
// edge cases we have to consider:
|
||||
//
|
||||
// When the first redundant argument is at the beginning or in the middle of the
|
||||
// generics, like so:
|
||||
//
|
||||
// ```
|
||||
// type Map = HashMap<String, String, String, String>;
|
||||
// ^^^^^^^^^^^^^^^^
|
||||
// | span must start with the argument
|
||||
// ```
|
||||
//
|
||||
// When the last redundant argument is at the ending of the generics, like so:
|
||||
//
|
||||
// ```
|
||||
// type Map = HashMap<String, String, String, String>;
|
||||
// ^^^^^^^^^^^^^^^^
|
||||
// | span must start with the comma
|
||||
// ```
|
||||
|
||||
// Index of the first redundant argument
|
||||
let from_idx = self.args_offset + self.expected_max;
|
||||
|
||||
// Index of the last redundant argument
|
||||
let to_idx = self.args_offset + self.provided - 1;
|
||||
|
||||
assert!(from_idx <= to_idx);
|
||||
|
||||
let (from, comma_eaten) = {
|
||||
let first_argument_starts_generics = from_idx == 0;
|
||||
let last_argument_ends_generics = to_idx + 1 == self.gen_args.args.len();
|
||||
|
||||
if !first_argument_starts_generics && last_argument_ends_generics {
|
||||
(self.gen_args.args[from_idx - 1].span().hi(), true)
|
||||
} else {
|
||||
(self.gen_args.args[from_idx].span().lo(), false)
|
||||
}
|
||||
};
|
||||
|
||||
let to = {
|
||||
let hi = self.gen_args.args[to_idx].span().hi();
|
||||
|
||||
if comma_eaten {
|
||||
hi
|
||||
} else {
|
||||
self.gen_args.args.get(to_idx + 1).map(|arg| arg.span().lo()).unwrap_or(hi)
|
||||
}
|
||||
};
|
||||
|
||||
let span = Span::new(from, to, self.span.ctxt());
|
||||
|
||||
let msg = format!(
|
||||
"remove {} {} argument{}",
|
||||
if redundant_args_count == 1 { "this" } else { "these" },
|
||||
self.kind,
|
||||
pluralize!(redundant_args_count),
|
||||
);
|
||||
|
||||
(span, msg)
|
||||
};
|
||||
|
||||
err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect);
|
||||
assert!(redundant_type_or_const_args);
|
||||
remove_type_or_const_args(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the `type defined here` message.
|
||||
|
@ -334,7 +732,7 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
|
||||
let msg = {
|
||||
let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
|
||||
let (quantifier, bound) = self.quantifier_and_bound();
|
||||
let (quantifier, bound) = self.get_quantifier_and_bound();
|
||||
|
||||
let params = if bound == 0 {
|
||||
String::new()
|
||||
|
@ -362,7 +760,7 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> {
|
|||
def_kind,
|
||||
quantifier,
|
||||
bound,
|
||||
self.kind,
|
||||
self.kind(),
|
||||
pluralize!(bound),
|
||||
params,
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue