1
Fork 0

improve diagnosts for GATs

This commit is contained in:
b-naber 2021-02-18 21:01:44 +01:00
parent fe62c6e295
commit e4d9bc66f6
113 changed files with 1625 additions and 890 deletions

View file

@ -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,
)
};

View file

@ -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,

View file

@ -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,
)