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
|
@ -242,12 +242,21 @@ impl Into<Option<P<GenericArgs>>> for ParenthesizedArgs {
|
|||
/// A path like `Foo(A, B) -> C`.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct ParenthesizedArgs {
|
||||
/// Overall span
|
||||
/// ```text
|
||||
/// Foo(A, B) -> C
|
||||
/// ^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
pub span: Span,
|
||||
|
||||
/// `(A, B)`
|
||||
pub inputs: Vec<P<Ty>>,
|
||||
|
||||
/// ```text
|
||||
/// Foo(A, B) -> C
|
||||
/// ^^^^^^
|
||||
/// ```
|
||||
pub inputs_span: Span,
|
||||
|
||||
/// `C`
|
||||
pub output: FnRetTy,
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#![feature(const_fn_transmute)]
|
||||
#![feature(const_panic)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(iterator_fold_self)]
|
||||
#![feature(label_break_value)]
|
||||
#![feature(nll)]
|
||||
#![feature(or_patterns)]
|
||||
|
|
|
@ -565,7 +565,7 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
|
|||
args: &mut ParenthesizedArgs,
|
||||
vis: &mut T,
|
||||
) {
|
||||
let ParenthesizedArgs { inputs, output, span } = args;
|
||||
let ParenthesizedArgs { inputs, output, span, .. } = args;
|
||||
visit_vec(inputs, |input| vis.visit_ty(input));
|
||||
noop_visit_fn_ret_ty(output, vis);
|
||||
vis.visit_span(span);
|
||||
|
|
|
@ -401,15 +401,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// compatibility, even in contexts like an impl header where
|
||||
// we generally don't permit such things (see #51008).
|
||||
self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
|
||||
let &ParenthesizedArgs { ref inputs, ref output, span } = data;
|
||||
let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
|
||||
let inputs = this.arena.alloc_from_iter(
|
||||
inputs.iter().map(|ty| this.lower_ty_direct(ty, ImplTraitContext::disallowed())),
|
||||
);
|
||||
let output_ty = match output {
|
||||
FnRetTy::Ty(ty) => this.lower_ty(&ty, ImplTraitContext::disallowed()),
|
||||
FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(span, &[])),
|
||||
FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(*span, &[])),
|
||||
};
|
||||
let args = smallvec![GenericArg::Type(this.ty_tup(span, inputs))];
|
||||
let args = smallvec![GenericArg::Type(this.ty_tup(*inputs_span, inputs))];
|
||||
let binding = this.output_ty_binding(output_ty.span, output_ty);
|
||||
(
|
||||
GenericArgsCtor { args, bindings: arena_vec![this; binding], parenthesized: true },
|
||||
|
|
|
@ -11,9 +11,9 @@ pub use rustc_ast::{CaptureBy, Movability, Mutability};
|
|||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use rustc_data_structures::sync::{par_for_each_in, Send, Sync};
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::source_map::{SourceMap, Spanned};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{def_id::LocalDefId, BytePos};
|
||||
use rustc_span::{MultiSpan, Span, DUMMY_SP};
|
||||
use rustc_target::asm::InlineAsmRegOrRegClass;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
@ -231,7 +231,11 @@ impl<'hir> PathSegment<'hir> {
|
|||
PathSegment { ident, hir_id: None, res: None, infer_args: true, args: None }
|
||||
}
|
||||
|
||||
pub fn generic_args(&self) -> &GenericArgs<'hir> {
|
||||
pub fn invalid() -> Self {
|
||||
Self::from_ident(Ident::invalid())
|
||||
}
|
||||
|
||||
pub fn args(&self) -> &GenericArgs<'hir> {
|
||||
if let Some(ref args) = self.args {
|
||||
args
|
||||
} else {
|
||||
|
@ -275,6 +279,10 @@ impl GenericArg<'_> {
|
|||
matches!(self, GenericArg::Const(_))
|
||||
}
|
||||
|
||||
pub fn is_synthetic(&self) -> bool {
|
||||
matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::invalid())
|
||||
}
|
||||
|
||||
pub fn descr(&self) -> &'static str {
|
||||
match self {
|
||||
GenericArg::Lifetime(_) => "lifetime",
|
||||
|
@ -283,14 +291,6 @@ impl GenericArg<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn short_descr(&self) -> &'static str {
|
||||
match self {
|
||||
GenericArg::Lifetime(_) => "lifetime",
|
||||
GenericArg::Type(_) => "type",
|
||||
GenericArg::Const(_) => "const",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ord(&self, feats: &rustc_feature::Features) -> ast::ParamKindOrd {
|
||||
match self {
|
||||
GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime,
|
||||
|
@ -352,6 +352,39 @@ impl GenericArgs<'_> {
|
|||
|
||||
own_counts
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
self.args
|
||||
.iter()
|
||||
.filter(|arg| !arg.is_synthetic())
|
||||
.map(|arg| arg.span())
|
||||
.fold_first(|span1, span2| span1.to(span2))
|
||||
}
|
||||
|
||||
/// Returns span encompassing arguments and their surrounding `<>` or `()`
|
||||
pub fn span_ext(&self, sm: &SourceMap) -> Option<Span> {
|
||||
let mut span = self.span()?;
|
||||
|
||||
let (o, c) = if self.parenthesized { ('(', ')') } else { ('<', '>') };
|
||||
|
||||
if let Ok(snippet) = sm.span_to_snippet(span) {
|
||||
let snippet = snippet.as_bytes();
|
||||
|
||||
if snippet[0] != (o as u8) || snippet[snippet.len() - 1] != (c as u8) {
|
||||
span = sm.span_extend_to_prev_char(span, o, true);
|
||||
span = span.with_lo(span.lo() - BytePos(1));
|
||||
|
||||
span = sm.span_extend_to_next_char(span, c, true);
|
||||
span = span.with_hi(span.hi() + BytePos(1));
|
||||
}
|
||||
}
|
||||
|
||||
Some(span)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.args.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// A modifier on a bound, currently this is only used for `?Sized`, where the
|
||||
|
|
|
@ -157,7 +157,7 @@ where
|
|||
}
|
||||
|
||||
language_item_table! {
|
||||
// Variant name, Name, Method name, Target;
|
||||
// Variant name, Name, Method name, Target;
|
||||
Bool, sym::bool, bool_impl, Target::Impl;
|
||||
Char, sym::char, char_impl, Target::Impl;
|
||||
Str, sym::str, str_impl, Target::Impl;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#![feature(const_fn)] // For the unsizing cast on `&[]`
|
||||
#![feature(const_panic)]
|
||||
#![feature(in_band_lifetimes)]
|
||||
#![feature(iterator_fold_self)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(or_patterns)]
|
||||
#![recursion_limit = "256"]
|
||||
|
|
|
@ -1213,7 +1213,7 @@ impl<'a> State<'a> {
|
|||
self.s.word(".");
|
||||
self.print_ident(segment.ident);
|
||||
|
||||
let generic_args = segment.generic_args();
|
||||
let generic_args = segment.args();
|
||||
if !generic_args.args.is_empty() || !generic_args.bindings.is_empty() {
|
||||
self.print_generic_args(generic_args, segment.infer_args, true);
|
||||
}
|
||||
|
@ -1661,11 +1661,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
if segment.ident.name != kw::PathRoot {
|
||||
self.print_ident(segment.ident);
|
||||
self.print_generic_args(
|
||||
segment.generic_args(),
|
||||
segment.infer_args,
|
||||
colons_before_params,
|
||||
);
|
||||
self.print_generic_args(segment.args(), segment.infer_args, colons_before_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1673,7 +1669,7 @@ impl<'a> State<'a> {
|
|||
pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
|
||||
if segment.ident.name != kw::PathRoot {
|
||||
self.print_ident(segment.ident);
|
||||
self.print_generic_args(segment.generic_args(), segment.infer_args, false);
|
||||
self.print_generic_args(segment.args(), segment.infer_args, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1693,7 +1689,7 @@ impl<'a> State<'a> {
|
|||
if segment.ident.name != kw::PathRoot {
|
||||
self.print_ident(segment.ident);
|
||||
self.print_generic_args(
|
||||
segment.generic_args(),
|
||||
segment.args(),
|
||||
segment.infer_args,
|
||||
colons_before_params,
|
||||
);
|
||||
|
@ -1705,7 +1701,7 @@ impl<'a> State<'a> {
|
|||
let item_segment = path.segments.last().unwrap();
|
||||
self.print_ident(item_segment.ident);
|
||||
self.print_generic_args(
|
||||
item_segment.generic_args(),
|
||||
item_segment.args(),
|
||||
item_segment.infer_args,
|
||||
colons_before_params,
|
||||
)
|
||||
|
@ -1725,7 +1721,7 @@ impl<'a> State<'a> {
|
|||
self.s.word("::");
|
||||
self.print_ident(item_segment.ident);
|
||||
self.print_generic_args(
|
||||
item_segment.generic_args(),
|
||||
item_segment.args(),
|
||||
item_segment.infer_args,
|
||||
colons_before_params,
|
||||
)
|
||||
|
|
|
@ -721,21 +721,25 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
&self.raw_proc_macros.unwrap()[pos]
|
||||
}
|
||||
|
||||
fn item_ident(&self, item_index: DefIndex, sess: &Session) -> Ident {
|
||||
fn try_item_ident(&self, item_index: DefIndex, sess: &Session) -> Result<Ident, String> {
|
||||
let name = self
|
||||
.def_key(item_index)
|
||||
.disambiguated_data
|
||||
.data
|
||||
.get_opt_name()
|
||||
.expect("no name in item_ident");
|
||||
.ok_or_else(|| format!("Missing opt name for {:?}", item_index))?;
|
||||
let span = self
|
||||
.root
|
||||
.tables
|
||||
.ident_span
|
||||
.get(self, item_index)
|
||||
.map(|data| data.decode((self, sess)))
|
||||
.unwrap_or_else(|| panic!("Missing ident span for {:?} ({:?})", name, item_index));
|
||||
Ident::new(name, span)
|
||||
.ok_or_else(|| format!("Missing ident span for {:?} ({:?})", name, item_index))?
|
||||
.decode((self, sess));
|
||||
Ok(Ident::new(name, span))
|
||||
}
|
||||
|
||||
fn item_ident(&self, item_index: DefIndex, sess: &Session) -> Ident {
|
||||
self.try_item_ident(item_index, sess).unwrap()
|
||||
}
|
||||
|
||||
fn def_kind(&self, index: DefIndex) -> DefKind {
|
||||
|
|
|
@ -129,6 +129,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
|
|||
generator_kind => { cdata.generator_kind(def_id.index) }
|
||||
def_kind => { cdata.def_kind(def_id.index) }
|
||||
def_span => { cdata.get_span(def_id.index, &tcx.sess) }
|
||||
def_ident_span => {
|
||||
cdata.try_item_ident(def_id.index, &tcx.sess).ok().map(|ident| ident.span)
|
||||
}
|
||||
lookup_stability => {
|
||||
cdata.get_stability(def_id.index).map(|s| tcx.intern_stability(s))
|
||||
}
|
||||
|
|
|
@ -881,6 +881,7 @@ rustc_queries! {
|
|||
query def_kind(def_id: DefId) -> DefKind {
|
||||
desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
query def_span(def_id: DefId) -> Span {
|
||||
desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) }
|
||||
// FIXME(mw): DefSpans are not really inputs since they are derived from
|
||||
|
@ -890,15 +891,23 @@ rustc_queries! {
|
|||
// regardless of HIR hashing.
|
||||
eval_always
|
||||
}
|
||||
|
||||
query def_ident_span(def_id: DefId) -> Option<Span> {
|
||||
desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> {
|
||||
desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> {
|
||||
desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
query lookup_deprecation_entry(def_id: DefId) -> Option<DeprecationEntry> {
|
||||
desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
||||
query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] {
|
||||
desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
|
|
@ -847,7 +847,7 @@ fn foo(&self) -> Self::T { String::new() }
|
|||
}
|
||||
|
||||
/// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
|
||||
/// requirement, provide a strucuted suggestion to constrain it to a given type `ty`.
|
||||
/// requirement, provide a structured suggestion to constrain it to a given type `ty`.
|
||||
fn constrain_generic_bound_associated_type_structured_suggestion(
|
||||
self,
|
||||
db: &mut DiagnosticBuilder<'_>,
|
||||
|
|
|
@ -871,19 +871,37 @@ impl<'tcx> Generics {
|
|||
// We could cache this as a property of `GenericParamCount`, but
|
||||
// the aim is to refactor this away entirely eventually and the
|
||||
// presence of this method will be a constant reminder.
|
||||
let mut own_counts: GenericParamCount = Default::default();
|
||||
let mut own_counts = GenericParamCount::default();
|
||||
|
||||
for param in &self.params {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime => own_counts.lifetimes += 1,
|
||||
GenericParamDefKind::Type { .. } => own_counts.types += 1,
|
||||
GenericParamDefKind::Const => own_counts.consts += 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
own_counts
|
||||
}
|
||||
|
||||
pub fn own_defaults(&self) -> GenericParamCount {
|
||||
let mut own_defaults = GenericParamCount::default();
|
||||
|
||||
for param in &self.params {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime => (),
|
||||
GenericParamDefKind::Type { has_default, .. } => {
|
||||
own_defaults.types += has_default as usize;
|
||||
}
|
||||
GenericParamDefKind::Const => {
|
||||
// FIXME(const_generics:defaults)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
own_defaults
|
||||
}
|
||||
|
||||
pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool {
|
||||
if self.own_requires_monomorphization() {
|
||||
return true;
|
||||
|
|
|
@ -230,10 +230,11 @@ impl<'a> Parser<'a> {
|
|||
} else {
|
||||
// `(T, U) -> R`
|
||||
let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?;
|
||||
let inputs_span = lo.to(self.prev_token.span);
|
||||
let span = ident.span.to(self.prev_token.span);
|
||||
let output =
|
||||
self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?;
|
||||
ParenthesizedArgs { inputs, output, span }.into()
|
||||
ParenthesizedArgs { span, inputs, inputs_span, output }.into()
|
||||
};
|
||||
|
||||
PathSegment { ident, args, id: ast::DUMMY_NODE_ID }
|
||||
|
|
|
@ -1433,7 +1433,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
hir::TyKind::Path(ref qpath) => {
|
||||
if let QPath::Resolved(_, path) = qpath {
|
||||
let last_segment = &path.segments[path.segments.len() - 1];
|
||||
let generics = last_segment.generic_args();
|
||||
let generics = last_segment.args();
|
||||
for arg in generics.args.iter() {
|
||||
if let GenericArg::Lifetime(lt) = arg {
|
||||
if lt.name.ident() == name {
|
||||
|
|
|
@ -582,9 +582,9 @@ impl SourceMap {
|
|||
/// Extracts the source surrounding the given `Span` using the `extract_source` function. The
|
||||
/// extract function takes three arguments: a string slice containing the source, an index in
|
||||
/// the slice for the beginning of the span and an index in the slice for the end of the span.
|
||||
fn span_to_source<F>(&self, sp: Span, extract_source: F) -> Result<String, SpanSnippetError>
|
||||
fn span_to_source<F, T>(&self, sp: Span, extract_source: F) -> Result<T, SpanSnippetError>
|
||||
where
|
||||
F: Fn(&str, usize, usize) -> Result<String, SpanSnippetError>,
|
||||
F: Fn(&str, usize, usize) -> Result<T, SpanSnippetError>,
|
||||
{
|
||||
let local_begin = self.lookup_byte_offset(sp.lo());
|
||||
let local_end = self.lookup_byte_offset(sp.hi());
|
||||
|
@ -648,10 +648,10 @@ impl SourceMap {
|
|||
|
||||
/// Extends the given `Span` to just after the previous occurrence of `c`. Return the same span
|
||||
/// if no character could be found or if an error occurred while retrieving the code snippet.
|
||||
pub fn span_extend_to_prev_char(&self, sp: Span, c: char) -> Span {
|
||||
pub fn span_extend_to_prev_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span {
|
||||
if let Ok(prev_source) = self.span_to_prev_source(sp) {
|
||||
let prev_source = prev_source.rsplit(c).next().unwrap_or("").trim_start();
|
||||
if !prev_source.is_empty() && !prev_source.contains('\n') {
|
||||
let prev_source = prev_source.rsplit(c).next().unwrap_or("");
|
||||
if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) {
|
||||
return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
|
||||
}
|
||||
}
|
||||
|
@ -682,6 +682,25 @@ impl SourceMap {
|
|||
sp
|
||||
}
|
||||
|
||||
/// Returns the source snippet as `String` after the given `Span`.
|
||||
pub fn span_to_next_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
|
||||
self.span_to_source(sp, |src, _, end_index| {
|
||||
src.get(end_index..).map(|s| s.to_string()).ok_or(SpanSnippetError::IllFormedSpan(sp))
|
||||
})
|
||||
}
|
||||
|
||||
/// Extends the given `Span` to just after the next occurrence of `c`.
|
||||
pub fn span_extend_to_next_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span {
|
||||
if let Ok(next_source) = self.span_to_next_source(sp) {
|
||||
let next_source = next_source.split(c).next().unwrap_or("");
|
||||
if !next_source.is_empty() && (!next_source.contains('\n') || accept_newlines) {
|
||||
return sp.with_hi(BytePos(sp.hi().0 + next_source.len() as u32));
|
||||
}
|
||||
}
|
||||
|
||||
sp
|
||||
}
|
||||
|
||||
/// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char`
|
||||
/// `c`.
|
||||
pub fn span_until_char(&self, sp: Span, c: char) -> Span {
|
||||
|
|
|
@ -222,6 +222,10 @@ fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {
|
|||
tcx.hir().span_if_local(def_id).unwrap()
|
||||
}
|
||||
|
||||
fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
|
||||
tcx.hir().get_if_local(def_id).and_then(|node| node.ident()).map(|ident| ident.span)
|
||||
}
|
||||
|
||||
/// If the given `DefId` describes an item belonging to a trait,
|
||||
/// returns the `DefId` of the trait that the trait item belongs to;
|
||||
/// otherwise, returns `None`.
|
||||
|
@ -492,6 +496,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
|||
associated_items,
|
||||
adt_sized_constraint,
|
||||
def_span,
|
||||
def_ident_span,
|
||||
param_env,
|
||||
param_env_reveal_all_normalized,
|
||||
trait_of_item,
|
||||
|
|
|
@ -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