Skip to content

Commit 014c26e

Browse files
authored
Rollup merge of rust-lang#138238 - compiler-errors:dyn-suggestion-in-struct, r=nnethercote
Fix dyn -> param suggestion in struct ICEs Makes the logic from rust-lang#138042 a bit less ICEy and more clean. Also fixes an incorrect suggestion when the struct already has generics. I'll point out the major changes and observations in the code. Fixes rust-lang#138229 Fixes rust-lang#138211 r? nnethercote since you reviewed the original pr, or re-roll if you don't want to review this
2 parents 78c3d99 + ceb0401 commit 014c26e

File tree

5 files changed

+150
-38
lines changed

5 files changed

+150
-38
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs

+94-33
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
7878
}
7979

8080
if self_ty.span.edition().at_least_rust_2021() {
81-
let msg = "expected a type, found a trait";
82-
let label = "you can add the `dyn` keyword if you want a trait object";
83-
let mut diag =
84-
rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg);
81+
let mut diag = rustc_errors::struct_span_code_err!(
82+
self.dcx(),
83+
self_ty.span,
84+
E0782,
85+
"{}",
86+
"expected a type, found a trait"
87+
);
8588
if self_ty.span.can_be_used_for_suggestions()
8689
&& !self.maybe_suggest_impl_trait(self_ty, &mut diag)
87-
&& !self.maybe_suggest_dyn_trait(self_ty, label, sugg, &mut diag)
90+
&& !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag)
8891
{
8992
self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag);
9093
}
@@ -123,31 +126,62 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
123126
}
124127
}
125128

129+
/// For a struct or enum with an invalid bare trait object field, suggest turning
130+
/// it into a generic type bound.
126131
fn maybe_suggest_add_generic_impl_trait(
127132
&self,
128133
self_ty: &hir::Ty<'_>,
129134
diag: &mut Diag<'_>,
130135
) -> bool {
131136
let tcx = self.tcx();
132-
let msg = "you might be missing a type parameter";
133-
let mut sugg = vec![];
134137

135-
let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id;
136-
let parent_item = tcx.hir_node_by_def_id(parent_id).expect_item();
137-
match parent_item.kind {
138-
hir::ItemKind::Struct(_, generics) | hir::ItemKind::Enum(_, generics) => {
139-
sugg.push((
140-
generics.where_clause_span,
141-
format!(
142-
"<T: {}>",
143-
self.tcx().sess.source_map().span_to_snippet(self_ty.span).unwrap()
144-
),
145-
));
146-
sugg.push((self_ty.span, "T".to_string()));
138+
let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id);
139+
let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id;
140+
141+
let generics = match tcx.hir_node_by_def_id(parent_item) {
142+
hir::Node::Item(hir::Item {
143+
kind: hir::ItemKind::Struct(variant, generics), ..
144+
}) => {
145+
if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) {
146+
return false;
147+
}
148+
generics
147149
}
148-
_ => {}
150+
hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(def, generics), .. }) => {
151+
if !def
152+
.variants
153+
.iter()
154+
.flat_map(|variant| variant.data.fields().iter())
155+
.any(|field| field.hir_id == parent_hir_id)
156+
{
157+
return false;
158+
}
159+
generics
160+
}
161+
_ => return false,
162+
};
163+
164+
let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(self_ty.span) else {
165+
return false;
166+
};
167+
168+
let param = "TUV"
169+
.chars()
170+
.map(|c| c.to_string())
171+
.chain((0..).map(|i| format!("P{i}")))
172+
.find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s))
173+
.expect("we definitely can find at least one param name to generate");
174+
let mut sugg = vec![(self_ty.span, param.to_string())];
175+
if let Some(insertion_span) = generics.span_for_param_suggestion() {
176+
sugg.push((insertion_span, format!(", {param}: {}", rendered_ty)));
177+
} else {
178+
sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty)));
149179
}
150-
diag.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable);
180+
diag.multipart_suggestion_verbose(
181+
"you might be missing a type parameter",
182+
sugg,
183+
Applicability::MachineApplicable,
184+
);
151185
true
152186
}
153187
/// Make sure that we are in the condition to suggest the blanket implementation.
@@ -198,32 +232,59 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
198232
}
199233
}
200234

