Skip to content

Commit 79bef72

Browse files
committed
Track HirId instead of Span in ObligationCauseCode::SizedArgumentType
This gets us more accurate suggestions.
1 parent 8551cab commit 79bef72

File tree

8 files changed

+96
-82
lines changed

8 files changed

+96
-82
lines changed

compiler/rustc_hir_typeck/src/check.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,21 +114,22 @@ pub(super) fn check_fn<'a, 'tcx>(
114114
let inputs_fn = fn_sig.inputs().iter().copied();
115115
for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
116116
// Check the pattern.
117-
let ty_span = try { inputs_hir?.get(idx)?.span };
117+
let ty: Option<&hir::Ty<'_>> = try { inputs_hir?.get(idx)? };
118+
let ty_span = ty.map(|ty| ty.span);
118119
fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
119120

120121
// Check that argument is Sized.
121122
if !params_can_be_unsized {
122123
fcx.require_type_is_sized(
123124
param_ty,
124125
param.pat.span,
125-
// ty_span == binding_span iff this is a closure parameter with no type ascription,
126+
// ty.span == binding_span iff this is a closure parameter with no type ascription,
126127
// or if it's an implicit `self` parameter
127128
traits::SizedArgumentType(
128129
if ty_span == Some(param.span) && tcx.is_closure(fn_def_id.into()) {
129130
None
130131
} else {
131-
ty_span
132+
ty.map(|ty| ty.hir_id)
132133
},
133134
),
134135
);

compiler/rustc_hir_typeck/src/gather_locals.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
6060
// parameters are special cases of patterns, but we want to handle them as
6161
// *distinct* cases. so track when we are hitting a pattern *within* an fn
6262
// parameter.
63-
outermost_fn_param_pat: Option<Span>,
63+
outermost_fn_param_pat: Option<(Span, hir::HirId)>,
6464
}
6565

6666
impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
@@ -131,7 +131,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
131131
}
132132

