about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-10-21 19:43:15 -0700
committerEsteban Küber <esteban@kuber.com.ar>2020-10-23 08:06:13 -0700
commit671d7c4afb36a7dcedf9ec8a6f3ef00c19bfc260 (patch)
tree09e30ca245dd37c3a4c0977935e0441da485910c
parenta4ee3ca1e4f1177516139a4704456958c7d08c91 (diff)
downloadrust-671d7c4afb36a7dcedf9ec8a6f3ef00c19bfc260.tar.gz
rust-671d7c4afb36a7dcedf9ec8a6f3ef00c19bfc260.zip
Account for possible boxable `impl Future` in semicolon removal suggestions
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs46
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs4
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs2
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs57
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs23
-rw-r--r--src/test/ui/suggestions/match-prev-arm-needing-semi.rs15
-rw-r--r--src/test/ui/suggestions/match-prev-arm-needing-semi.stderr37
7 files changed, 152 insertions, 32 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 3a0ec6327c1..f6aa3c3d5ba 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -688,13 +688,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     };
                     let msg = "`match` arms have incompatible types";
                     err.span_label(outer_error_span, msg);
-                    if let Some(sp) = semi_span {
-                        err.span_suggestion_short(
-                            sp,
-                            "consider removing this semicolon",
-                            String::new(),
-                            Applicability::MachineApplicable,
-                        );
+                    if let Some((sp, boxed)) = semi_span {
+                        if boxed {
+                            err.span_suggestion_verbose(
+                                sp,
+                                "consider removing this semicolon and boxing the expression",
+                                String::new(),
+                                Applicability::HasPlaceholders,
+                            );
+                        } else {
+                            err.span_suggestion_short(
+                                sp,
+                                "consider removing this semicolon",
+                                String::new(),
+                                Applicability::MachineApplicable,
+                            );
+                        }
                     }
                     if let Some(ret_sp) = opt_suggest_box_span {
                         // Get return type span and point to it.
@@ -717,13 +726,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 if let Some(sp) = outer {
                     err.span_label(sp, "`if` and `else` have incompatible types");
                 }
-                if let Some(sp) = semicolon {
-                    err.span_suggestion_short(
-                        sp,
-                        "consider removing this semicolon",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    );
+                if let Some((sp, boxed)) = semicolon {
+                    if boxed {
+                        err.span_suggestion_verbose(
+                            sp,
+                            "consider removing this semicolon and boxing the expression",
+                            String::new(),
+                            Applicability::HasPlaceholders,
+                        );
+                    } else {
+                        err.span_suggestion_short(
+                            sp,
+                            "consider removing this semicolon",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
                 }
                 if let Some(ret_sp) = opt_suggest_box_span {
                     self.suggest_boxing_for_return_impl_trait(
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index bbc46b8d608..daa3010abb5 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -344,7 +344,7 @@ static_assert_size!(ObligationCauseCode<'_>, 32);
 pub struct MatchExpressionArmCause<'tcx> {
     pub arm_span: Span,
     pub scrut_span: Span,
-    pub semi_span: Option<Span>,
+    pub semi_span: Option<(Span, bool)>,
     pub source: hir::MatchSource,
     pub prior_arms: Vec<Span>,
     pub last_ty: Ty<'tcx>,
@@ -357,7 +357,7 @@ pub struct IfExpressionCause {
     pub then: Span,
     pub else_sp: Span,
     pub outer: Option<Span>,
-    pub semicolon: Option<Span>,
+    pub semicolon: Option<(Span, bool)>,
     pub opt_suggest_box_span: Option<Span>,
 }
 
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 94e886be54d..27c85317e82 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -521,7 +521,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         block: &'tcx hir::Block<'tcx>,
         expected_ty: Option<Ty<'tcx>>,
-    ) -> (Span, Option<Span>) {
+    ) -> (Span, Option<(Span, bool)>) {
         if let Some(expr) = &block.expr {
             (expr.span, None)
         } else if let Some(stmt) = block.stmts.last() {
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index 017b0abd1d6..f26a168bcb2 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -1061,7 +1061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         blk: &'tcx hir::Block<'tcx>,
         expected_ty: Ty<'tcx>,
-    ) -> Option<Span> {
+    ) -> Option<(Span, bool)> {
         // Be helpful when the user wrote `{... expr;}` and
         // taking the `;` off is enough to fix the error.
         let last_stmt = blk.stmts.last()?;
@@ -1070,13 +1070,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => return None,
         };
         let last_expr_ty = self.node_ty(last_expr.hir_id);
-        if matches!(last_expr_ty.kind(), ty::Error(_))
-            || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()
+        let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
+            (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
+                debug!(
+                    "both opaque, likely future {:?} {:?} {:?} {:?}",
+                    last_def_id, last_bounds, exp_def_id, exp_bounds
+                );
+                let last_hir_id = self.tcx.hir().local_def_id_to_hir_id(last_def_id.expect_local());
+                let exp_hir_id = self.tcx.hir().local_def_id_to_hir_id(exp_def_id.expect_local());
+                if let (
+                    hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
+                    hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
+                ) = (
+                    &self.tcx.hir().expect_item(last_hir_id).kind,
+                    &self.tcx.hir().expect_item(exp_hir_id).kind,
+                ) {
+                    debug!("{:?} {:?}", last_bounds, exp_bounds);
+                    last_bounds.iter().zip(exp_bounds.iter()).all(|(left, right)| {
+                        match (left, right) {
+                            (
+                                hir::GenericBound::Trait(tl, ml),
+                                hir::GenericBound::Trait(tr, mr),
+                            ) => {
+                                tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
+                                    && ml == mr
+                            }
+                            (
+                                hir::GenericBound::LangItemTrait(langl, _, _, argsl),
+                                hir::GenericBound::LangItemTrait(langr, _, _, argsr),
+                            ) => {
+                                // FIXME: consider the bounds!
+                                debug!("{:?} {:?}", argsl, argsr);
+                                langl == langr
+                            }
+                            _ => false,
+                        }
+                    })
+                } else {
+                    false
+                }
+            }
+            _ => false,
+        };
+        debug!(
+            "needs_box {:?} {:?} {:?}",
+            needs_box,
+            last_expr_ty.kind(),
+            self.can_sub(self.param_env, last_expr_ty, expected_ty)
+        );
+        if (matches!(last_expr_ty.kind(), ty::Error(_))
+            || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err())
+            && !needs_box
         {
             return None;
         }
         let original_span = original_sp(last_stmt.span, blk.span);
-        Some(original_span.with_lo(original_span.hi() - BytePos(1)))
+        Some((original_span.with_lo(original_span.hi() - BytePos(1)), needs_box))
     }
 
     // Instantiates the given path, which must refer to an item with the given
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index fd2f5eb5018..a124ad16612 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -758,13 +758,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
         err: &mut DiagnosticBuilder<'_>,
     ) {
-        if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
-            err.span_suggestion(
-                span_semi,
-                "consider removing this semicolon",
-                String::new(),
-                Applicability::MachineApplicable,
-            );
+        if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
+            if boxed {
+                err.span_suggestion_verbose(
+                    span_semi,
+                    "consider removing this semicolon and boxing the expression",
+                    String::new(),
+                    Applicability::HasPlaceholders,
+                );
+            } else {
+                err.span_suggestion_short(
+                    span_semi,
+                    "consider removing this semicolon",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            }
         }
     }
 
diff --git a/src/test/ui/suggestions/match-prev-arm-needing-semi.rs b/src/test/ui/suggestions/match-prev-arm-needing-semi.rs
index d8d6de4bf55..9704242e105 100644
--- a/src/test/ui/suggestions/match-prev-arm-needing-semi.rs
+++ b/src/test/ui/suggestions/match-prev-arm-needing-semi.rs
@@ -14,6 +14,7 @@ fn extra_semicolon() {
 }
 
 async fn async_dummy() {} //~ NOTE the `Output` of this `async fn`'s found opaque type
+async fn async_dummy2() {} //~ NOTE the `Output` of this `async fn`'s found opaque type
 
 async fn async_extra_semicolon_same() {
     let _ = match true { //~ NOTE `match` arms have incompatible types
@@ -28,5 +29,17 @@ async fn async_extra_semicolon_same() {
     };
 }
 
-fn main() {}
+async fn async_extra_semicolon_different() {
+    let _ = match true { //~ NOTE `match` arms have incompatible types
+        true => {
+            async_dummy(); //~ NOTE this is found to be
+            //~^ HELP consider removing this semicolon
+        }
+        false => async_dummy2(), //~ ERROR `match` arms have incompatible types
+        //~^ NOTE expected `()`, found opaque type
+        //~| NOTE expected type `()`
+        //~| HELP consider `await`ing on the `Future`
+    };
+}
 
+fn main() {}
diff --git a/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr b/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr
index e242a018843..00b02fbc007 100644
--- a/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr
+++ b/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr
@@ -1,5 +1,5 @@
 error[E0308]: `match` arms have incompatible types
-  --> $DIR/match-prev-arm-needing-semi.rs:24:18
+  --> $DIR/match-prev-arm-needing-semi.rs:25:18
    |
 LL |   async fn async_dummy() {}
    |                          - the `Output` of this `async fn`'s found opaque type
@@ -20,7 +20,7 @@ LL | |     };
    |
    = note:     expected type `()`
            found opaque type `impl Future`
-help: consider removing this semicolon
+help: consider removing this semicolon and boxing the expression
    |
 LL |             async_dummy()
    |                         --
@@ -30,6 +30,37 @@ LL |         false => async_dummy().await,
    |                               ^^^^^^
 
 error[E0308]: `match` arms have incompatible types
+  --> $DIR/match-prev-arm-needing-semi.rs:38:18
+   |
+LL |   async fn async_dummy2() {}
+   |                           - the `Output` of this `async fn`'s found opaque type
+...
+LL |       let _ = match true {
+   |  _____________-
+LL | |         true => {
+LL | |             async_dummy();
+   | |             -------------- this is found to be of type `()`
+LL | |
+LL | |         }
+LL | |         false => async_dummy2(),
+   | |                  ^^^^^^^^^^^^^^ expected `()`, found opaque type
+...  |
+LL | |
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note:     expected type `()`
+           found opaque type `impl Future`
+help: consider removing this semicolon and boxing the expression
+   |
+LL |             async_dummy()
+   |                         --
+help: consider `await`ing on the `Future`
+   |
+LL |         false => async_dummy2().await,
+   |                                ^^^^^^
+
+error[E0308]: `match` arms have incompatible types
   --> $DIR/match-prev-arm-needing-semi.rs:11:18
    |
 LL |       let _ = match true {
@@ -48,6 +79,6 @@ LL | |
 LL | |     };
    | |_____- `match` arms have incompatible types
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0308`.