Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Operator overloading type inference Error
<!--
Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
Forum for questions: https://users.rust-lang.org/c/ide/14

Before submitting, please make sure that you're not running into one of these known issues:

 1. local imports (`use` statements) don't work: #1165
 2. local items don't work: #1559
 3. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file)
 4. some settings are required for procedural macro and build script support (`rust-analyzer.cargo.loadOutDirsFromCheck`, `rust-analyzer.procMacro.enable`): #6448
 5. some platform-specific imports are not resolved: #6038
 6. the official `rust-lang.rust` VS Code extension conflicts with `rust-analyzer`: #6463

Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
-->
```rust
use std::ops::Add;

struct wrapper_f64{test: f64}
impl Add<usize> for wrapper_f64{
    type Output = Self;
    fn add(self , rhs : usize)-> wrapper_f64{
        wrapper_f64{test: self.test + rhs as f64}
    }
}
fn main(){
    let f64_in = wrapper_f64{test: 1.0};
    let usize_num : usize = 2;
    let res = f64_in + usize_num;

}
```
when I run those codes on
**rustc:** stable-x86_64-unknown-linux-gnu - rustc 1.49.0
**OS: window wsl**
**RA** is update-to-date
RA type inference like
![img](https://user-images.githubusercontent.com/42641719/103534786-86fcb200-4eca-11eb-87ee-cccd53b6ccf0.png)
And the real type of **res** should be **wrapper_f64**

Pull request:
Implement more precise binary op return type heuristic
Should fix #7150

Patch:
diff --git a/crates/hir_ty/src/op.rs b/crates/hir_ty/src/op.rs
index 0491c5cb42..0222de2bc2 100644
--- a/crates/hir_ty/src/op.rs
+++ b/crates/hir_ty/src/op.rs
@@ -9,21 +9,55 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty {
         BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
         BinaryOp::Assignment { .. } => TyBuilder::unit(),
         BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => {
-            match lhs_ty.kind(&Interner) {
+            // all integer combinations are valid here
+            if matches!(
+                lhs_ty.kind(&Interner),
                 TyKind::Scalar(Scalar::Int(_))
-                | TyKind::Scalar(Scalar::Uint(_))
-                | TyKind::Scalar(Scalar::Float(_)) => lhs_ty,
-                TyKind::InferenceVar(_, TyVariableKind::Integer)
-                | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty,
-                _ => TyKind::Error.intern(&Interner),
+                    | TyKind::Scalar(Scalar::Uint(_))
+                    | TyKind::InferenceVar(_, TyVariableKind::Integer)
+            ) && matches!(
+                rhs_ty.kind(&Interner),
+                TyKind::Scalar(Scalar::Int(_))
+                    | TyKind::Scalar(Scalar::Uint(_))
+                    | TyKind::InferenceVar(_, TyVariableKind::Integer)
+            ) {
+                lhs_ty
+            } else {
+                TyKind::Error.intern(&Interner)
             }
         }
-        BinaryOp::ArithOp(_) => match rhs_ty.kind(&Interner) {
-            TyKind::Scalar(Scalar::Int(_))
-            | TyKind::Scalar(Scalar::Uint(_))
-            | TyKind::Scalar(Scalar::Float(_)) => rhs_ty,
-            TyKind::InferenceVar(_, TyVariableKind::Integer)
-            | TyKind::InferenceVar(_, TyVariableKind::Float) => rhs_ty,
+        BinaryOp::ArithOp(_) => match (lhs_ty.kind(&Interner), rhs_ty.kind(&Interner)) {
+            // (int, int) | (uint, uint) | (float, float)
+            (TyKind::Scalar(Scalar::Int(_)), TyKind::Scalar(Scalar::Int(_)))
+            | (TyKind::Scalar(Scalar::Uint(_)), TyKind::Scalar(Scalar::Uint(_)))
+            | (TyKind::Scalar(Scalar::Float(_)), TyKind::Scalar(Scalar::Float(_))) => rhs_ty,
+            // ({int}, int) | ({int}, uint)
+            (TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Int(_)))
+            | (TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Uint(_))) => {
+                rhs_ty
+            }
+            // (int, {int}) | (uint, {int})
+            (TyKind::Scalar(Scalar::Int(_)), TyKind::InferenceVar(_, TyVariableKind::Integer))
+            | (TyKind::Scalar(Scalar::Uint(_)), TyKind::InferenceVar(_, TyVariableKind::Integer)) => {
+                lhs_ty
+            }
+            // ({float} | float)
+            (TyKind::InferenceVar(_, TyVariableKind::Float), TyKind::Scalar(Scalar::Float(_))) => {
+                rhs_ty
+            }
+            // (float, {float})
+            (TyKind::Scalar(Scalar::Float(_)), TyKind::InferenceVar(_, TyVariableKind::Float)) => {
+                lhs_ty
+            }
+            // ({int}, {int}) | ({float}, {float})
+            (
+                TyKind::InferenceVar(_, TyVariableKind::Integer),
+                TyKind::InferenceVar(_, TyVariableKind::Integer),
+            )
+            | (
+                TyKind::InferenceVar(_, TyVariableKind::Float),
+                TyKind::InferenceVar(_, TyVariableKind::Float),
+            ) => rhs_ty,
             _ => TyKind::Error.intern(&Interner),
         },
     }
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 16991b6880..cc4ac9ea2e 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -29,7 +29,7 @@ pub struct CompletionItem {
     /// Range of identifier that is being completed.
     ///
     /// It should be used primarily for UI, but we also use this to convert
-    /// generic TextEdit into LSP's completion edit (see conv.rs).
+    /// genetic TextEdit into LSP's completion edit (see conv.rs).
     ///
     /// `source_range` must contain the completion offset. `insert_text` should
     /// start with what `source_range` points to, or VSCode will filter out the
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index f809667e92..e012b4452f 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -656,19 +656,6 @@ impl Config {
     pub fn code_lens_refresh(&self) -> bool {
         try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false)
     }
-    pub fn insert_replace_support(&self) -> bool {
-        try_or!(
-            self.caps
-                .text_document
-                .as_ref()?
-                .completion
-                .as_ref()?
-                .completion_item
-                .as_ref()?
-                .insert_replace_support?,
-            false
-        )
-    }
 }
 
 #[derive(Deserialize, Debug, Clone)]
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index edfa42eb59..31d8c487be 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -664,13 +664,10 @@ pub(crate) fn handle_completion(
     };
     let line_index = snap.file_line_index(position.file_id)?;
 
-    let insert_replace_support =
-        snap.config.insert_replace_support().then(|| text_document_position.position);
     let items: Vec<CompletionItem> = items
         .into_iter()
         .flat_map(|item| {
-            let mut new_completion_items =
-                to_proto::completion_item(insert_replace_support, &line_index, item.clone());
+            let mut new_completion_items = to_proto::completion_item(&line_index, item.clone());
 
             if completion_config.enable_imports_on_the_fly {
                 for new_item in &mut new_completion_items {
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 73c4193e88..2ac487632f 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -150,16 +150,8 @@ pub(crate) fn all_edits_are_disjoint(
             edit_ranges.push(edit.range);
         }
         Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => {
-            let replace = edit.replace;
-            let insert = edit.insert;
-            if replace.start != insert.start
-                || insert.start > insert.end
-                || insert.end > replace.end
-            {
-                // insert has to be a prefix of replace but it is not
-                return false;
-            }
-            edit_ranges.push(replace);
+            edit_ranges.push(edit.insert);
+            edit_ranges.push(edit.replace);
         }
         None => {}
     }
@@ -318,6 +310,18 @@ mod tests {
             "Completion with disjoint edits fails the validation even with empty extra edits"
         );
 
+        completion_with_joint_edits.text_edit =
+            Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
+                new_text: "new_text".to_string(),
+                insert: disjoint_edit.range,
+                replace: joint_edit.range,
+            }));
+        completion_with_joint_edits.additional_text_edits = None;
+        assert!(
+            !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
+            "Completion with disjoint edits fails the validation even with empty extra edits"
+        );
+
         completion_with_joint_edits.text_edit =
             Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
                 new_text: "new_text".to_string(),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 9fac562ff7..2ac31d981b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -145,23 +145,6 @@ pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::Text
     lsp_types::TextEdit { range, new_text }
 }
 
-pub(crate) fn completion_text_edit(
-    line_index: &LineIndex,
-    insert_replace_support: Option<lsp_types::Position>,
-    indel: Indel,
-) -> lsp_types::CompletionTextEdit {
-    let text_edit = text_edit(line_index, indel);
-    match insert_replace_support {
-        Some(cursor_pos) => lsp_types::InsertReplaceEdit {
-            new_text: text_edit.new_text,
-            insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
-            replace: text_edit.range,
-        }
-        .into(),
-        None => text_edit.into(),
-    }
-}
-
 pub(crate) fn snippet_text_edit(
     line_index: &LineIndex,
     is_snippet: bool,
@@ -196,7 +179,6 @@ pub(crate) fn snippet_text_edit_vec(
 }
 
 pub(crate) fn completion_item(
-    insert_replace_support: Option<lsp_types::Position>,
     line_index: &LineIndex,
     item: CompletionItem,
 ) -> Vec<lsp_types::CompletionItem> {
@@ -208,7 +190,7 @@ pub(crate) fn completion_item(
     for indel in item.text_edit().iter() {
         if indel.delete.contains_range(source_range) {
             text_edit = Some(if indel.delete == source_range {
-                self::completion_text_edit(line_index, insert_replace_support, indel.clone())
+                self::text_edit(line_index, indel.clone())
             } else {
                 assert!(source_range.end() == indel.delete.end());
                 let range1 = TextRange::new(indel.delete.start(), source_range.start());
@@ -216,7 +198,7 @@ pub(crate) fn completion_item(
                 let indel1 = Indel::replace(range1, String::new());
                 let indel2 = Indel::replace(range2, indel.insert.clone());
                 additional_text_edits.push(self::text_edit(line_index, indel1));
-                self::completion_text_edit(line_index, insert_replace_support, indel2)
+                self::text_edit(line_index, indel2)
             })
         } else {
             assert!(source_range.intersect(indel.delete).is_none());
@@ -231,7 +213,7 @@ pub(crate) fn completion_item(
         detail: item.detail().map(|it| it.to_string()),
         filter_text: Some(item.lookup().to_string()),
         kind: item.kind().map(completion_item_kind),
-        text_edit: Some(text_edit),
+        text_edit: Some(text_edit.into()),
         additional_text_edits: Some(additional_text_edits),
         documentation: item.documentation().map(documentation),
         deprecated: Some(item.deprecated()),
@@ -1154,7 +1136,7 @@ mod tests {
             .unwrap()
             .into_iter()
             .filter(|c| c.label().ends_with("arg"))
-            .map(|c| completion_item(None, &line_index, c))
+            .map(|c| completion_item(&line_index, c))
             .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
             .collect();
         expect_test::expect![[r#"


Test patch:
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 1c1aa491d6..1879dbc783 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -1919,10 +1919,26 @@ fn closure_1() {
 fn closure_2() {
     check_infer_with_mismatches(
         r#"
+        #[lang = "add"]
+        pub trait Add<Rhs = Self> {
+            type Output;
+            fn add(self, rhs: Rhs) -> Self::Output;
+        }
+
         trait FnOnce<Args> {
             type Output;
         }
 
+        impl Add for u64 {
+            type Output = Self;
+            fn add(self, rhs: u64) -> Self::Output {0}
+        }
+
+        impl Add for u128 {
+            type Output = Self;
+            fn add(self, rhs: u128) -> Self::Output {0}
+        }
+
         fn test<F: FnOnce(u32) -> u64>(f: F) {
             f(1);
             let g = |v| v + 1;
@@ -1931,26 +1947,36 @@ fn closure_2() {
         }
         "#,
         expect![[r#"
-            72..73 'f': F
-            78..154 '{     ...+ v; }': ()
-            84..85 'f': F
-            84..88 'f(1)': {unknown}
-            86..87 '1': i32
-            98..99 'g': |u64| -> i32
-            102..111 '|v| v + 1': |u64| -> i32
-            103..104 'v': u64
-            106..107 'v': u64
-            106..111 'v + 1': i32
-            110..111 '1': i32
-            117..118 'g': |u64| -> i32
-            117..124 'g(1u64)': i32
-            119..123 '1u64': u64
-            134..135 'h': |u128| -> u128
-            138..151 '|v| 1u128 + v': |u128| -> u128
-            139..140 'v': u128
-            142..147 '1u128': u128
-            142..151 '1u128 + v': u128
-            150..151 'v': u128
+            72..76 'self': Self
+            78..81 'rhs': Rhs
+            203..207 'self': u64
+            209..212 'rhs': u64
+            235..238 '{0}': u64
+            236..237 '0': u64
+            297..301 'self': u128
+            303..306 'rhs': u128
+            330..333 '{0}': u128
+            331..332 '0': u128
+            368..369 'f': F
+            374..450 '{     ...+ v; }': ()
+            380..381 'f': F
+            380..384 'f(1)': {unknown}
+            382..383 '1': i32
+            394..395 'g': |u64| -> u64
+            398..407 '|v| v + 1': |u64| -> u64
+            399..400 'v': u64
+            402..403 'v': u64
+            402..407 'v + 1': u64
+            406..407 '1': u64
+            413..414 'g': |u64| -> u64
+            413..420 'g(1u64)': u64
+            415..419 '1u64': u64
+            430..431 'h': |u128| -> u128
+            434..447 '|v| 1u128 + v': |u128| -> u128
+            435..436 'v': u128
+            438..443 '1u128': u128
+            438..447 '1u128 + v': u128
+            446..447 'v': u128
         "#]],
     );
 }
@@ -3443,3 +3469,50 @@ pub trait Deserialize {
     "#,
     );
 }
+
+#[test]
+fn bin_op_adt_with_rhs_primitive() {
+    check_infer_with_mismatches(
+        r#"
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+    type Output;
+    fn add(self, rhs: Rhs) -> Self::Output;
+}
+
+struct Wrapper(u32);
+impl Add<u32> for Wrapper {
+    type Output = Self;
+    fn add(self, rhs: u32) -> Wrapper {
+        Wrapper(rhs)
+    }
+}
+fn main(){
+    let wrapped = Wrapper(10);
+    let num: u32 = 2;
+    let res = wrapped + num;
+
+}"#,
+        expect![[r#"
+            72..76 'self': Self
+            78..81 'rhs': Rhs
+            192..196 'self': Wrapper
+            198..201 'rhs': u32
+            219..247 '{     ...     }': Wrapper
+            229..236 'Wrapper': Wrapper(u32) -> Wrapper
+            229..241 'Wrapper(rhs)': Wrapper
+            237..240 'rhs': u32
+            259..345 '{     ...um;  }': ()
+            269..276 'wrapped': Wrapper
+            279..286 'Wrapper': Wrapper(u32) -> Wrapper
+            279..290 'Wrapper(10)': Wrapper
+            287..289 '10': u32
+            300..303 'num': u32
+            311..312 '2': u32
+            322..325 'res': Wrapper
+            328..335 'wrapped': Wrapper
+            328..341 'wrapped + num': Wrapper
+            338..341 'num': u32
+        "#]],
+    )
+}