1
Fork 0

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:
bors 2021-01-13 20:35:58 +00:00
commit a62a76047e
121 changed files with 2787 additions and 766 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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