1
Fork 0

Track HirId instead of Span in ObligationCauseCode::SizedArgumentType

This gets us more accurate suggestions.
This commit is contained in:
Esteban Küber 2023-12-20 19:13:24 +00:00
parent 8551cab7b7
commit 79bef72fd5
8 changed files with 96 additions and 82 deletions

View file

@ -114,7 +114,8 @@ pub(super) fn check_fn<'a, 'tcx>(
let inputs_fn = fn_sig.inputs().iter().copied(); let inputs_fn = fn_sig.inputs().iter().copied();
for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
// Check the pattern. // Check the pattern.
let ty_span = try { inputs_hir?.get(idx)?.span }; let ty: Option<&hir::Ty<'_>> = try { inputs_hir?.get(idx)? };
let ty_span = ty.map(|ty| ty.span);
fcx.check_pat_top(param.pat, param_ty, ty_span, None, None); fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
// Check that argument is Sized. // Check that argument is Sized.
@ -122,13 +123,13 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.require_type_is_sized( fcx.require_type_is_sized(
param_ty, param_ty,
param.pat.span, param.pat.span,
// ty_span == binding_span iff this is a closure parameter with no type ascription, // ty.span == binding_span iff this is a closure parameter with no type ascription,
// or if it's an implicit `self` parameter // or if it's an implicit `self` parameter
traits::SizedArgumentType( traits::SizedArgumentType(
if ty_span == Some(param.span) && tcx.is_closure(fn_def_id.into()) { if ty_span == Some(param.span) && tcx.is_closure(fn_def_id.into()) {
None None
} else { } else {
ty_span ty.map(|ty| ty.hir_id)
}, },
), ),
); );

View file

@ -60,7 +60,7 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
// parameters are special cases of patterns, but we want to handle them as // parameters are special cases of patterns, but we want to handle them as
// *distinct* cases. so track when we are hitting a pattern *within* an fn // *distinct* cases. so track when we are hitting a pattern *within* an fn
// parameter. // parameter.
outermost_fn_param_pat: Option<Span>, outermost_fn_param_pat: Option<(Span, hir::HirId)>,
} }
impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
@ -131,7 +131,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
} }
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span); let old_outermost_fn_param_pat =
self.outermost_fn_param_pat.replace((param.ty_span, param.hir_id));
intravisit::walk_param(self, param); intravisit::walk_param(self, param);
self.outermost_fn_param_pat = old_outermost_fn_param_pat; self.outermost_fn_param_pat = old_outermost_fn_param_pat;
} }
@ -141,7 +142,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
if let PatKind::Binding(_, _, ident, _) = p.kind { if let PatKind::Binding(_, _, ident, _) = p.kind {
let var_ty = self.assign(p.span, p.hir_id, None); let var_ty = self.assign(p.span, p.hir_id, None);
if let Some(ty_span) = self.outermost_fn_param_pat { if let Some((ty_span, hir_id)) = self.outermost_fn_param_pat {
if !self.fcx.tcx.features().unsized_fn_params { if !self.fcx.tcx.features().unsized_fn_params {
self.fcx.require_type_is_sized( self.fcx.require_type_is_sized(
var_ty, var_ty,
@ -154,7 +155,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
{ {
None None
} else { } else {
Some(ty_span) Some(hir_id)
}, },
), ),
); );

View file

@ -289,7 +289,7 @@ pub enum ObligationCauseCode<'tcx> {
/// Type of each variable must be `Sized`. /// Type of each variable must be `Sized`.
VariableType(hir::HirId), VariableType(hir::HirId),
/// Argument type must be `Sized`. /// Argument type must be `Sized`.
SizedArgumentType(Option<Span>), SizedArgumentType(Option<hir::HirId>),
/// Return type must be `Sized`. /// Return type must be `Sized`.
SizedReturnType, SizedReturnType,
/// Yield type must be `Sized`. /// Yield type must be `Sized`.

View file

@ -19,11 +19,10 @@ use rustc_errors::{
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::{Map, Visitor};
use rustc_hir::is_range_literal; use rustc_hir::is_range_literal;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Node}; use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
use rustc_hir::{Expr, HirId};
use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
@ -3200,63 +3199,80 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.help("unsized locals are gated as an unstable feature"); err.help("unsized locals are gated as an unstable feature");
} }
} }
ObligationCauseCode::SizedArgumentType(ty_span) => { ObligationCauseCode::SizedArgumentType(hir_id) => {
if let Some(span) = ty_span { let mut ty = None;
let trait_len = if let ty::PredicateKind::Clause(clause) = let borrowed_msg = "function arguments must have a statically known size, borrowed \
predicate.kind().skip_binder() types always have a known size";
&& let ty::ClauseKind::Trait(trait_pred) = clause if let Some(hir_id) = hir_id
&& let ty::Dynamic(preds, ..) = trait_pred.self_ty().kind() && let Some(hir::Node::Param(param)) = self.tcx.hir().find(hir_id)
{ && let Some(item) = self.tcx.hir().find_parent(hir_id)
let span = if let Ok(snippet) = && let Some(decl) = item.fn_decl()
self.tcx.sess.source_map().span_to_snippet(span) && let Some(t) = decl.inputs.iter().find(|t| param.ty_span.contains(t.span))
&& snippet.starts_with("dyn ") {
{ // We use `contains` because the type might be surrounded by parentheses,
let pos = snippet.len() - snippet[3..].trim_start().len(); // which makes `ty_span` and `t.span` disagree with each other, but one
span.with_hi(span.lo() + BytePos(pos as u32)) // fully contains the other: `foo: (dyn Foo + Bar)`
} else { // ^-------------^
span.shrink_to_lo() // ||
}; // |t.span
err.span_suggestion_verbose( // param._ty_span
span, ty = Some(t);
"you can use `impl Trait` as the argument type", } else if let Some(hir_id) = hir_id
"impl ".to_string(), && let Some(hir::Node::Ty(t)) = self.tcx.hir().find(hir_id)
Applicability::MaybeIncorrect, {
); ty = Some(t);
preds }
.iter() if let Some(ty) = ty {
.filter(|pred| { match ty.kind {
// We only want to count `dyn Foo + Bar`, not `dyn Foo<Bar>`, hir::TyKind::TraitObject(traits, _, _) => {
// because the later doesn't need parentheses. let (span, kw) = match traits {
matches!( [first, ..] if first.span.lo() == ty.span.lo() => {
pred.skip_binder(), // Missing `dyn` in front of trait object.
ty::ExistentialPredicate::Trait(_) (ty.span.shrink_to_lo(), "dyn ")
| ty::ExistentialPredicate::AutoTrait(_) }
) [first, ..] => (ty.span.until(first.span), ""),
}) [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
.count() };
} else { let needs_parens = traits.len() != 1;
1 err.span_suggestion_verbose(
}; span,
let sugg = if trait_len == 1 { "you can use `impl Trait` as the argument type",
vec![(span.shrink_to_lo(), "&".to_string())] "impl ".to_string(),
} else if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) Applicability::MaybeIncorrect,
&& snippet.starts_with('(') );
{ let sugg = if !needs_parens {
// We don't want to suggest `&((dyn Foo + Bar))` when we have vec![(span.shrink_to_lo(), format!("&{kw}"))]
// `(dyn Foo + Bar)`. } else {
vec![(span.shrink_to_lo(), "&".to_string())] vec![
} else { (span.shrink_to_lo(), format!("&({kw}")),
vec![ (ty.span.shrink_to_hi(), ")".to_string()),
(span.shrink_to_lo(), "&(".to_string()), ]
(span.shrink_to_hi(), ")".to_string()), };
] err.multipart_suggestion_verbose(
}; borrowed_msg,
err.multipart_suggestion_verbose( sugg,
"function arguments must have a statically known size, borrowed types \ Applicability::MachineApplicable,
always have a known size", );
sugg, }
Applicability::MachineApplicable, hir::TyKind::Slice(_ty) => {
); err.span_suggestion_verbose(
ty.span.shrink_to_lo(),
"function arguments must have a statically known size, borrowed \
slices always have a known size",
"&",
Applicability::MachineApplicable,
);
}
hir::TyKind::Path(_) => {
err.span_suggestion_verbose(
ty.span.shrink_to_lo(),
borrowed_msg,
"&",
Applicability::MachineApplicable,
);
}
_ => {}
}
} else { } else {
err.note("all function arguments must have a statically known size"); err.note("all function arguments must have a statically known size");
} }

View file

@ -6,7 +6,7 @@ LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
| |
= help: the trait `std::marker::Sized` is not implemented for `[u8]` = help: the trait `std::marker::Sized` is not implemented for `[u8]`
= help: unsized fn params are gated as an unstable feature = help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size help: function arguments must have a statically known size, borrowed slices always have a known size
| |
LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: &[u8]| x }]> { LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: &[u8]| x }]> {
| + | +

View file

@ -29,8 +29,8 @@ LL | fn bar(x: impl Foo) {
| ++++ | ++++
help: function arguments must have a statically known size, borrowed types always have a known size help: function arguments must have a statically known size, borrowed types always have a known size
| |
LL | fn bar(x: &Foo) { LL | fn bar(x: &dyn Foo) {
| + | ++++
error[E0277]: the size for values of type `[()]` cannot be known at compilation time error[E0277]: the size for values of type `[()]` cannot be known at compilation time
--> $DIR/feature-gate-unsized_fn_params.rs:25:8 --> $DIR/feature-gate-unsized_fn_params.rs:25:8
@ -40,7 +40,7 @@ LL | fn qux(_: [()]) {}
| |
= help: the trait `Sized` is not implemented for `[()]` = help: the trait `Sized` is not implemented for `[()]`
= help: unsized fn params are gated as an unstable feature = help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size help: function arguments must have a statically known size, borrowed slices always have a known size
| |
LL | fn qux(_: &[()]) {} LL | fn qux(_: &[()]) {}
| + | +

View file

@ -6,10 +6,6 @@ LL | fn foo(_x: K) {}
| |
= help: the trait `Sized` is not implemented for `(dyn I + 'static)` = help: the trait `Sized` is not implemented for `(dyn I + 'static)`
= help: unsized fn params are gated as an unstable feature = help: unsized fn params are gated as an unstable feature
help: you can use `impl Trait` as the argument type
|
LL | fn foo(_x: impl K) {}
| ++++
help: function arguments must have a statically known size, borrowed types always have a known size help: function arguments must have a statically known size, borrowed types always have a known size
| |
LL | fn foo(_x: &K) {} LL | fn foo(_x: &K) {}

View file

@ -34,8 +34,8 @@ LL | fn foo(_x: impl Foo + Send) {
| ++++ | ++++
help: function arguments must have a statically known size, borrowed types always have a known size help: function arguments must have a statically known size, borrowed types always have a known size
| |
LL | fn foo(_x: &(Foo + Send)) { LL | fn foo(_x: &(dyn Foo + Send)) {
| ++ + | +++++ +
error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time
--> $DIR/not-on-bare-trait.rs:12:8 --> $DIR/not-on-bare-trait.rs:12:8
@ -47,12 +47,12 @@ LL | fn bar(_x: (dyn Foo + Send)) {
= help: unsized fn params are gated as an unstable feature = help: unsized fn params are gated as an unstable feature
help: you can use `impl Trait` as the argument type help: you can use `impl Trait` as the argument type
| |
LL | fn bar(_x: impl (dyn Foo + Send)) { LL | fn bar(_x: (impl Foo + Send)) {
| ++++ | ~~~~
help: function arguments must have a statically known size, borrowed types always have a known size help: function arguments must have a statically known size, borrowed types always have a known size
| |
LL | fn bar(_x: &(dyn Foo + Send)) { LL | fn bar(_x: (&(dyn Foo + Send))) {
| + | ++ +
error: aborting due to 2 previous errors; 1 warning emitted error: aborting due to 2 previous errors; 1 warning emitted