about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs41
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs30
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs1
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs17
9 files changed, 129 insertions, 26 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index afdc49a2dc5..e83ce6dc42c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -372,6 +372,7 @@ language_item_table! {
     DerefMut,                sym::deref_mut,           deref_mut_trait,            Target::Trait,          GenericRequirement::Exact(0);
     DerefTarget,             sym::deref_target,        deref_target,               Target::AssocTy,        GenericRequirement::None;
     Receiver,                sym::receiver,            receiver_trait,             Target::Trait,          GenericRequirement::None;
+    ReceiverTarget,           sym::receiver_target,     receiver_target,            Target::AssocTy,        GenericRequirement::None;
 
     Fn,                      sym::fn_,                 fn_trait,                   Target::Trait,          GenericRequirement::Exact(1);
     FnMut,                   sym::fn_mut,              fn_mut_trait,               Target::Trait,          GenericRequirement::Exact(1);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index 2c7076da11a..62feca5f8cb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -39,7 +39,7 @@ pub fn autoderef(
 ) -> impl Iterator<Item = Ty> {
     let mut table = InferenceTable::new(db, env);
     let ty = table.instantiate_canonical(ty);
-    let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false);
+    let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false);
     let mut v = Vec::new();
     while let Some((ty, _steps)) = autoderef.next() {
         // `ty` may contain unresolved inference variables. Since there's no chance they would be
@@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> {
     at_start: bool,
     steps: T,
     explicit: bool,
+    use_receiver_trait: bool,
 }
 
 impl<'table, 'db> Autoderef<'table, 'db> {
-    pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
+    pub(crate) fn new(
+        table: &'table mut InferenceTable<'db>,
+        ty: Ty,
+        explicit: bool,
+        use_receiver_trait: bool,
+    ) -> Self {
         let ty = table.resolve_ty_shallow(&ty);
-        Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit }
+        Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait }
     }
 
     pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
@@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> {
         table: &'table mut InferenceTable<'db>,
         ty: Ty,
         explicit: bool,
+        use_receiver_trait: bool,
     ) -> Self {
         let ty = table.resolve_ty_shallow(&ty);
-        Autoderef { table, ty, at_start: true, steps: 0, explicit }
+        Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait }
     }
 }
 
@@ -137,7 +144,8 @@ impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
             return None;
         }
 
-        let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?;
+        let (kind, new_ty) =
+            autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?;
 
         self.steps.push(kind, &self.ty);
         self.ty = new_ty;
@@ -150,11 +158,12 @@ pub(crate) fn autoderef_step(
     table: &mut InferenceTable<'_>,
     ty: Ty,
     explicit: bool,
+    use_receiver_trait: bool,
 ) -> Option<(AutoderefKind, Ty)> {
     if let Some(derefed) = builtin_deref(table.db, &ty, explicit) {
         Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
     } else {
-        Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
+        Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?))
     }
 }
 
