Skip to content

Commit 4b38ef6

Browse files
authored
Rollup merge of rust-lang#127692 - veera-sivarajan:bugfix-125139, r=estebank
Suggest `impl Trait` for References to Bare Trait in Function Header Fixes rust-lang#125139 This PR suggests `impl Trait` when `&Trait` is found as a function parameter type or return type. This makes use of existing diagnostics by adding `peel_refs()` when checking for type equality. Additionaly, it makes a few other improvements: 1. Checks if functions inside impl blocks have bare trait in their headers. 2. Introduces a trait `NextLifetimeParamName` similar to the existing `NextTypeParamName` for suggesting a lifetime name. Also, abstracts out the common logic between the two trait impls. ### Related Issues I ran into a bunch of related diagnostic issues but couldn't fix them within the scope of this PR. So, I have created the following issues: 1. [Misleading Suggestion when Returning a Reference to a Bare Trait from a Function](rust-lang#127689) 2. [Verbose Error When a Function Takes a Bare Trait as Parameter](rust-lang#127690) 3. [Incorrect Suggestion when Returning a Bare Trait from a Function](rust-lang#127691) r​? ``@estebank`` since you implemented rust-lang#119148
2 parents d6c8169 + 12de141 commit 4b38ef6

File tree

5 files changed

+891
-28
lines changed

5 files changed

+891
-28
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs

+55-23
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
133133
return;
134134
};
135135
let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name);
136-
if sugg.is_empty() {
137-
return;
138-
};
139136
diag.multipart_suggestion(
140137
format!(
141138
"alternatively use a blanket implementation to implement `{of_trait_name}` for \
@@ -170,6 +167,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
170167
let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
171168
// FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>`
172169
// and suggest `Trait0<Ty = impl Trait1>`.
170+
// Functions are found in three different contexts.
171+
// 1. Independent functions
172+
// 2. Functions inside trait blocks
173+
// 3. Functions inside impl blocks
173174
let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) {
174175
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => {
175176
(sig, generics, None)
@@ -180,13 +181,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
180181
owner_id,
181182
..
182183
}) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))),
184+
hir::Node::ImplItem(hir::ImplItem {
185+
kind: hir::ImplItemKind::Fn(sig, _),
186+
generics,
187+
owner_id,
188+
..
189+
}) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))),
183190
_ => return false,
184191
};
185192
let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else {
186193
return false;
187194
};
188195
let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())];
189196
let mut is_downgradable = true;
197+
198+
// Check if trait object is safe for suggesting dynamic dispatch.
190199
let is_object_safe = match self_ty.kind {
191200
hir::TyKind::TraitObject(objects, ..) => {
192201
objects.iter().all(|(o, _)| match o.trait_ref.path.res {
@@ -202,8 +211,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
202211
}
203212
_ => false,
204213
};
214+
215+
let borrowed = matches!(
216+
tcx.parent_hir_node(self_ty.hir_id),
217+
hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. })
218+
);
219+
220+
// Suggestions for function return type.
205221
if let hir::FnRetTy::Return(ty) = sig.decl.output
206-
&& ty.hir_id == self_ty.hir_id
222+
&& ty.peel_refs().hir_id == self_ty.hir_id
207223
{
208224
let pre = if !is_object_safe {
209225
format!("`{trait_name}` is not object safe, ")
@@ -214,14 +230,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
214230
"{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \
215231
single underlying type",
216232
);
233+
217234
diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable);
235+
236+
// Suggest `Box<dyn Trait>` for return type
218237
if is_object_safe {
219-
diag.multipart_suggestion_verbose(
220-
"alternatively, you can return an owned trait object",
238+
// If the return type is `&Trait`, we don't want
239+
// the ampersand to be displayed in the `Box<dyn Trait>`
240+
// suggestion.
241+
let suggestion = if borrowed {
242+
vec![(ty.span, format!("Box<dyn {trait_name}>"))]
243+
} else {
221244
vec![
222245
(ty.span.shrink_to_lo(), "Box<dyn ".to_string()),
223246
(ty.span.shrink_to_hi(), ">".to_string()),
224-
],
247+
]
248+
};
249+
250+
diag.multipart_suggestion_verbose(
251+
"alternatively, you can return an owned trait object",
252+
suggestion,
225253
Applicability::MachineApplicable,
226254
);
227255
} else if is_downgradable {
@@ -230,39 +258,43 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
230258
}
231259
return true;
232260
}
261+
262+
// Suggestions for function parameters.
233263
for ty in sig.decl.inputs {
234-
if ty.hir_id != self_ty.hir_id {
264+
if ty.peel_refs().hir_id != self_ty.hir_id {
235265
continue;
236266
}
237267
let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name);
238-
if !sugg.is_empty() {
239-
diag.multipart_suggestion_verbose(
240-
format!("use a new generic type parameter, constrained by `{trait_name}`"),
241-
sugg,
242-
Applicability::MachineApplicable,
243-
);
244-
diag.multipart_suggestion_verbose(
245-
"you can also use an opaque type, but users won't be able to specify the type \
246-
parameter when calling the `fn`, having to rely exclusively on type inference",
247-
impl_sugg,
248-
Applicability::MachineApplicable,
249-
);
250-
}
268+
diag.multipart_suggestion_verbose(
269+
format!("use a new generic type parameter, constrained by `{trait_name}`"),
270+
sugg,
271+
Applicability::MachineApplicable,
272+
);
273+
diag.multipart_suggestion_verbose(
274+
"you can also use an opaque type, but users won't be able to specify the type \
275+
parameter when calling the `fn`, having to rely exclusively on type inference",
276+
impl_sugg,
277+
Applicability::MachineApplicable,
278+
);
251279
if !is_object_safe {
252280
diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`"));
253281
if is_downgradable {
254282
// We'll emit the object safety error already, with a structured suggestion.
255283
diag.downgrade_to_delayed_bug();
256284
}
257285
} else {
286+
// No ampersand in suggestion if it's borrowed already
287+
let (dyn_str, paren_dyn_str) =
288+
if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") };
289+
258290
let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind {
259291
// There are more than one trait bound, we need surrounding parentheses.
260292
vec![
261-
(self_ty.span.shrink_to_lo(), "&(dyn ".to_string()),
293+
(self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()),
262294
(self_ty.span.shrink_to_hi(), ")".to_string()),
263295
]
264296
} else {
265-
vec![(self_ty.span.shrink_to_lo(), "&dyn ".to_string())]
297+
vec![(self_ty.span.shrink_to_lo(), dyn_str.to_string())]
266298
};
267299
diag.multipart_suggestion_verbose(
268300
format!(

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

+12-4
Original file line numberDiff line numberDiff line change
@@ -5023,24 +5023,32 @@ impl<'v> Visitor<'v> for AwaitsVisitor {
50235023
}
50245024
}
50255025

5026+
/// Suggest a new type parameter name for diagnostic purposes.
5027+
///
5028+
/// `name` is the preferred name you'd like to suggest if it's not in use already.
50265029
pub trait NextTypeParamName {
50275030
fn next_type_param_name(&self, name: Option<&str>) -> String;
50285031
}
50295032

50305033
impl NextTypeParamName for &[hir::GenericParam<'_>] {
50315034
fn next_type_param_name(&self, name: Option<&str>) -> String {
5032-
// This is the list of possible parameter names that we might suggest.
5035+
// Type names are usually single letters in uppercase. So convert the first letter of input string to uppercase.
50335036
let name = name.and_then(|n| n.chars().next()).map(|c| c.to_uppercase().to_string());
50345037
let name = name.as_deref();
5038+
5039+
// This is the list of possible parameter names that we might suggest.
50355040
let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"];
5036-
let used_names = self
5041+
5042+
// Filter out used names based on `filter_fn`.
5043+
let used_names: Vec<Symbol> = self
50375044
.iter()
5038-
.filter_map(|p| match p.name {
5045+
.filter_map(|param| match param.name {
50395046
hir::ParamName::Plain(ident) => Some(ident.name),
50405047
_ => None,
50415048
})
5042-
.collect::<Vec<_>>();
5049+
.collect();
50435050

5051+
// Find a name from `possible_names` that is not in `used_names`.
50445052
possible_names
50455053
.iter()
50465054
.find(|n| !used_names.contains(&Symbol::intern(n)))

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ error[E0782]: trait objects must include the `dyn` keyword
44
LL | fn function(x: &SomeTrait, y: Box<SomeTrait>) {
55
| ^^^^^^^^^
66
|
7-
help: add `dyn` keyword before this trait
7+
help: use a new generic type parameter, constrained by `SomeTrait`
8+
|
9+
LL | fn function<T: SomeTrait>(x: &T, y: Box<SomeTrait>) {
10+
| ++++++++++++++ ~
11+
help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference
12+
|
13+
LL | fn function(x: &impl SomeTrait, y: Box<SomeTrait>) {
14+
| ++++
15+
help: alternatively, use a trait object to accept any type that implements `SomeTrait`, accessing its methods at runtime using dynamic dispatch
816
|
917
LL | fn function(x: &dyn SomeTrait, y: Box<SomeTrait>) {
1018
| +++
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//@ edition:2021
2+
3+
trait Trait {}
4+
5+
struct IceCream;
6+
7+
impl IceCream {
8+
fn foo(_: &Trait) {}
9+
//~^ ERROR: trait objects must include the `dyn` keyword
10+
11+
fn bar(self, _: &'a Trait) {}
12+
//~^ ERROR: trait objects must include the `dyn` keyword
13+
//~| ERROR: use of undeclared lifetime name
14+
15+
fn alice<'a>(&self, _: &Trait) {}
16+
//~^ ERROR: trait objects must include the `dyn` keyword
17+
18+
fn bob<'a>(_: &'a Trait) {}
19+
//~^ ERROR: trait objects must include the `dyn` keyword
20+
21+
fn cat() -> &Trait {
22+
//~^ ERROR: missing lifetime specifier
23+
//~| ERROR: trait objects must include the `dyn` keyword
24+
&Type
25+
}
26+
27+
fn dog<'a>() -> &Trait {
28+
//~^ ERROR: missing lifetime specifier
29+
//~| ERROR: trait objects must include the `dyn` keyword
30+
&Type
31+
}
32+
33+
fn kitten() -> &'a Trait {
34+
//~^ ERROR: use of undeclared lifetime name
35+
//~| ERROR: trait objects must include the `dyn` keyword
36+
&Type
37+
}
38+
39+
fn puppy<'a>() -> &'a Trait {
40+
//~^ ERROR: trait objects must include the `dyn` keyword
41+
&Type
42+
}
43+
44+
fn parrot() -> &mut Trait {
45+
//~^ ERROR: missing lifetime specifier
46+
//~| ERROR: trait objects must include the `dyn` keyword
47+
&mut Type
48+
//~^ ERROR: cannot return reference to temporary value
49+
}
50+
}
51+
52+
trait Sing {
53+
fn foo(_: &Trait);
54+
//~^ ERROR: trait objects must include the `dyn` keyword
55+
56+
fn bar(_: &'a Trait);
57+
//~^ ERROR: trait objects must include the `dyn` keyword
58+
//~| ERROR: use of undeclared lifetime name
59+
60+
fn alice<'a>(_: &Trait);
61+
//~^ ERROR: trait objects must include the `dyn` keyword
62+
63+
fn bob<'a>(_: &'a Trait);
64+
//~^ ERROR: trait objects must include the `dyn` keyword
65+
66+
fn cat() -> &Trait;
67+
//~^ ERROR: missing lifetime specifier
68+
//~| ERROR: trait objects must include the `dyn` keyword
69+
70+
fn dog<'a>() -> &Trait {
71+
//~^ ERROR: missing lifetime specifier
72+
//~| ERROR: trait objects must include the `dyn` keyword
73+
&Type
74+
}
75+
76+
fn kitten() -> &'a Trait {
77+
//~^ ERROR: use of undeclared lifetime name
78+
//~| ERROR: trait objects must include the `dyn` keyword
79+
&Type
80+
}
81+
82+
fn puppy<'a>() -> &'a Trait {
83+
//~^ ERROR: trait objects must include the `dyn` keyword
84+
&Type
85+
}
86+
87+
fn parrot() -> &mut Trait {
88+
//~^ ERROR: missing lifetime specifier
89+
//~| ERROR: trait objects must include the `dyn` keyword
90+
&mut Type
91+
//~^ ERROR: cannot return reference to temporary value
92+
}
93+
}
94+
95+
fn foo(_: &Trait) {}
96+
//~^ ERROR: trait objects must include the `dyn` keyword
97+
98+
fn bar(_: &'a Trait) {}
99+
//~^ ERROR: trait objects must include the `dyn` keyword
100+
//~| ERROR: use of undeclared lifetime name
101+
102+
fn alice<'a>(_: &Trait) {}
103+
//~^ ERROR: trait objects must include the `dyn` keyword
104+
105+
fn bob<'a>(_: &'a Trait) {}
106+
//~^ ERROR: trait objects must include the `dyn` keyword
107+
108+
struct Type;
109+
110+
impl Trait for Type {}
111+
112+
fn cat() -> &Trait {
113+
//~^ ERROR: missing lifetime specifier
114+
//~| ERROR: trait objects must include the `dyn` keyword
115+
&Type
116+
}
117+
118+
fn dog<'a>() -> &Trait {
119+
//~^ ERROR: missing lifetime specifier
120+
//~| ERROR: trait objects must include the `dyn` keyword
121+
&Type
122+
}
123+
124+
fn kitten() -> &'a Trait {
125+
//~^ ERROR: use of undeclared lifetime name
126+
//~| ERROR: trait objects must include the `dyn` keyword
127+
&Type
128+
}
129+
130+
fn puppy<'a>() -> &'a Trait {
131+
//~^ ERROR: trait objects must include the `dyn` keyword
132+
&Type
133+
}
134+
135+
fn parrot() -> &mut Trait {
136+
//~^ ERROR: missing lifetime specifier
137+
//~| ERROR: trait objects must include the `dyn` keyword
138+
&mut Type
139+
//~^ ERROR: cannot return reference to temporary value
140+
}
141+
142+
fn main() {}

0 commit comments

Comments
 (0)