about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2021-02-25 14:34:04 +0100
committerGitHub <noreply@github.com>2021-02-25 14:34:04 +0100
commit12ea0f6112d99310ba992f5656c29929da0bc6eb (patch)
tree144b02741c4b15840124a8085325e1cf4a023851
parent20928e0cbf65a65a5e96d56d2f665e62f593fdf8 (diff)
parentfa74d489a227054f20b0ffdda85e864e53cc7617 (diff)
downloadrust-12ea0f6112d99310ba992f5656c29929da0bc6eb.tar.gz
rust-12ea0f6112d99310ba992f5656c29929da0bc6eb.zip
Rollup merge of #82364 - osa1:issue82361, r=estebank
Improve error msgs when found type is deref of expected

This improves help messages in two cases:

- When expected type is `T` and found type is `&T`, we now look through blocks
  and suggest dereferencing the expression of the block, rather than the whole
  block.

- In the above case, if the expression is an `&`, we not suggest removing the
  `&` instead of adding `*`.

Both of these are demonstrated in the regression test. Before this patch the
first error in the test would be:

    error[E0308]: `if` and `else` have incompatible types
     --> test.rs:8:9
      |
    5 | /     if true {
    6 | |         a
      | |         - expected because of this
    7 | |     } else {
    8 | |         b
      | |         ^ expected `usize`, found `&usize`
    9 | |     };
      | |_____- `if` and `else` have incompatible types
      |
    help: consider dereferencing the borrow
      |
    7 |     } else *{
    8 |         b
    9 |     };
      |

Now:

    error[E0308]: `if` and `else` have incompatible types
     --> test.rs:8:9
      |
    5 | /     if true {
    6 | |         a
      | |         - expected because of this
    7 | |     } else {
    8 | |         b
      | |         ^
      | |         |
      | |         expected `usize`, found `&usize`
      | |         help: consider dereferencing the borrow: `*b`
    9 | |     };
      | |_____- `if` and `else` have incompatible types

The second error:

    error[E0308]: `if` and `else` have incompatible types
      --> test.rs:14:9
       |
    11 | /     if true {
    12 | |         1
       | |         - expected because of this
    13 | |     } else {
    14 | |         &1
       | |         ^^ expected integer, found `&{integer}`
    15 | |     };
       | |_____- `if` and `else` have incompatible types
       |
    help: consider dereferencing the borrow
       |
    13 |     } else *{
    14 |         &1
    15 |     };
       |

now:

    error[E0308]: `if` and `else` have incompatible types
      --> test.rs:14:9
       |
    11 | /     if true {
    12 | |         1
       | |         - expected because of this
    13 | |     } else {
    14 | |         &1
       | |         ^-
       | |         ||
       | |         |help: consider removing the `&`: `1`
       | |         expected integer, found `&{integer}`
    15 | |     };
       | |_____- `if` and `else` have incompatible types

Fixes #82361

---

r? ````@estebank````
-rw-r--r--compiler/rustc_hir/src/hir.rs8
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs28
-rw-r--r--src/test/ui/suggestions/issue-82361.fixed24
-rw-r--r--src/test/ui/suggestions/issue-82361.rs24
-rw-r--r--src/test/ui/suggestions/issue-82361.stderr48
5 files changed, 128 insertions, 4 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 2fef7c2cc08..f4402843afc 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1577,6 +1577,14 @@ impl Expr<'_> {
         expr
     }
 
