Auto merge of #77524 - Patryk27:fixes/66228, r=estebank
Rework diagnostics for wrong number of generic args (fixes #66228 and #71924) This PR reworks the `wrong number of {} arguments` message, so that it provides more details and contextual hints.
This commit is contained in:
commit
a62a76047e
121 changed files with 2787 additions and 766 deletions
|
@ -96,7 +96,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let trait_def = self.tcx().trait_def(trait_def_id);
|
||||
|
||||
if !self.tcx().features().unboxed_closures
|
||||
&& trait_segment.generic_args().parenthesized != trait_def.paren_sugar
|
||||
&& trait_segment.args().parenthesized != trait_def.paren_sugar
|
||||
{
|
||||
let sess = &self.tcx().sess.parse_sess;
|
||||
// For now, require that parenthetical notation be used only with `Fn()` etc.
|
||||
|
@ -126,7 +126,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
})
|
||||
.unwrap_or_else(|| "()".to_string()),
|
||||
trait_segment
|
||||
.generic_args()
|
||||
.args()
|
||||
.bindings
|
||||
.iter()
|
||||
.find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use super::IsMethodCall;
|
||||
use crate::astconv::{
|
||||
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
|
||||
GenericArgCountResult, GenericArgPosition,
|
||||
};
|
||||
use crate::errors::AssocTypeBindingNotAllowed;
|
||||
use crate::structured_errors::{StructuredDiagnostic, WrongNumberOfGenericArgs};
|
||||
use rustc_ast::ast::ParamKindOrd;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
|
||||
use rustc_errors::{struct_span_err, Applicability, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::GenericArg;
|
||||
|
@ -13,7 +15,6 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
|
||||
use rustc_span::{symbol::kw, MultiSpan, Span};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
|
@ -353,20 +354,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
pub fn check_generic_arg_count_for_call(
|
||||
tcx: TyCtxt<'_>,
|
||||
span: Span,
|
||||
def: &ty::Generics,
|
||||
def_id: DefId,
|
||||
generics: &ty::Generics,
|
||||
seg: &hir::PathSegment<'_>,
|
||||
is_method_call: bool,
|
||||
is_method_call: IsMethodCall,
|
||||
) -> GenericArgCountResult {
|
||||
let empty_args = hir::GenericArgs::none();
|
||||
let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def);
|
||||
let suppress_mismatch = Self::check_impl_trait(tcx, seg, &generics);
|
||||
|
||||
let gen_args = seg.args.unwrap_or(&empty_args);
|
||||
let gen_pos = if is_method_call == IsMethodCall::Yes {
|
||||
GenericArgPosition::MethodCall
|
||||
} else {
|
||||
GenericArgPosition::Value
|
||||
};
|
||||
let has_self = generics.parent.is_none() && generics.has_self;
|
||||
let infer_args = seg.infer_args || suppress_mismatch;
|
||||
|
||||
Self::check_generic_arg_count(
|
||||
tcx,
|
||||
span,
|
||||
def,
|
||||
if let Some(ref args) = seg.args { args } else { &empty_args },
|
||||
if is_method_call { GenericArgPosition::MethodCall } else { GenericArgPosition::Value },
|
||||
def.parent.is_none() && def.has_self, // `has_self`
|
||||
seg.infer_args || suppress_mismatch, // `infer_args`
|
||||
tcx, span, def_id, seg, generics, gen_args, gen_pos, has_self, infer_args,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -375,156 +381,109 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
pub(crate) fn check_generic_arg_count(
|
||||
tcx: TyCtxt<'_>,
|
||||
span: Span,
|
||||
def: &ty::Generics,
|
||||
args: &hir::GenericArgs<'_>,
|
||||
position: GenericArgPosition,
|
||||
def_id: DefId,
|
||||
seg: &hir::PathSegment<'_>,
|
||||
gen_params: &ty::Generics,
|
||||
gen_args: &hir::GenericArgs<'_>,
|
||||
gen_pos: GenericArgPosition,
|
||||
has_self: bool,
|
||||
infer_args: bool,
|
||||
) -> GenericArgCountResult {
|
||||
// At this stage we are guaranteed that the generic arguments are in the correct order, e.g.
|
||||
// that lifetimes will proceed types. So it suffices to check the number of each generic
|
||||
// arguments in order to validate them with respect to the generic parameters.
|
||||
let param_counts = def.own_counts();
|
||||
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;
|
||||
let arg_counts = args.own_counts();
|
||||
let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0;
|
||||
let arg_counts = gen_args.own_counts();
|
||||
let infer_lifetimes = gen_pos != GenericArgPosition::Type && arg_counts.lifetimes == 0;
|
||||
|
||||
let mut defaults: ty::GenericParamCount = Default::default();
|
||||
for param in &def.params {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime => {}
|
||||
GenericParamDefKind::Type { has_default, .. } => {
|
||||
defaults.types += has_default as usize
|
||||
}
|
||||
GenericParamDefKind::Const => {
|
||||
// FIXME(const_generics_defaults)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if position != GenericArgPosition::Type && !args.bindings.is_empty() {
|
||||
AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
|
||||
if gen_pos != GenericArgPosition::Type && !gen_args.bindings.is_empty() {
|
||||
Self::prohibit_assoc_ty_binding(tcx, gen_args.bindings[0].span);
|
||||
}
|
||||
|
||||
let explicit_late_bound =
|
||||
Self::prohibit_explicit_late_bound_lifetimes(tcx, def, args, position);
|
||||
Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos);
|
||||
|
||||
let check_kind_count = |kind,
|
||||
required,
|
||||
permitted,
|
||||
provided,
|
||||
offset,
|
||||
unexpected_spans: &mut Vec<Span>,
|
||||
silent| {
|
||||
debug!(
|
||||
"check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}",
|
||||
kind, required, permitted, provided, offset
|
||||
);
|
||||
// We enforce the following: `required` <= `provided` <= `permitted`.
|
||||
// For kinds without defaults (e.g.., lifetimes), `required == permitted`.
|
||||
// For other kinds (i.e., types), `permitted` may be greater than `required`.
|
||||
if required <= provided && provided <= permitted {
|
||||
return true;
|
||||
}
|
||||
let mut invalid_args = vec![];
|
||||
|
||||
if silent {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unfortunately lifetime and type parameter mismatches are typically styled
|
||||
// differently in diagnostics, which means we have a few cases to consider here.
|
||||
let (bound, quantifier) = if required != permitted {
|
||||
if provided < required {
|
||||
(required, "at least ")
|
||||
} else {
|
||||
// provided > permitted
|
||||
(permitted, "at most ")
|
||||
let mut check_generics =
|
||||
|kind, expected_min, expected_max, provided, params_offset, args_offset, silent| {
|
||||
if (expected_min..=expected_max).contains(&provided) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
(required, "")
|
||||
|
||||
if silent {
|
||||
return false;
|
||||
}
|
||||
|
||||
if provided > expected_max {
|
||||
invalid_args.extend(
|
||||
gen_args.args[args_offset + expected_max..args_offset + provided]
|
||||
.iter()
|
||||
.map(|arg| arg.span()),
|
||||
);
|
||||
};
|
||||
|
||||
WrongNumberOfGenericArgs {
|
||||
tcx,
|
||||
kind,
|
||||
expected_min,
|
||||
expected_max,
|
||||
provided,
|
||||
params_offset,
|
||||
args_offset,
|
||||
path_segment: seg,
|
||||
gen_params,
|
||||
gen_args,
|
||||
def_id,
|
||||
span,
|
||||
}
|
||||
.diagnostic()
|
||||
.emit();
|
||||
|
||||
false
|
||||
};
|
||||
|
||||
let (spans, labels) = if provided > permitted {
|
||||
// In the case when the user has provided too many arguments,
|
||||
// we want to point to the unexpected arguments.
|
||||
let (spans, labels): (Vec<Span>, Vec<String>) = args.args
|
||||
[offset + permitted..offset + provided]
|
||||
.iter()
|
||||
.map(|arg| (arg.span(), format!("unexpected {} argument", arg.short_descr())))
|
||||
.unzip();
|
||||
unexpected_spans.extend(spans.clone());
|
||||
(spans, labels)
|
||||
} else {
|
||||
(
|
||||
vec![span],
|
||||
vec![format!(
|
||||
"expected {}{} {} argument{}",
|
||||
quantifier,
|
||||
bound,
|
||||
kind,
|
||||
pluralize!(bound),
|
||||
)],
|
||||
)
|
||||
};
|
||||
|
||||
let mut err = tcx.sess.struct_span_err_with_code(
|
||||
spans.clone(),
|
||||
&format!(
|
||||
"wrong number of {} arguments: expected {}{}, found {}",
|
||||
kind, quantifier, bound, provided,
|
||||
),
|
||||
DiagnosticId::Error("E0107".into()),
|
||||
);
|
||||
for (span, label) in spans.into_iter().zip(labels) {
|
||||
err.span_label(span, label.as_str());
|
||||
}
|
||||
err.emit();
|
||||
false
|
||||
};
|
||||
|
||||
let mut unexpected_spans = vec![];
|
||||
|
||||
let lifetime_count_correct = check_kind_count(
|
||||
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,
|
||||
&mut unexpected_spans,
|
||||
explicit_late_bound == ExplicitLateBound::Yes,
|
||||
);
|
||||
|
||||
let kind_str = if param_counts.consts + arg_counts.consts == 0 {
|
||||
"type"
|
||||
} else if named_type_param_count + arg_counts.types == 0 {
|
||||
"const"
|
||||
} else {
|
||||
"generic"
|
||||
};
|
||||
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 arg_count_correct = check_kind_count(
|
||||
kind_str,
|
||||
if infer_args {
|
||||
let expected_min = if infer_args {
|
||||
0
|
||||
} else {
|
||||
param_counts.consts + named_type_param_count - defaults.types
|
||||
},
|
||||
param_counts.consts + named_type_param_count,
|
||||
arg_counts.consts + arg_counts.types,
|
||||
arg_counts.lifetimes,
|
||||
&mut unexpected_spans,
|
||||
false,
|
||||
);
|
||||
param_counts.consts + named_type_param_count - default_counts.types
|
||||
};
|
||||
|
||||
check_generics(
|
||||
kind,
|
||||
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,
|
||||
)
|
||||
};
|
||||
|
||||
GenericArgCountResult {
|
||||
explicit_late_bound,
|
||||
correct: if lifetime_count_correct && arg_count_correct {
|
||||
correct: if lifetimes_correct && args_correct {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GenericArgCountMismatch {
|
||||
reported: Some(ErrorReported),
|
||||
invalid_args: unexpected_spans,
|
||||
})
|
||||
Err(GenericArgCountMismatch { reported: Some(ErrorReported), invalid_args })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -549,7 +508,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
if explicit && impl_trait {
|
||||
let spans = seg
|
||||
.generic_args()
|
||||
.args()
|
||||
.args
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
|
@ -594,12 +553,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0;
|
||||
|
||||
if infer_lifetimes {
|
||||
ExplicitLateBound::No
|
||||
} else if let Some(span_late) = def.has_late_bound_regions {
|
||||
return ExplicitLateBound::No;
|
||||
}
|
||||
|
||||
if let Some(span_late) = def.has_late_bound_regions {
|
||||
let msg = "cannot specify lifetime arguments explicitly \
|
||||
if late bound lifetime parameters are present";
|
||||
let note = "the late bound lifetime parameter is introduced here";
|
||||
let span = args.args[0].span();
|
||||
|
||||
if position == GenericArgPosition::Value
|
||||
&& arg_counts.lifetimes != param_counts.lifetimes
|
||||
{
|
||||
|
@ -616,6 +578,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|lint| lint.build(msg).emit(),
|
||||
);
|
||||
}
|
||||
|
||||
ExplicitLateBound::Yes
|
||||
} else {
|
||||
ExplicitLateBound::No
|
||||
|
|
|
@ -138,6 +138,12 @@ pub enum ExplicitLateBound {
|
|||
No,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum IsMethodCall {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
/// Denotes the "position" of a generic argument, indicating if it is a generic type,
|
||||
/// generic function or generic method call.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
|
@ -252,7 +258,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
span,
|
||||
def_id,
|
||||
&[],
|
||||
item_segment.generic_args(),
|
||||
item_segment,
|
||||
item_segment.args(),
|
||||
item_segment.infer_args,
|
||||
None,
|
||||
);
|
||||
|
@ -300,6 +307,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
span: Span,
|
||||
def_id: DefId,
|
||||
parent_substs: &[subst::GenericArg<'tcx>],
|
||||
seg: &hir::PathSegment<'_>,
|
||||
generic_args: &'a hir::GenericArgs<'_>,
|
||||
infer_args: bool,
|
||||
self_ty: Option<Ty<'tcx>>,
|
||||
|
@ -314,10 +322,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
);
|
||||
|
||||
let tcx = self.tcx();
|
||||
let generic_params = tcx.generics_of(def_id);
|
||||
let generics = tcx.generics_of(def_id);
|
||||
|
||||
if generic_params.has_self {
|
||||
if generic_params.parent.is_some() {
|
||||
if generics.has_self {
|
||||
if generics.parent.is_some() {
|
||||
// The parent is a trait so it should have at least one subst
|
||||
// for the `Self` type.
|
||||
assert!(!parent_substs.is_empty())
|
||||
|
@ -332,7 +340,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let arg_count = Self::check_generic_arg_count(
|
||||
tcx,
|
||||
span,
|
||||
&generic_params,
|
||||
def_id,
|
||||
seg,
|
||||
&generics,
|
||||
&generic_args,
|
||||
GenericArgPosition::Type,
|
||||
self_ty.is_some(),
|
||||
|
@ -343,7 +353,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
// Traits always have `Self` as a generic parameter, which means they will not return early
|
||||
// here and so associated type bindings will be handled regardless of whether there are any
|
||||
// non-`Self` generic parameters.
|
||||
if generic_params.params.len() == 0 {
|
||||
if generics.params.len() == 0 {
|
||||
return (tcx.intern_substs(&[]), vec![], arg_count);
|
||||
}
|
||||
|
||||
|
@ -553,7 +563,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
debug!(
|
||||
"create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}",
|
||||
generic_params, self_ty, substs
|
||||
generics, self_ty, substs
|
||||
);
|
||||
|
||||
(substs, assoc_bindings, arg_count)
|
||||
|
@ -576,7 +586,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
span,
|
||||
item_def_id,
|
||||
parent_substs,
|
||||
item_segment.generic_args(),
|
||||
item_segment,
|
||||
item_segment.args(),
|
||||
item_segment.infer_args,
|
||||
None,
|
||||
)
|
||||
|
@ -701,8 +712,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
) {
|
||||
let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span));
|
||||
|
||||
let (substs, assoc_bindings, _) =
|
||||
self.create_substs_for_ast_path(span, trait_def_id, &[], args, false, Some(self_ty));
|
||||
let (substs, assoc_bindings, _) = self.create_substs_for_ast_path(
|
||||
span,
|
||||
trait_def_id,
|
||||
&[],
|
||||
&hir::PathSegment::invalid(),
|
||||
args,
|
||||
false,
|
||||
Some(self_ty),
|
||||
);
|
||||
let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs));
|
||||
bounds.trait_bounds.push((poly_trait_ref, span, Constness::NotConst));
|
||||
|
||||
|
@ -750,7 +768,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
span,
|
||||
trait_def_id,
|
||||
&[],
|
||||
trait_segment.generic_args(),
|
||||
trait_segment,
|
||||
trait_segment.args(),
|
||||
trait_segment.infer_args,
|
||||
Some(self_ty),
|
||||
)
|
||||
|
@ -1076,7 +1095,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
dummy_self,
|
||||
&mut bounds,
|
||||
) {
|
||||
potential_assoc_types.extend(cur_potential_assoc_types.into_iter());
|
||||
potential_assoc_types.extend(cur_potential_assoc_types);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1751,7 +1770,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let mut has_err = false;
|
||||
for segment in segments {
|
||||
let (mut err_for_lt, mut err_for_ty, mut err_for_ct) = (false, false, false);
|
||||
for arg in segment.generic_args().args {
|
||||
for arg in segment.args().args {
|
||||
let (span, kind) = match arg {
|
||||
hir::GenericArg::Lifetime(lt) => {
|
||||
if err_for_lt {
|
||||
|
@ -1793,7 +1812,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
}
|
||||
|
||||
// Only emit the first error to avoid overloading the user with error messages.
|
||||
if let [binding, ..] = segment.generic_args().bindings {
|
||||
if let [binding, ..] = segment.args().bindings {
|
||||
has_err = true;
|
||||
Self::prohibit_assoc_ty_binding(self.tcx(), binding.span);
|
||||
}
|
||||
|
@ -2130,6 +2149,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
span,
|
||||
def_id,
|
||||
&[],
|
||||
&hir::PathSegment::invalid(),
|
||||
&GenericArgs::none(),
|
||||
true,
|
||||
None,
|
||||
|
|
|
@ -418,13 +418,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
err.emit();
|
||||
}
|
||||
CastError::SizedUnsizedCast => {
|
||||
use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic};
|
||||
SizedUnsizedCastError::new(
|
||||
&fcx.tcx.sess,
|
||||
self.span,
|
||||
self.expr_ty,
|
||||
fcx.ty_to_string(self.cast_ty),
|
||||
)
|
||||
use crate::structured_errors::{SizedUnsizedCast, StructuredDiagnostic};
|
||||
|
||||
SizedUnsizedCast {
|
||||
sess: &fcx.tcx.sess,
|
||||
span: self.span,
|
||||
expr_ty: self.expr_ty,
|
||||
cast_ty: fcx.ty_to_string(self.cast_ty),
|
||||
}
|
||||
.diagnostic()
|
||||
.emit();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::astconv::{
|
||||
AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
|
||||
GenericArgCountResult, PathSeg,
|
||||
GenericArgCountResult, IsMethodCall, PathSeg,
|
||||
};
|
||||
use crate::check::callee::{self, DeferredCallResolution};
|
||||
use crate::check::method::{self, MethodCallee, SelfSource};
|
||||
|
@ -1219,18 +1219,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// a problem.
|
||||
|
||||
let mut infer_args_for_err = FxHashSet::default();
|
||||
|
||||
for &PathSeg(def_id, index) in &path_segs {
|
||||
let seg = &segments[index];
|
||||
let generics = tcx.generics_of(def_id);
|
||||
|
||||
// Argument-position `impl Trait` is treated as a normal generic
|
||||
// parameter internally, but we don't allow users to specify the
|
||||
// parameter's value explicitly, so we have to do some error-
|
||||
// checking here.
|
||||
if let GenericArgCountResult {
|
||||
correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }),
|
||||
correct: Err(GenericArgCountMismatch { reported: Some(_), .. }),
|
||||
..
|
||||
} = AstConv::check_generic_arg_count_for_call(
|
||||
tcx, span, &generics, &seg, false, // `is_method_call`
|
||||
tcx,
|
||||
span,
|
||||
def_id,
|
||||
&generics,
|
||||
seg,
|
||||
IsMethodCall::No,
|
||||
) {
|
||||
infer_args_for_err.insert(index);
|
||||
self.set_tainted_by_errors(); // See issue #53251.
|
||||
|
|
|
@ -22,6 +22,7 @@ use rustc_span::symbol::{sym, Ident};
|
|||
use rustc_span::{self, MultiSpan, Span};
|
||||
use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression};
|
||||
|
||||
use crate::structured_errors::StructuredDiagnostic;
|
||||
use std::mem::replace;
|
||||
use std::slice;
|
||||
|
||||
|
@ -173,18 +174,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
if let Some(def_id) = def_id {
|
||||
if let Some(node) = tcx.hir().get_if_local(def_id) {
|
||||
let mut spans: MultiSpan = node
|
||||
.ident()
|
||||
.map(|ident| ident.span)
|
||||
.unwrap_or_else(|| tcx.hir().span(node.hir_id().unwrap()))
|
||||
.into();
|
||||
if let Some(def_span) = tcx.def_ident_span(def_id) {
|
||||
let mut spans: MultiSpan = def_span.into();
|
||||
|
||||
if let Some(id) = node.body_id() {
|
||||
let body = tcx.hir().body(id);
|
||||
for param in body.params {
|
||||
spans.push_span_label(param.span, String::new());
|
||||
}
|
||||
let params = tcx
|
||||
.hir()
|
||||
.get_if_local(def_id)
|
||||
.and_then(|node| node.body_id())
|
||||
.into_iter()
|
||||
.map(|id| tcx.hir().body(id).params)
|
||||
.flatten();
|
||||
|
||||
for param in params {
|
||||
spans.push_span_label(param.span, String::new());
|
||||
}
|
||||
|
||||
let def_kind = tcx.def_kind(def_id);
|
||||
|
@ -358,9 +360,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// We also need to make sure we at least write the ty of the other
|
||||
// arguments which we skipped above.
|
||||
if c_variadic {
|
||||
fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
|
||||
use crate::structured_errors::{StructuredDiagnostic, VariadicError};
|
||||
VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
|
||||
fn variadic_error<'tcx>(sess: &Session, span: Span, ty: Ty<'tcx>, cast_ty: &str) {
|
||||
use crate::structured_errors::MissingCastForVariadicArg;
|
||||
|
||||
MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit()
|
||||
}
|
||||
|
||||
for arg in args.iter().skip(expected_arg_count) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{probe, MethodCallee};
|
||||
|
||||
use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt};
|
||||
use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
|
||||
use crate::check::{callee, FnCtxt};
|
||||
use crate::hir::def_id::DefId;
|
||||
use crate::hir::GenericArg;
|
||||
|
@ -298,8 +298,14 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
|||
// If they were not explicitly supplied, just construct fresh
|
||||
// variables.
|
||||
let generics = self.tcx.generics_of(pick.item.def_id);
|
||||
|
||||
let arg_count_correct = AstConv::check_generic_arg_count_for_call(
|
||||
self.tcx, self.span, &generics, &seg, true, // `is_method_call`
|
||||
self.tcx,
|
||||
self.span,
|
||||
pick.item.def_id,
|
||||
&generics,
|
||||
seg,
|
||||
IsMethodCall::Yes,
|
||||
);
|
||||
|
||||
// Create subst for early-bound lifetime parameters, combining
|
||||
|
|
|
@ -1498,13 +1498,11 @@ fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
|
|||
Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty),
|
||||
OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args),
|
||||
Path(hir::QPath::TypeRelative(ty, segment)) => {
|
||||
is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.generic_args().args)
|
||||
is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args)
|
||||
}
|
||||
Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => {
|
||||
ty_opt.map_or(false, is_suggestable_infer_ty)
|
||||
|| segments
|
||||
.iter()
|
||||
.any(|segment| are_suggestable_generic_args(segment.generic_args().args))
|
||||
|| segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
@ -1,149 +1,36 @@
|
|||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_middle::ty::{Ty, TypeFoldable};
|
||||
mod missing_cast_for_variadic_arg;
|
||||
mod sized_unsized_cast;
|
||||
mod wrong_number_of_generic_args;
|
||||
|
||||
pub use self::{
|
||||
missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*,
|
||||
};
|
||||
|
||||
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
|
||||
pub trait StructuredDiagnostic<'tcx> {
|
||||
fn session(&self) -> &Session;
|
||||
|
||||
fn code(&self) -> DiagnosticId;
|
||||
|
||||
fn common(&self) -> DiagnosticBuilder<'tcx>;
|
||||
|
||||
fn diagnostic(&self) -> DiagnosticBuilder<'tcx> {
|
||||
let err = self.common();
|
||||
if self.session().teach(&self.code()) { self.extended(err) } else { self.regular(err) }
|
||||
}
|
||||
let err = self.diagnostic_common();
|
||||
|
||||
fn regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
|
||||
err
|
||||
}
|
||||
|
||||
fn extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VariadicError<'tcx> {
|
||||
sess: &'tcx Session,
|
||||
span: Span,
|
||||
t: Ty<'tcx>,
|
||||
cast_ty: &'tcx str,
|
||||
}
|
||||
|
||||
impl<'tcx> VariadicError<'tcx> {
|
||||
pub fn new(
|
||||
sess: &'tcx Session,
|
||||
span: Span,
|
||||
t: Ty<'tcx>,
|
||||
cast_ty: &'tcx str,
|
||||
) -> VariadicError<'tcx> {
|
||||
VariadicError { sess, span, t, cast_ty }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> StructuredDiagnostic<'tcx> for VariadicError<'tcx> {
|
||||
fn session(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
fn code(&self) -> DiagnosticId {
|
||||
rustc_errors::error_code!(E0617)
|
||||
}
|
||||
|
||||
fn common(&self) -> DiagnosticBuilder<'tcx> {
|
||||
let mut err = if self.t.references_error() {
|
||||
self.sess.diagnostic().struct_dummy()
|
||||
if self.session().teach(&self.code()) {
|
||||
self.diagnostic_extended(err)
|
||||
} else {
|
||||
self.sess.struct_span_fatal_with_code(
|
||||
self.span,
|
||||
&format!("can't pass `{}` to variadic function", self.t),
|
||||
self.code(),
|
||||
)
|
||||
};
|
||||
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 extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
|
||||
err.note(&format!(
|
||||
"certain types, like `{}`, must be cast before passing them to a \
|
||||
variadic function, because of arcane ABI rules dictated by the C \
|
||||
standard",
|
||||
self.t
|
||||
));
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SizedUnsizedCastError<'tcx> {
|
||||
sess: &'tcx Session,
|
||||
span: Span,
|
||||
expr_ty: Ty<'tcx>,
|
||||
cast_ty: String,
|
||||
}
|
||||
|
||||
impl<'tcx> SizedUnsizedCastError<'tcx> {
|
||||
pub fn new(
|
||||
sess: &'tcx Session,
|
||||
span: Span,
|
||||
expr_ty: Ty<'tcx>,
|
||||
cast_ty: String,
|
||||
) -> SizedUnsizedCastError<'tcx> {
|
||||
SizedUnsizedCastError { sess, span, expr_ty, cast_ty }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCastError<'tcx> {
|
||||
fn session(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
fn code(&self) -> DiagnosticId {
|
||||
rustc_errors::error_code!(E0607)
|
||||
}
|
||||
|
||||
fn common(&self) -> DiagnosticBuilder<'tcx> {
|
||||
if self.expr_ty.references_error() {
|
||||
self.sess.diagnostic().struct_dummy()
|
||||
} else {
|
||||
self.sess.struct_span_fatal_with_code(
|
||||
self.span,
|
||||
&format!(
|
||||
"cannot cast thin pointer `{}` to fat pointer `{}`",
|
||||
self.expr_ty, self.cast_ty
|
||||
),
|
||||
self.code(),
|
||||
)
|
||||
self.diagnostic_regular(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
|
||||
err.help(
|
||||
"Thin pointers are \"simple\" pointers: they are purely a reference to a
|
||||
memory address.
|
||||
fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx>;
|
||||
|
||||
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.
|
||||
fn diagnostic_regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
|
||||
err
|
||||
}
|
||||
|
||||
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",
|
||||
);
|
||||
fn diagnostic_extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
|
||||
err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
use crate::structured_errors::StructuredDiagnostic;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_middle::ty::{Ty, TypeFoldable};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
|
||||
pub struct MissingCastForVariadicArg<'tcx> {
|
||||
pub sess: &'tcx Session,
|
||||
pub span: Span,
|
||||
pub ty: Ty<'tcx>,
|
||||
pub cast_ty: &'tcx 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> {
|
||||
let mut err = if self.ty.references_error() {
|
||||
self.sess.diagnostic().struct_dummy()
|
||||
} else {
|
||||
self.sess.struct_span_fatal_with_code(
|
||||
self.span,
|
||||
&format!("can't pass `{}` to variadic function", self.ty),
|
||||
self.code(),
|
||||
)
|
||||
};
|
||||
|
||||
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>) -> DiagnosticBuilder<'tcx> {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
use crate::structured_errors::StructuredDiagnostic;
|
||||
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_middle::ty::{Ty, TypeFoldable};
|
||||
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> {
|
||||
if self.expr_ty.references_error() {
|
||||
self.sess.diagnostic().struct_dummy()
|
||||
} else {
|
||||
self.sess.struct_span_fatal_with_code(
|
||||
self.span,
|
||||
&format!(
|
||||
"cannot cast thin pointer `{}` to fat pointer `{}`",
|
||||
self.expr_ty, self.cast_ty
|
||||
),
|
||||
self.code(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn diagnostic_extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,393 @@
|
|||
use crate::structured_errors::StructuredDiagnostic;
|
||||
use hir::def::DefKind;
|
||||
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::{self as ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{def_id::DefId, MultiSpan};
|
||||
|
||||
/// 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,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// Offending path segment
|
||||
crate path_segment: &'a hir::PathSegment<'a>,
|
||||
|
||||
/// Generic parameters as expected by type or trait
|
||||
crate gen_params: &'a ty::Generics,
|
||||
|
||||
/// 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)
|
||||
} else {
|
||||
("at most ", self.expected_max)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
err.span_label(
|
||||
self.path_segment.ident.span,
|
||||
format!(
|
||||
"expected {}{} {} argument{}",
|
||||
quantifier,
|
||||
bound,
|
||||
self.kind,
|
||||
pluralize!(bound),
|
||||
),
|
||||
);
|
||||
|
||||
// When user's provided too many arguments, 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.provided > self.expected_max {
|
||||
return;
|
||||
}
|
||||
|
||||
let args = self.gen_args.args.iter().skip(self.args_offset).take(self.provided).enumerate();
|
||||
|
||||
for (i, arg) in args {
|
||||
err.span_label(
|
||||
arg.span(),
|
||||
if i + 1 == self.provided {
|
||||
format!(
|
||||
"supplied {} {} argument{}",
|
||||
self.provided,
|
||||
self.kind,
|
||||
pluralize!(self.provided)
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} 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:
|
||||
///
|
||||
/// ```text
|
||||
/// 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;
|
||||
|
||||
let (span, sugg_prefix) = if self.args_offset + self.provided == 0 {
|
||||
let span = self.gen_args.args[0].span().shrink_to_lo();
|
||||
(span, "")
|
||||
} else {
|
||||
let span =
|
||||
self.gen_args.args[self.args_offset + self.provided - 1].span().shrink_to_hi();
|
||||
(span, ", ")
|
||||
};
|
||||
|
||||
let msg = format!("add missing {} argument{}", self.kind, pluralize!(missing_arg_count));
|
||||
|
||||
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(", ");
|
||||
|
||||
let sugg = format!("{}{}", sugg_prefix, sugg);
|
||||
|
||||
err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders);
|
||||
}
|
||||
|
||||
/// Suggests to remove redundant argument(s):
|
||||
///
|
||||
/// ```text
|
||||
/// type Map = HashMap<String, String, String, String>;
|
||||
/// ```
|
||||
fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
assert!(self.provided > 0);
|
||||
|
||||
let redundant_args_count = self.provided - self.expected_max;
|
||||
let remove_entire_generics = redundant_args_count >= self.gen_args.args.len();
|
||||
|
||||
let (span, msg) = if remove_entire_generics {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
|
||||
let span = self
|
||||
.path_segment
|
||||
.args
|
||||
.unwrap()
|
||||
.span_ext(sm)
|
||||
.unwrap()
|
||||
.with_lo(self.path_segment.ident.span.hi());
|
||||
|
||||
let msg = format!(
|
||||
"remove these {}generics",
|
||||
if self.gen_args.parenthesized { "parenthetical " } else { "" },
|
||||
);
|
||||
|
||||
(span, msg)
|
||||
} 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);
|
||||
}
|
||||
|
||||
/// Builds the `type defined here` message.
|
||||
fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
|
||||
def_span.into()
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let msg = {
|
||||
let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
|
||||
let (quantifier, bound) = self.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, String::new());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
let mut err = self.start_diagnostics();
|
||||
|
||||
self.notify(&mut err);
|
||||
self.suggest(&mut err);
|
||||
self.show_definition(&mut err);
|
||||
|
||||
err
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue