Skip to content

Commit bca0ab8

Browse files
Rework maybe_suggest_add_generic_impl_trait
1 parent 07292cc commit bca0ab8

File tree

5 files changed

+108
-29
lines changed

5 files changed

+108
-29
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs

+71-24
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
123123
}
124124
}
125125

126+
/// For a struct or enum with an invalid bare trait object field, suggest turning
127+
/// it into a generic type bound.
126128
fn maybe_suggest_add_generic_impl_trait(
127129
&self,
128130
self_ty: &hir::Ty<'_>,
@@ -132,21 +134,38 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
132134
let msg = "you might be missing a type parameter";
133135
let mut sugg = vec![];
134136

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()));
137+
let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id);
138+
let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id;
139+
140+
let generics = match tcx.hir_node_by_def_id(parent_item) {
141+
hir::Node::Item(hir::Item {
142+
kind: hir::ItemKind::Struct(variant, generics), ..
143+
}) => {
144+
if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) {
145+
return false;
146+
}
147+
generics
147148
}
148-
_ => {}
149-
}
149+
hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(def, generics), .. }) => {
150+
if !def
151+
.variants
152+
.iter()
153+
.flat_map(|variant| variant.data.fields().iter())
154+
.any(|field| field.hir_id == parent_hir_id)
155+
{
156+
return false;
157+
}
158+
generics
159+
}
160+
_ => return false,
161+
};
162+
163+
// FIXME: `T` may already be taken.
164+
sugg.push((
165+
generics.where_clause_span,
166+
format!("<T: {}>", self.tcx().sess.source_map().span_to_snippet(self_ty.span).unwrap()),
167+
));
168+
sugg.push((self_ty.span, "T".to_string()));
150169
diag.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable);
151170
true
152171
}
@@ -198,6 +217,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
198217
}
199218
}
200219

220+
/// Try our best to approximate when adding `dyn` would be helpful for a bare
221+
/// trait object.
222+
///
223+
/// Right now, this is if the type is either directly nested in another ty,
224+
/// or if it's in the tail field within a struct. This approximates what the
225+
/// user would've gotten on edition 2015, except for the case where we have
226+
/// an *obvious* knock-on `Sized` error.
201227
fn maybe_suggest_dyn_trait(
202228
&self,
203229
self_ty: &hir::Ty<'_>,
@@ -206,19 +232,40 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
206232
diag: &mut Diag<'_>,
207233
) -> bool {
208234
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();
211235

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-
}
236+
// Look at the direct HIR parent, since we care about the relationship between
237+
// the type and the thing that directly encloses it.
238+
match tcx.parent_hir_node(self_ty.hir_id) {
239+
// These are all generally ok. Namely, when a trait object is nested
240+
// into another expression or ty, it's either very certain that they
241+
// missed the ty (e.g. `&Trait`) or it's not really possible to tell
242+
// what their intention is, so let's not give confusing suggestions and
243+
// just mention `dyn`. The user can make up their mind what to do here.
244+
hir::Node::Ty(_)
245+
| hir::Node::Expr(_)
246+
| hir::Node::PatExpr(_)
247+
| hir::Node::PathSegment(_)
248+
| hir::Node::AssocItemConstraint(_)
249+
| hir::Node::TraitRef(_)
250+
| hir::Node::Item(_)
251+
| hir::Node::WherePredicate(_) => {}
216252

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;
253+
hir::Node::Field(field) => {
254+
// Enums can't have unsized fields, fields can only have an unsized tail field.
255+
if let hir::Node::Item(hir::Item {
256+
kind: hir::ItemKind::Struct(variant, _), ..
257+
}) = tcx.parent_hir_node(field.hir_id)
258+
&& variant
259+
.fields()
260+
.last()
261+
.is_some_and(|tail_field| tail_field.hir_id == field.hir_id)
262+
{
263+
// Ok
264+
} else {
265+
return false;
266+
}
221267
}
268+
_ => return false,
222269
}
223270

224271
// FIXME: Only emit this suggestion if the trait is dyn-compatible.

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 renamed to tests/ui/dyn-keyword/suggest-struct-or-union-add-generic-impl-trait.rs

+5-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,10 @@ 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
2933

3034
fn main() {}

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

+14-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,17 @@ 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:31:5
51+
|
52+
LL | Trait:, {}
53+
| ^^^^^
54+
|
55+
help: you can add the `dyn` keyword if you want a trait object
56+
|
57+
LL | dyn Trait:, {}
58+
| +++
59+
60+
error: aborting due to 5 previous errors
5061

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

0 commit comments

Comments
 (0)