133133
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
134-
let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
134+
let old_outermost_fn_param_pat =
135+
self.outermost_fn_param_pat.replace((param.ty_span, param.hir_id));
135136
intravisit::walk_param(self, param);
136137
self.outermost_fn_param_pat = old_outermost_fn_param_pat;
137138
}
@@ -141,7 +142,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
141142
if let PatKind::Binding(_, _, ident, _) = p.kind {
142143
let var_ty = self.assign(p.span, p.hir_id, None);
143144

144-
if let Some(ty_span) = self.outermost_fn_param_pat {
145+
if let Some((ty_span, hir_id)) = self.outermost_fn_param_pat {
145146
if !self.fcx.tcx.features().unsized_fn_params {
146147
self.fcx.require_type_is_sized(
147148
var_ty,
@@ -154,7 +155,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
154155
{
155156
None
156157
} else {
157-
Some(ty_span)
158+
Some(hir_id)
158159
},
159160
),
160161
);

compiler/rustc_middle/src/traits/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ pub enum ObligationCauseCode<'tcx> {
289289
/// Type of each variable must be `Sized`.
290290
VariableType(hir::HirId),
291291
/// Argument type must be `Sized`.
292-
SizedArgumentType(Option<Span>),
292+
SizedArgumentType(Option<hir::HirId>),
293293
/// Return type must be `Sized`.
294294
SizedReturnType,
295295
/// Yield type must be `Sized`.

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

Lines changed: 76 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@ use rustc_errors::{
1919
use rustc_hir as hir;
2020
use rustc_hir::def::{DefKind, Res};
2121
use rustc_hir::def_id::DefId;
22-
use rustc_hir::intravisit::Visitor;
22+
use rustc_hir::intravisit::{Map, Visitor};
2323
use rustc_hir::is_range_literal;
2424
use rustc_hir::lang_items::LangItem;
25-
use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Node};
26-
use rustc_hir::{Expr, HirId};
25+
use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
2726
use rustc_infer::infer::error_reporting::TypeErrCtxt;
2827
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2928
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
@@ -3200,63 +3199,80 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
32003199
err.help("unsized locals are gated as an unstable feature");
32013200
}
32023201
}
3203-
ObligationCauseCode::SizedArgumentType(ty_span) => {
3204-
if let Some(span) = ty_span {
3205-
let trait_len = if let ty::PredicateKind::Clause(clause) =
3206-
predicate.kind().skip_binder()
3207-
&& let ty::ClauseKind::Trait(trait_pred) = clause
3208-
&& let ty::Dynamic(preds, ..) = trait_pred.self_ty().kind()
3209-
{
3210-
let span = if let Ok(snippet) =
3211-
self.tcx.sess.source_map().span_to_snippet(span)
3212-
&& snippet.starts_with("dyn ")
3213-
{
3214-
let pos = snippet.len() - snippet[3..].trim_start().len();
3215-
span.with_hi(span.lo() + BytePos(pos as u32))
3216-
} else {
3217-
span.shrink_to_lo()
3218-
};
3219-
err.span_suggestion_verbose(
3220-
span,
3221-
"you can use `impl Trait` as the argument type",
3222-
"impl ".to_string(),
3223-
Applicability::MaybeIncorrect,
3224-
);
3225-
preds
3226-
.iter()
3227-
.filter(|pred| {
3228-
// We only want to count `dyn Foo + Bar`, not `dyn Foo<Bar>`,
3229-
// because the later doesn't need parentheses.
3230-
matches!(
3231-
pred.skip_binder(),
3232-
ty::ExistentialPredicate::Trait(_)
3233-
| ty::ExistentialPredicate::AutoTrait(_)
3234-
)
3235-
})
3236-
.count()
3237-
} else {
3238-
1
3239-
};
3240-
let sugg = if trait_len == 1 {
3241-
vec![(span.shrink_to_lo(), "&".to_string())]
3242-
} else if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
3243-
&& snippet.starts_with('(')
3244-
{
3245-
// We don't want to suggest `&((dyn Foo + Bar))` when we have
3246-
// `(dyn Foo + Bar)`.
3247-
vec![(span.shrink_to_lo(), "&".to_string())]
3248-
} else {
3249-
vec![
3250-
(span.shrink_to_lo(), "&(".to_string()),
3251-
(span.shrink_to_hi(), ")".to_string()),
3252-
]
3253-
};
3254-
err.multipart_suggestion_verbose(
3255-
"function arguments must have a statically known size, borrowed types \
3256-
always have a known size",
3257-
sugg,
3258-
Applicability::MachineApplicable,
3259-
);
3202+
ObligationCauseCode::SizedArgumentType(hir_id) => {
3203+
let mut ty = None;
3204+
let borrowed_msg = "function arguments must have a statically known size, borrowed \
3205+
types always have a known size";
3206+
if let Some(hir_id) = hir_id
3207+
&& let Some(hir::Node::Param(param)) = self.tcx.hir().find(hir_id)
3208+
&& let Some(item) = self.tcx.hir().find_parent(hir_id)
3209+
&& let Some(decl) = item.fn_decl()
3210+
&& let Some(t) = decl.inputs.iter().find(|t| param.ty_span.contains(t.span))
3211+
{
3212+
// We use `contains` because the type might be surrounded by parentheses,
3213+
// which makes `ty_span` and `t.span` disagree with each other, but one
3214+
// fully contains the other: `foo: (dyn Foo + Bar)`
3215+
// ^-------------^
3216+
// ||
3217+
// |t.span
3218+
// param._ty_span
3219+
ty = Some(t);
3220+
} else if let Some(hir_id) = hir_id
3221+
&& let Some(hir::Node::Ty(t)) = self.tcx.hir().find(hir_id)
3222+
{
3223+
ty = Some(t);
3224+
}
3225+
if let Some(ty) = ty {
3226+
match ty.kind {
3227+
hir::TyKind::TraitObject(traits, _, _) => {
3228+
let (span, kw) = match traits {
3229+
[first, ..] if first.span.lo() == ty.span.lo() => {
3230+
// Missing `dyn` in front of trait object.
3231+
(ty.span.shrink_to_lo(), "dyn ")
3232+
}
3233+
[first, ..] => (ty.span.until(first.span), ""),
3234+
[] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
3235+
};
3236+
let needs_parens = traits.len() != 1;
3237+
err.span_suggestion_verbose(
3238+
span,
3239+
"you can use `impl Trait` as the argument type",
3240+
"impl ".to_string(),
3241+
Applicability::MaybeIncorrect,
3242+
);
3243+
let sugg = if !needs_parens {
3244+
vec![(span.shrink_to_lo(), format!("&{kw}"))]
3245+
} else {
3246+
vec![
3247+
(span.shrink_to_lo(), format!("&({kw}")),
3248+
(ty.span.shrink_to_hi(), ")".to_string()),
3249+
]
3250+
};
3251+
err.multipart_suggestion_verbose(
3252+
borrowed_msg,
3253+
sugg,
3254+
Applicability::MachineApplicable,
3255+
);
3256+
}
3257+
hir::TyKind::Slice(_ty) => {
3258+
err.span_suggestion_verbose(
3259+
ty.span.shrink_to_lo(),
3260+
"function arguments must have a statically known size, borrowed \
3261+
slices always have a known size",
3262+
"&",
3263+
Applicability::MachineApplicable,
3264+
);
3265+
}
3266+
hir::TyKind::Path(_) => {
3267+
err.span_suggestion_verbose(
3268+
ty.span.shrink_to_lo(),
3269+
borrowed_msg,
3270+
"&",
3271+
Applicability::MachineApplicable,
3272+
);
3273+
}
3274+
_ => {}
3275+
}
32603276
} else {
32613277
err.note("all function arguments must have a statically known size");
32623278
}

src/tools/clippy/tests/ui/crashes/ice-6251.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
66
|
77
= help: the trait `std::marker::Sized` is not implemented for `[u8]`
88
= help: unsized fn params are gated as an unstable feature
9-
help: function arguments must have a statically known size, borrowed types always have a known size
9+
help: function arguments must have a statically known size, borrowed slices always have a known size
1010
|
1111
LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: &[u8]| x }]> {
1212
| +

tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ LL | fn bar(x: impl Foo) {
2929
| ++++
3030
help: function arguments must have a statically known size, borrowed types always have a known size
3131
|
32-
LL | fn bar(x: &Foo) {
33-
| +
32+
LL | fn bar(x: &dyn Foo) {
33+
| ++++
3434

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

tests/ui/resolve/issue-5035-2.stderr

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ LL | fn foo(_x: K) {}
66
|
77
= help: the trait `Sized` is not implemented for `(dyn I + 'static)`
88
= help: unsized fn params are gated as an unstable feature
9-
help: you can use `impl Trait` as the argument type
10-
|
11-
LL | fn foo(_x: impl K) {}
12-
| ++++
139
help: function arguments must have a statically known size, borrowed types always have a known size
1410
|
1511
LL | fn foo(_x: &K) {}

tests/ui/traits/bound/not-on-bare-trait.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ LL | fn foo(_x: impl Foo + Send) {
3434
| ++++
3535
help: function arguments must have a statically known size, borrowed types always have a known size
3636
|
37-
LL | fn foo(_x: &(Foo + Send)) {
38-
| ++ +
37+
LL | fn foo(_x: &(dyn Foo + Send)) {
38+
| +++++ +
3939

4040
error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time
4141
--> $DIR/not-on-bare-trait.rs:12:8
@@ -47,12 +47,12 @@ LL | fn bar(_x: (dyn Foo + Send)) {
4747
= help: unsized fn params are gated as an unstable feature
4848
help: you can use `impl Trait` as the argument type
4949
|
50-
LL | fn bar(_x: impl (dyn Foo + Send)) {
51-
| ++++
50+
LL | fn bar(_x: (impl Foo + Send)) {
51+
| ~~~~
5252
help: function arguments must have a statically known size, borrowed types always have a known size
5353
|
54-
LL | fn bar(_x: &(dyn Foo + Send)) {
55-
| +
54+
LL | fn bar(_x: (&(dyn Foo + Send))) {
55+
| ++ +
5656

5757
error: aborting due to 2 previous errors; 1 warning emitted
5858

0 commit comments

Comments
 (0)