+    pub fn peel_blocks(&self) -> &Self {
+        let mut expr = self;
+        while let ExprKind::Block(Block { expr: Some(inner), .. }, _) = &expr.kind {
+            expr = inner;
+        }
+        expr
+    }
+
     pub fn can_have_side_effects(&self) -> bool {
         match self.peel_drop_temps().kind {
             ExprKind::Path(_) | ExprKind::Lit(_) => false,
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index 8d2004a543b..39b973ed371 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -616,10 +616,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             _ if sp == expr.span && !is_macro => {
                 if let Some(steps) = self.deref_steps(checked_ty, expected) {
+                    let expr = expr.peel_blocks();
+
                     if steps == 1 {
-                        // For a suggestion to make sense, the type would need to be `Copy`.
-                        if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) {
-                            if let Ok(code) = sm.span_to_snippet(sp) {
+                        if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
+                            // If the expression has `&`, removing it would fix the error
+                            let prefix_span = expr.span.with_hi(inner.span.lo());
+                            let message = match mutbl {
+                                hir::Mutability::Not => "consider removing the `&`",
+                                hir::Mutability::Mut => "consider removing the `&mut`",
+                            };
+                            let suggestion = String::new();
+                            return Some((
+                                prefix_span,
+                                message,
+                                suggestion,
+                                Applicability::MachineApplicable,
+                            ));
+                        } else if self.infcx.type_is_copy_modulo_regions(
+                            self.param_env,
+                            expected,
+                            sp,
+                        ) {
+                            // For this suggestion to make sense, the type would need to be `Copy`.
+                            if let Ok(code) = sm.span_to_snippet(expr.span) {
                                 let message = if checked_ty.is_region_ptr() {
                                     "consider dereferencing the borrow"
                                 } else {
@@ -631,7 +651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     format!("*{}", code)
                                 };
                                 return Some((
-                                    sp,
+                                    expr.span,
                                     message,
                                     suggestion,
                                     Applicability::MachineApplicable,
diff --git a/src/test/ui/suggestions/issue-82361.fixed b/src/test/ui/suggestions/issue-82361.fixed
new file mode 100644
index 00000000000..d72de982bf9
--- /dev/null
+++ b/src/test/ui/suggestions/issue-82361.fixed
@@ -0,0 +1,24 @@
+// run-rustfix
+
+fn main() {
+    let a: usize = 123;
+    let b: &usize = &a;
+
+    if true {
+        a
+    } else {
+        *b //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+}
diff --git a/src/test/ui/suggestions/issue-82361.rs b/src/test/ui/suggestions/issue-82361.rs
new file mode 100644
index 00000000000..c068f6d22b4
--- /dev/null
+++ b/src/test/ui/suggestions/issue-82361.rs
@@ -0,0 +1,24 @@
+// run-rustfix
+
+fn main() {
+    let a: usize = 123;
+    let b: &usize = &a;
+
+    if true {
+        a
+    } else {
+        b //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        &1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+
+    if true {
+        1
+    } else {
+        &mut 1 //~ ERROR `if` and `else` have incompatible types [E0308]
+    };
+}
diff --git a/src/test/ui/suggestions/issue-82361.stderr b/src/test/ui/suggestions/issue-82361.stderr
new file mode 100644
index 00000000000..c19d59ccd4c
--- /dev/null
+++ b/src/test/ui/suggestions/issue-82361.stderr
@@ -0,0 +1,48 @@
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/issue-82361.rs:10:9
+   |
+LL | /     if true {
+LL | |         a
+   | |         - expected because of this
+LL | |     } else {
+LL | |         b
+   | |         ^
+   | |         |
+   | |         expected `usize`, found `&usize`
+   | |         help: consider dereferencing the borrow: `*b`
+LL | |     };
+   | |_____- `if` and `else` have incompatible types
+
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/issue-82361.rs:16:9
+   |
+LL | /     if true {
+LL | |         1
+   | |         - expected because of this
+LL | |     } else {
+LL | |         &1
+   | |         -^
+   | |         |
+   | |         expected integer, found `&{integer}`
+   | |         help: consider removing the `&`
+LL | |     };
+   | |_____- `if` and `else` have incompatible types
+
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/issue-82361.rs:22:9
+   |
+LL | /     if true {
+LL | |         1
+   | |         - expected because of this
+LL | |     } else {
+LL | |         &mut 1
+   | |         -----^
+   | |         |
+   | |         expected integer, found `&mut {integer}`
+   | |         help: consider removing the `&mut`
+LL | |     };
+   | |_____- `if` and `else` have incompatible types
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.