Repository: rust-analyzer/rust-analyzer
Pull request #544 (2019-01-14 22:17:01)

Implement rudimentary type inference for unary operators

flodiebold: Looks good to me, but it probably needs to be rebased after the `Canceled` changes :) Otherwise r=me
matklad: bors delegate+
bors[bot]: :v: marcusklaas can now approve this pull request

Patch:
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 4e8dc0c546..6633388447 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -182,7 +182,7 @@ pub enum Expr {
     },
     UnaryOp {
         expr: ExprId,
-        op: Option<UnaryOp>,
+        op: UnaryOp,
     },
     BinaryOp {
         lhs: ExprId,
@@ -612,8 +612,11 @@ impl ExprCollector {
             }
             ast::ExprKind::PrefixExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
-                let op = e.op();
-                self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
+                if let Some(op) = e.op() {
+                    self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
+                } else {
+                    self.alloc_expr(Expr::Missing, syntax_ptr)
+                }
             }
             ast::ExprKind::LambdaExpr(e) => {
                 let mut args = Vec::new();
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index dbbbce795d..85d4dc05ce 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -1051,7 +1051,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             Expr::UnaryOp { expr, op } => {
                 let inner_ty = self.infer_expr(*expr, &Expectation::none());
                 match op {
-                    Some(UnaryOp::Deref) => {
+                    UnaryOp::Deref => {
                         if let Some(derefed_ty) = inner_ty.builtin_deref() {
                             derefed_ty
                         } else {
@@ -1059,7 +1059,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                             Ty::Unknown
                         }
                     }
-                    _ => Ty::Unknown,
+                    UnaryOp::Neg => {
+                        match inner_ty {
+                            Ty::Int(primitive::UncertainIntTy::Unknown)
+                            | Ty::Int(primitive::UncertainIntTy::Signed(..))
+                            | Ty::Infer(InferTy::IntVar(..))
+                            | Ty::Infer(InferTy::FloatVar(..))
+                            | Ty::Float(..) => inner_ty,
+                            // TODO: resolve ops::Neg trait
+                            _ => Ty::Unknown,
+                        }
+                    }
+                    UnaryOp::Not if inner_ty == Ty::Bool => Ty::Bool,
+                    // TODO: resolve ops::Not trait for inner_ty
+                    UnaryOp::Not => Ty::Unknown,
                 }
             }
             Expr::BinaryOp { lhs, rhs, op } => match op {


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 929fee04ce..5d7bc25cc4 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -158,6 +158,29 @@ fn test() {
     );
 }
 
+#[test]
+fn infer_unary_op() {
+    check_inference(
+        r#"
+enum SomeType {}
+
+fn test(x: SomeType) {
+    let b = false;
+    let c = !b;
+    let a = 100;
+    let d: i128 = -a;
+    let e = -100;
+    let f = !!!true;
+    -3.14;
+    -x;
+    !x;
+    -"hello";
+}
+"#,
+        "unary_op.txt",
+    );
+}
+
 #[test]
 fn infer_backwards() {
     check_inference(
diff --git a/crates/ra_hir/src/ty/tests/data/unary_op.txt b/crates/ra_hir/src/ty/tests/data/unary_op.txt
new file mode 100644
index 0000000000..203022e829
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/unary_op.txt
@@ -0,0 +1,28 @@
+[27; 28) 'x': SomeType
+[40; 197) '{     ...lo"; }': ()
+[50; 51) 'b': bool
+[54; 59) 'false': bool
+[69; 70) 'c': bool
+[73; 75) '!b': bool
+[74; 75) 'b': bool
+[85; 86) 'a': i128
+[89; 92) '100': i128
+[102; 103) 'd': i128
+[112; 114) '-a': i128
+[113; 114) 'a': i128
+[124; 125) 'e': i32
+[128; 132) '-100': i32
+[129; 132) '100': i32
+[142; 143) 'f': bool
+[146; 153) '!!!true': bool
+[147; 153) '!!true': bool
+[148; 153) '!true': bool
+[149; 153) 'true': bool
+[159; 164) '-3.14': f64
+[160; 164) '3.14': f64
+[170; 172) '-x': [unknown]
+[171; 172) 'x': SomeType
+[178; 180) '!x': [unknown]
+[179; 180) 'x': SomeType
+[186; 194) '-"hello"': [unknown]
+[187; 194) '"hello"': &str