-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Suggest impl Trait
for References to Bare Trait in Function Header
#127692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -120,9 +120,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
return; | ||
}; | ||
let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); | ||
if sugg.is_empty() { | ||
return; | ||
}; | ||
diag.multipart_suggestion( | ||
format!( | ||
"alternatively use a blanket implementation to implement `{of_trait_name}` for \ | ||
|
@@ -157,6 +154,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; | ||
// FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>` | ||
// and suggest `Trait0<Ty = impl Trait1>`. | ||
// Functions are found in three different contexts. | ||
// 1. Independent functions | ||
// 2. Functions inside trait blocks | ||
// 3. Functions inside impl blocks | ||
let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { | ||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { | ||
(sig, generics, None) | ||
|
@@ -167,13 +168,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
owner_id, | ||
.. | ||
}) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), | ||
hir::Node::ImplItem(hir::ImplItem { | ||
kind: hir::ImplItemKind::Fn(sig, _), | ||
generics, | ||
owner_id, | ||
.. | ||
}) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't wrong, but I'm not sure why this is necessary to add for these changes. Can you explain what's being changed by adding this arm? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added this for consistency so that we show the same suggestions in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm asking in what specific case you discovered this? It's really hard to determine what changes affect which diagnostics when it's all together like this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, It does not add anything to the diagnostic. It just ensures that the existing diagnostics apply to functions inside impl blocks as well. |
||
_ => return false, | ||
}; | ||
let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { | ||
return false; | ||
}; | ||
let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; | ||
let mut is_downgradable = true; | ||
|
||
// Check if trait object is safe for suggesting dynamic dispatch. | ||
let is_object_safe = match self_ty.kind { | ||
hir::TyKind::TraitObject(objects, ..) => { | ||
objects.iter().all(|o| match o.trait_ref.path.res { | ||
|
@@ -189,8 +198,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
} | ||
_ => false, | ||
}; | ||
|
||
let borrowed = matches!( | ||
tcx.parent_hir_node(self_ty.hir_id), | ||
hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) | ||
); | ||
|
||
// Suggestions for function return type. | ||
if let hir::FnRetTy::Return(ty) = sig.decl.output | ||
&& ty.hir_id == self_ty.hir_id | ||
&& ty.peel_refs().hir_id == self_ty.hir_id | ||
{ | ||
let pre = if !is_object_safe { | ||
format!("`{trait_name}` is not object safe, ") | ||
|
@@ -201,14 +217,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
"{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ | ||
single underlying type", | ||
); | ||
|
||
diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); | ||
|
||
// Suggest `Box<dyn Trait>` for return type | ||
if is_object_safe { | ||
diag.multipart_suggestion_verbose( | ||
"alternatively, you can return an owned trait object", | ||
// If the return type is `&Trait`, we don't want | ||
// the ampersand to be displayed in the `Box<dyn Trait>` | ||
// suggestion. | ||
let suggestion = if borrowed { | ||
vec![(ty.span, format!("Box<dyn {trait_name}>"))] | ||
} else { | ||
vec![ | ||
(ty.span.shrink_to_lo(), "Box<dyn ".to_string()), | ||
(ty.span.shrink_to_hi(), ">".to_string()), | ||
], | ||
] | ||
}; | ||
|
||
diag.multipart_suggestion_verbose( | ||
"alternatively, you can return an owned trait object", | ||
suggestion, | ||
Applicability::MachineApplicable, | ||
); | ||
} else if is_downgradable { | ||
|
@@ -217,39 +245,43 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
} | ||
return true; | ||
} | ||
|
||
// Suggestions for function parameters. | ||
for ty in sig.decl.inputs { | ||
if ty.hir_id != self_ty.hir_id { | ||
if ty.peel_refs().hir_id != self_ty.hir_id { | ||
continue; | ||
} | ||
let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); | ||
if !sugg.is_empty() { | ||
diag.multipart_suggestion_verbose( | ||
format!("use a new generic type parameter, constrained by `{trait_name}`"), | ||
sugg, | ||
Applicability::MachineApplicable, | ||
); | ||
diag.multipart_suggestion_verbose( | ||
"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", | ||
impl_sugg, | ||
Applicability::MachineApplicable, | ||
); | ||
} | ||
diag.multipart_suggestion_verbose( | ||
format!("use a new generic type parameter, constrained by `{trait_name}`"), | ||
sugg, | ||
Applicability::MachineApplicable, | ||
); | ||
diag.multipart_suggestion_verbose( | ||
"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", | ||
impl_sugg, | ||
Applicability::MachineApplicable, | ||
); | ||
if !is_object_safe { | ||
diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`")); | ||
if is_downgradable { | ||
// We'll emit the object safety error already, with a structured suggestion. | ||
diag.downgrade_to_delayed_bug(); | ||
} | ||
} else { | ||
// No ampersand in suggestion if it's borrowed already | ||
let (dyn_str, paren_dyn_str) = | ||
if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; | ||
|
||
let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { | ||
// There are more than one trait bound, we need surrounding parentheses. | ||
vec![ | ||
(self_ty.span.shrink_to_lo(), "&(dyn ".to_string()), | ||
(self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), | ||
(self_ty.span.shrink_to_hi(), ")".to_string()), | ||
] | ||
} else { | ||
vec![(self_ty.span.shrink_to_lo(), "&dyn ".to_string())] | ||
vec![(self_ty.span.shrink_to_lo(), dyn_str.to_string())] | ||
}; | ||
diag.multipart_suggestion_verbose( | ||
format!( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
//@ edition:2021 | ||
|
||
trait Trait {} | ||
|
||
struct IceCream; | ||
|
||
impl IceCream { | ||
fn foo(_: &Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn bar(self, _: &'a Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
//~| ERROR: use of undeclared lifetime name | ||
|
||
fn alice<'a>(&self, _: &Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn bob<'a>(_: &'a Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn cat() -> &Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn dog<'a>() -> &Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn kitten() -> &'a Trait { | ||
//~^ ERROR: use of undeclared lifetime name | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn puppy<'a>() -> &'a Trait { | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn parrot() -> &mut Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&mut Type | ||
//~^ ERROR: cannot return reference to temporary value | ||
} | ||
} | ||
|
||
trait Sing { | ||
fn foo(_: &Trait); | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn bar(_: &'a Trait); | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
//~| ERROR: use of undeclared lifetime name | ||
|
||
fn alice<'a>(_: &Trait); | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn bob<'a>(_: &'a Trait); | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn cat() -> &Trait; | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn dog<'a>() -> &Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn kitten() -> &'a Trait { | ||
//~^ ERROR: use of undeclared lifetime name | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn puppy<'a>() -> &'a Trait { | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn parrot() -> &mut Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&mut Type | ||
//~^ ERROR: cannot return reference to temporary value | ||
} | ||
} | ||
|
||
fn foo(_: &Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn bar(_: &'a Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
//~| ERROR: use of undeclared lifetime name | ||
|
||
fn alice<'a>(_: &Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
fn bob<'a>(_: &'a Trait) {} | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
|
||
struct Type; | ||
|
||
impl Trait for Type {} | ||
|
||
fn cat() -> &Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn dog<'a>() -> &Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn kitten() -> &'a Trait { | ||
//~^ ERROR: use of undeclared lifetime name | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn puppy<'a>() -> &'a Trait { | ||
//~^ ERROR: trait objects must include the `dyn` keyword | ||
&Type | ||
} | ||
|
||
fn parrot() -> &mut Trait { | ||
//~^ ERROR: missing lifetime specifier | ||
//~| ERROR: trait objects must include the `dyn` keyword | ||
&mut Type | ||
//~^ ERROR: cannot return reference to temporary value | ||
} | ||
|
||
fn main() {} |
Uh oh!
There was an error while loading. Please reload this page.