fix: add parens in transformed dyn type in ref type
Example
---
```rust
trait A<T: ?Sized> { fn a(&self) -> &T; }
trait B {}
impl<'a, T: B> A<dyn 'a + B> for T {$0}
```
**Before this PR**
```rust
impl<'a, T: B> A<dyn 'a + B> for T {
fn a(&self) -> &dyn 'a + B {
// ^^^^^^^^^^ ambiguous `+` in a type
${0:todo!()}
}
}
```
**After this PR**
```rust
impl<'a, T: B> A<dyn 'a + B> for T {
fn a(&self) -> &(dyn 'a + B) {
${0:todo!()}
}
}
```
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 1e8fb51..6e07f5f 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -2704,4 +2704,23 @@
"#,
);
}
+
+ #[test]
+ fn issue_10326() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+trait A<T: ?Sized> { fn a(&self) -> &T; }
+trait B {}
+impl<'a, T: B> A<dyn 'a + B> for T {$0}"#,
+ r#"
+trait A<T: ?Sized> { fn a(&self) -> &T; }
+trait B {}
+impl<'a, T: B> A<dyn 'a + B> for T {
+ fn a(&self) -> &(dyn 'a + B) {
+ ${0:todo!()}
+ }
+}"#,
+ );
+ }
}
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 7cf8dff..1f4d5c4 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -415,16 +415,21 @@
editor.replace(path.syntax(), qualified.clone().syntax());
} else if let Some(path_ty) = ast::PathType::cast(parent) {
let old = path_ty.syntax();
+ let needs_paren = type_needs_parens(old.parent(), subst);
+ let subst = if needs_paren {
+ make.ty(&format!("({subst})"))
+ } else {
+ subst.clone()
+ };
if old.parent().is_some() {
- editor.replace(old, subst.clone().syntax());
+ editor.replace(old, subst.syntax());
} else {
let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?;
let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?;
editor.replace_all(
start..=end,
subst
- .clone()
.syntax()
.children()
.map(NodeOrToken::Node)
@@ -598,6 +603,15 @@
}
}
+fn type_needs_parens(parent: Option<SyntaxNode>, new: &ast::Type) -> bool {
+ if !matches!(new, ast::Type::DynTraitType(_)) {
+ return false;
+ }
+ let Some(parent) = parent else { return false };
+ let kind = parent.kind();
+ ast::CastExpr::can_cast(kind) || ast::RefType::can_cast(kind) || ast::PtrType::can_cast(kind)
+}
+
// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
// trait ref, and then go from the types in the substs back to the syntax).
fn get_syntactic_substs(impl_def: ast::Impl) -> Option<AstSubsts> {