235+
/// Try our best to approximate when adding `dyn` would be helpful for a bare
236+
/// trait object.
237+
///
238+
/// Right now, this is if the type is either directly nested in another ty,
239+
/// or if it's in the tail field within a struct. This approximates what the
240+
/// user would've gotten on edition 2015, except for the case where we have
241+
/// an *obvious* knock-on `Sized` error.
201242
fn maybe_suggest_dyn_trait(
202243
&self,
203244
self_ty: &hir::Ty<'_>,
204-
label: &str,
205245
sugg: Vec<(Span, String)>,
206246
diag: &mut Diag<'_>,
207247
) -> bool {
208248
let tcx = self.tcx();
209-
let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id;
210-
let parent_item = tcx.hir_node_by_def_id(parent_id).expect_item();
211249

212-
// If the parent item is an enum, don't suggest the dyn trait.
213-
if let hir::ItemKind::Enum(..) = parent_item.kind {
214-
return false;
215-
}
250+
// Look at the direct HIR parent, since we care about the relationship between
251+
// the type and the thing that directly encloses it.
252+
match tcx.parent_hir_node(self_ty.hir_id) {
253+
// These are all generally ok. Namely, when a trait object is nested
254+
// into another expression or ty, it's either very certain that they
255+
// missed the ty (e.g. `&Trait`) or it's not really possible to tell
256+
// what their intention is, so let's not give confusing suggestions and
257+
// just mention `dyn`. The user can make up their mind what to do here.
258+
hir::Node::Ty(_)
259+
| hir::Node::Expr(_)
260+
| hir::Node::PatExpr(_)
261+
| hir::Node::PathSegment(_)
262+
| hir::Node::AssocItemConstraint(_)
263+
| hir::Node::TraitRef(_)
264+
| hir::Node::Item(_)
265+
| hir::Node::WherePredicate(_) => {}
216266

217-
// If the parent item is a struct, check if self_ty is the last field.
218-
if let hir::ItemKind::Struct(variant_data, _) = parent_item.kind {
219-
if variant_data.fields().last().unwrap().ty.span != self_ty.span {
220-
return false;
267+
hir::Node::Field(field) => {
268+
// Enums can't have unsized fields, fields can only have an unsized tail field.
269+
if let hir::Node::Item(hir::Item {
270+
kind: hir::ItemKind::Struct(variant, _), ..
271+
}) = tcx.parent_hir_node(field.hir_id)
272+
&& variant
273+
.fields()
274+
.last()
275+
.is_some_and(|tail_field| tail_field.hir_id == field.hir_id)
276+
{
277+
// Ok
278+
} else {
279+
return false;
280+
}
221281
}
282+
_ => return false,
222283
}
223284

224285
// FIXME: Only emit this suggestion if the trait is dyn-compatible.
225286
diag.multipart_suggestion_verbose(
226-
label.to_string(),
287+
"you can add the `dyn` keyword if you want a trait object",
227288
sugg,
228289
Applicability::MachineApplicable,
229290
);

tests/ui/dyn-keyword/dyn-2021-edition-error.rs

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ fn function(x: &SomeTrait, y: Box<SomeTrait>) {
77
//~^ ERROR expected a type, found a trait
88
}
99

10+
// Regression test for <https://github.com/rust-lang/rust/issues/138211>.
11+
extern "C" {
12+
fn foo() -> *const SomeTrait;
13+
//~^ ERROR expected a type, found a trait
14+
}
15+
1016
trait SomeTrait {}
1117

1218
fn main() {}

tests/ui/dyn-keyword/dyn-2021-edition-error.stderr

+12-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ help: you can add the `dyn` keyword if you want a trait object
2929
LL | fn function(x: &SomeTrait, y: Box<dyn SomeTrait>) {
3030
| +++
3131

32+
error[E0782]: expected a type, found a trait
33+
--> $DIR/dyn-2021-edition-error.rs:12:24
34+
|
35+
LL | fn foo() -> *const SomeTrait;
36+
| ^^^^^^^^^
37+
|
38+
help: you can add the `dyn` keyword if you want a trait object
39+
|
40+
LL | fn foo() -> *const dyn SomeTrait;
41+
| +++
42+
3243
error[E0782]: expected a type, found a trait
3344
--> $DIR/dyn-2021-edition-error.rs:6:14
3445
|
@@ -40,6 +51,6 @@ help: you can add the `dyn` keyword if you want a trait object
4051
LL | let _x: &dyn SomeTrait = todo!();
4152
| +++
4253

43-
error: aborting due to 3 previous errors
54+
error: aborting due to 4 previous errors
4455

4556
For more information about this error, try `rustc --explain E0782`.

tests/ui/suggestions/suggest-struct-or-union-add-generic-impl-trait.rs tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ struct Foo2 {
1313
//~^ ERROR expected a type, found a trait
1414
}
1515

16-
1716
enum Enum1 {
1817
A(Trait),
1918
//~^ ERROR expected a type, found a trait
@@ -26,5 +25,17 @@ enum Enum2 {
2625
//~^ ERROR expected a type, found a trait
2726
}
2827

28+
// Regression test for <https://github.com/rust-lang/rust/issues/138229>.
29+
pub struct InWhereClause
30+
where
31+
Trait:, {}
32+
//~^ ERROR expected a type, found a trait
33+
34+
struct HasGenerics<T> {
35+
f: Trait,
36+
//~^ ERROR expected a type, found a trait
37+
t: T,
38+
}
39+
2940

3041
fn main() {}

tests/ui/suggestions/suggest-struct-or-union-add-generic-impl-trait.stderr tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.stderr

+26-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ LL | b: dyn Trait,
2222
| +++
2323

2424
error[E0782]: expected a type, found a trait
25-
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:18:7
25+
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:17:7
2626
|
2727
LL | A(Trait),
2828
| ^^^^^
@@ -34,7 +34,7 @@ LL ~ A(T),
3434
|
3535

3636
error[E0782]: expected a type, found a trait
37-
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:25:7
37+
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:24:7
3838
|
3939
LL | B(Trait),
4040
| ^^^^^
@@ -46,6 +46,29 @@ LL | A(u32),
4646
LL ~ B(T),
4747
|
4848

49-
error: aborting due to 4 previous errors
49+
error[E0782]: expected a type, found a trait
50+
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:35:8
51+
|
52+
LL | f: Trait,
53+
| ^^^^^
54+
|
55+
help: you might be missing a type parameter
56+
|
57+
LL ~ struct HasGenerics<T, U: Trait> {
58+
LL ~ f: U,
59+
|
60+
61+
error[E0782]: expected a type, found a trait
62+
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:31:5
63+
|
64+
LL | Trait:, {}
65+
| ^^^^^
66+
|
67+
help: you can add the `dyn` keyword if you want a trait object
68+
|
69+
LL | dyn Trait:, {}
70+
| +++
71+
72+
error: aborting due to 6 previous errors
5073

5174
For more information about this error, try `rustc --explain E0782`.

0 commit comments

Comments
 (0)