@@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>(
 pub(crate) fn deref_by_trait(
     table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
     ty: Ty,
+    use_receiver_trait: bool,
 ) -> Option<Ty> {
     let _p = tracing::info_span!("deref_by_trait").entered();
     if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
@@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait(
         return None;
     }
 
-    let deref_trait =
-        db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
+    let trait_id = || {
+        if use_receiver_trait {
+            if let Some(receiver) =
+                db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
+            {
+                return Some(receiver);
+            }
+        }
+        // Old rustc versions might not have `Receiver` trait.
+        // Fallback to `Deref` if they don't
+        db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())
+    };
+    let trait_id = trait_id()?;
     let target = db
-        .trait_data(deref_trait)
+        .trait_data(trait_id)
         .associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?;
 
     let projection = {
-        let b = TyBuilder::subst_for_def(db, deref_trait, None);
+        let b = TyBuilder::subst_for_def(db, trait_id, None);
         if b.remaining() != 1 {
             // the Target type + Deref trait should only have one generic parameter,
             // namely Deref's Self type
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index 2fe90a8a924..d40816ba8ce 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -420,7 +420,7 @@ impl InferenceTable<'_> {
 
         let snapshot = self.snapshot();
 
-        let mut autoderef = Autoderef::new(self, from_ty.clone(), false);
+        let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false);
         let mut first_error = None;
         let mut found = None;
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 6b6c0348dcb..b951443897c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -487,7 +487,7 @@ impl InferenceContext<'_> {
             }
             Expr::Call { callee, args, .. } => {
                 let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
-                let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
+                let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
                 let (res, derefed_callee) = loop {
                     let Some((callee_deref_ty, _)) = derefs.next() else {
                         break (None, callee_ty.clone());
@@ -854,7 +854,7 @@ impl InferenceContext<'_> {
                         if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) {
                             self.resolve_ty_shallow(derefed)
                         } else {
-                            deref_by_trait(&mut self.table, inner_ty)
+                            deref_by_trait(&mut self.table, inner_ty, false)
                                 .unwrap_or_else(|| self.err_ty())
                         }
                     }
@@ -1718,7 +1718,7 @@ impl InferenceContext<'_> {
         receiver_ty: &Ty,
         name: &Name,
     ) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
-        let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false);
+        let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false);
         let mut private_field = None;
         let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
             let (field_id, parameters) = match derefed_ty.kind(Interner) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 182032f0481..1cea67ee964 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -528,7 +528,7 @@ impl ReceiverAdjustments {
         let mut ty = table.resolve_ty_shallow(&ty);
         let mut adjust = Vec::new();
         for _ in 0..self.autoderefs {
-            match autoderef::autoderef_step(table, ty.clone(), true) {
+            match autoderef::autoderef_step(table, ty.clone(), true, false) {
                 None => {
                     never!("autoderef not possible for {:?}", ty);
                     ty = TyKind::Error.intern(Interner);
@@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver(
     // be found in any of the derefs of receiver_ty, so we have to go through
     // that, including raw derefs.
     table.run_in_snapshot(|table| {
-        let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
+        let mut autoderef =
+            autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
         while let Some((self_ty, _)) = autoderef.next() {
             iterate_inherent_methods(
                 &self_ty,
@@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver(
         ControlFlow::Continue(())
     })?;
     table.run_in_snapshot(|table| {
-        let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
+        let mut autoderef =
+            autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
         while let Some((self_ty, _)) = autoderef.next() {
             if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) {
                 // don't try to resolve methods on unknown types
@@ -1709,7 +1711,7 @@ fn autoderef_method_receiver(
     ty: Ty,
 ) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
     let mut deref_chain: Vec<_> = Vec::new();
-    let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false);
+    let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true);
     while let Some((ty, derefs)) = autoderef.next() {
         deref_chain.push((
             autoderef.table.canonicalize(ty),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 74acf23b75a..8866de22dfb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -1343,7 +1343,7 @@ fn foo<T: Trait>(a: &T) {
 fn autoderef_visibility_field() {
     check(
         r#"
-//- minicore: deref
+//- minicore: receiver
 mod a {
     pub struct Foo(pub char);
     pub struct Bar(i32);
@@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() {
     cov_mark::check!(autoderef_candidate_not_visible);
     check(
         r#"
-//- minicore: deref
+//- minicore: receiver
 mod a {
     pub struct Foo(pub char);
     impl Foo {
@@ -1741,7 +1741,7 @@ fn main() {
 fn deref_fun_1() {
     check_types(
         r#"
-//- minicore: deref
+//- minicore: receiver
 
 struct A<T, U>(T, U);
 struct B<T>(T);
@@ -1782,7 +1782,7 @@ fn test() {
 fn deref_fun_2() {
     check_types(
         r#"
-//- minicore: deref
+//- minicore: receiver
 
 struct A<T, U>(T, U);
 struct B<T>(T);
@@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into<Foo>) {
 fn bad_inferred_reference_2() {
     check_no_mismatches(
         r#"
-//- minicore: deref
+//- minicore: receiver
 trait ExactSizeIterator {
     fn len(&self) -> usize;
 }
@@ -2054,7 +2054,7 @@ fn foo() {
 fn box_deref_is_builtin() {
     check(
         r#"
-//- minicore: deref
+//- minicore: receiver
 use core::ops::Deref;
 
 #[lang = "owned_box"]
@@ -2087,7 +2087,7 @@ fn test() {
 fn manually_drop_deref_is_not_builtin() {
     check(
         r#"
-//- minicore: manually_drop, deref
+//- minicore: manually_drop, receiver
 struct Foo;
 impl Foo {
     fn foo(&self) {}
@@ -2105,7 +2105,7 @@ fn test() {
 fn mismatched_args_due_to_supertraits_with_deref() {
     check_no_mismatches(
         r#"
-//- minicore: deref
+//- minicore: receiver
 use core::ops::Deref;
 
 trait Trait1 {
@@ -2139,3 +2139,34 @@ fn problem_method<T: Trait3>() {
 "#,
     );
 }
+
+#[test]
+fn receiver_without_deref_impl() {
+    check(
+        r#"
+//- minicore: receiver
+use core::ops::Receiver;
+
+struct Foo;
+
+impl Foo {
+    fn foo1(self: &Bar) -> i32 { 42 }
+    fn foo2(self: Bar) -> bool { true }
+}
+
+struct Bar;
+
+impl Receiver for Bar {
+    type Target = Foo;
+}
+
+fn main() {
+    let bar = Bar;
+    let _v1 = bar.foo1();
+      //^^^ type: i32
+    let _v2 = bar.foo2();
+      //^^^ type: bool
+}
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 0d1765a7b76..d12654665ce 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -1475,4 +1475,34 @@ async fn bar() {
 "#,
         );
     }
+
+    #[test]
+    fn receiver_without_deref_impl_completion() {
+        check_no_kw(
+            r#"
+//- minicore: receiver
+use core::ops::Receiver;
+
+struct Foo;
+
+impl Foo {
+    fn foo(self: Bar) {}
+}
+
+struct Bar;
+
+impl Receiver for Bar {
+    type Target = Foo;
+}
+
+fn main() {
+    let bar = Bar;
+    bar.$0
+}
+"#,
+            expect![[r#"
+    me foo() fn(self: Bar)
+"#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index 7a090a6b6bb..9bc78ff87b8 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -394,6 +394,7 @@ define_symbols! {
     RangeToInclusive,
     Ready,
     receiver,
+    receiver_target,
     recursion_limit,
     register_attr,
     register_tool,
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index fd06736a252..4ed68d18e80 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -53,6 +53,7 @@
 //!     pin:
 //!     pointee: copy, send, sync, ord, hash, unpin
 //!     range:
+//!     receiver: deref
 //!     result:
 //!     send: sized
 //!     size_of: sized
@@ -513,10 +514,26 @@ pub mod ops {
             fn deref_mut(&mut self) -> &mut Self::Target;
         }
         // endregion:deref_mut
+
+        // region:receiver
+        #[lang = "receiver"]
+        pub trait Receiver {
+            #[lang = "receiver_target"]
+            type Target: ?Sized;
+        }
+
+        impl<P: ?Sized, T: ?Sized> Receiver for P
+        where
+            P: Deref<Target = T>,
+        {
+            type Target = T;
+        }
+        // endregion:receiver
     }
     pub use self::deref::{
         Deref,
         DerefMut, // :deref_mut
+        Receiver, // :receiver
     };
     // endregion:deref