Skip to content

Commit a41cec9

Browse files
committed
Auto merge of #16696 - ShoyuVanilla:fix-goto-deref-mut, r=Veykril
fix: Goto definition for `deref_mut` Fixes #16520 https://github.com/rust-lang/rust-analyzer/blob/a3236be9d7a8179ac4a997858138a4d6c260a451/crates/hir/src/source_analyzer.rs#L375-L393 As we can see from the above, current implementation routes all dereferencing prefix operations to `Deref::deref` implementation, not regarding mutabilities. https://github.com/rust-lang/rust-analyzer/blob/a3236be9d7a8179ac4a997858138a4d6c260a451/crates/hir-ty/src/infer/mutability.rs#L134-L151 Since we are resolving them already in mutability inferences, we can use those results for proper `deref` / `deref_mut` routing.
2 parents a3236be + 6124431 commit a41cec9

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

crates/hir/src/source_analyzer.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -377,14 +377,34 @@ impl SourceAnalyzer {
377377
db: &dyn HirDatabase,
378378
prefix_expr: &ast::PrefixExpr,
379379
) -> Option<FunctionId> {
380-
let (lang_item, fn_name) = match prefix_expr.op_kind()? {
381-
ast::UnaryOp::Deref => (LangItem::Deref, name![deref]),
382-
ast::UnaryOp::Not => (LangItem::Not, name![not]),
383-
ast::UnaryOp::Neg => (LangItem::Neg, name![neg]),
380+
let (op_trait, op_fn) = match prefix_expr.op_kind()? {
381+
ast::UnaryOp::Deref => {
382+
// This can be either `Deref::deref` or `DerefMut::deref_mut`.
383+
// Since deref kind is inferenced and stored in `InferenceResult.method_resolution`,
384+
// use that result to find out which one it is.
385+
let (deref_trait, deref) =
386+
self.lang_trait_fn(db, LangItem::Deref, &name![deref])?;
387+
self.infer
388+
.as_ref()
389+
.and_then(|infer| {
390+
let expr = self.expr_id(db, &prefix_expr.clone().into())?;
391+
let (func, _) = infer.method_resolution(expr)?;
392+
let (deref_mut_trait, deref_mut) =
393+
self.lang_trait_fn(db, LangItem::DerefMut, &name![deref_mut])?;
394+
if func == deref_mut {
395+
Some((deref_mut_trait, deref_mut))
396+
} else {
397+
None
398+
}
399+
})
400+
.unwrap_or((deref_trait, deref))
401+
}
402+
ast::UnaryOp::Not => self.lang_trait_fn(db, LangItem::Not, &name![not])?,
403+
ast::UnaryOp::Neg => self.lang_trait_fn(db, LangItem::Neg, &name![neg])?,
384404
};
405+
385406
let ty = self.ty_of_expr(db, &prefix_expr.expr()?)?;
386407

387-
let (op_trait, op_fn) = self.lang_trait_fn(db, lang_item, &fn_name)?;
388408
// HACK: subst for all methods coincides with that for their trait because the methods
389409
// don't have any generic parameters, so we skip building another subst for the methods.
390410
let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build();

crates/ide/src/goto_definition.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,6 +1977,33 @@ fn f() {
19771977
);
19781978
}
19791979

1980+
#[test]
1981+
fn goto_deref_mut() {
1982+
check(
1983+
r#"
1984+
//- minicore: deref, deref_mut
1985+
1986+
struct Foo;
1987+
struct Bar;
1988+
1989+
impl core::ops::Deref for Foo {
1990+
type Target = Bar;
1991+
fn deref(&self) -> &Self::Target {}
1992+
}
1993+
1994+
impl core::ops::DerefMut for Foo {
1995+
fn deref_mut(&mut self) -> &mut Self::Target {}
1996+
//^^^^^^^^^
1997+
}
1998+
1999+
fn f() {
2000+
let a = Foo;
2001+
$0*a = Bar;
2002+
}
2003+
"#,
2004+
);
2005+
}
2006+
19802007
#[test]
19812008
fn goto_bin_op() {
19822009
check(

0 commit comments

Comments
 (0)