about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <huyuumi.dev@gmail.com>2020-10-23 18:26:40 +0900
committerGitHub <noreply@github.com>2020-10-23 18:26:40 +0900
commit7ba519ec50cf2c48bae64eead90c3fda9110920b (patch)
treea16fe408b9992a4ce5412336c6ed50a318db3d39
parent3f462c22b53ee5ae2f56e2b268a15fd4dab9d22c (diff)
parentb0059500f6765612fbda6d33240116f7520d433a (diff)
downloadrust-7ba519ec50cf2c48bae64eead90c3fda9110920b.tar.gz
rust-7ba519ec50cf2c48bae64eead90c3fda9110920b.zip
Rollup merge of #78255 - dtolnay:match, r=lcnr
Reduce diagram mess in 'match arms have incompatible types' error

I noticed this wild diagram in https://twitter.com/a_hoverbear/status/1318960787105353728 which I think does not benefit from the big outer vertical span.

This PR shrinks the outer span to cover just the `match` keyword and scrutinee expression *if* at least one of the highlighted match arms involved in the error is multiline.

**Before:**

<pre>
<b>error[E0308]: `match` arms have incompatible types</b>
   <b>--&gt;</b> src/topology/builder.rs:141:35
    <b>|</b>
<b>120 |</b>             let transform = match transform {
    <b>|    _________________________-</b>
<b>121 |   |</b>             Transform::Function(t) =&gt; {
    <b>|  _|_______________________________________-</b>
<b>122 | | |</b>                 filter_event_type(input_rx, input_type).compat().flat_map(|v| {
<b>123 | | |</b>                     futures::stream::iter(match v {
<b>124 | | |</b>                         Err(e) =&gt; {
<b>...   | |</b>
<b>139 | | |</b>                 .compat();
<b>140 | | |</b>             }
    <b>| |_|_____________- this is found to be of type `()`</b>
<b>141 |   |</b>             Transform::Task(t) =&gt; t
    <b>|  _|___________________________________^</b>
<b>142 | | |</b>                 .transform(filter_event_type(input_rx, input_type))
<b>143 | | |</b>                 .forward(output)
<b>144 | | |</b>                 .map(|_| debug!("Finished"))
<b>145 | | |</b>                 .compat(),
    <b>| |_|_________________________^ expected `()`, found struct `futures::compat::Compat01As03`</b>
<b>146 |   |</b>         };
    <b>|   |_________- `match` arms have incompatible types</b>
    <b>|</b>
    <b>= note:</b> expected type `<b>()</b>`
             found struct `<b>futures::compat::Compat01As03&lt;futures::Map&lt;futures::stream::Forward&lt;std::boxed::Box&lt;dyn futures::Stream&lt;Error = (), Item = event::Event&gt; + std::marker::Send&gt;, topology::fanout::Fanout&gt;, [closure@src/topology/builder.rs:144:22: 144:44]&gt;&gt;</b>`
</pre>

**After:**

<pre>
<b>error[E0308]: `match` arms have incompatible types</b>
   <b>--&gt;</b> src/topology/builder.rs:141:35
    <b>|</b>
<b>120 |</b>             let transform = match transform {
    <b>|                             --------------- `match` arms have incompatible types</b>
<b>121 |</b>                 Transform::Function(t) =&gt; {
    <b>|  _________________________________________-</b>
<b>122 | |</b>                   filter_event_type(input_rx, input_type).compat().flat_map(|v| {
<b>123 | |</b>                       futures::stream::iter(match v {
<b>124 | |</b>                           Err(e) =&gt; {
<b>...   |</b>
<b>139 | |</b>                   .compat();
<b>140 | |</b>               }
    <b>| |_______________- this is found to be of type `()`</b>
<b>141 |</b>                 Transform::Task(t) =&gt; t
    <b>|  _____________________________________^</b>
<b>142 | |</b>                   .transform(filter_event_type(input_rx, input_type))
<b>143 | |</b>                   .forward(output)
<b>144 | |</b>                   .map(|_| debug!("Finished"))
<b>145 | |</b>                   .compat(),
    <b>| |___________________________^ expected `()`, found struct `futures::compat::Compat01As03`</b>
    <b>|</b>
    <b>= note:</b> expected type `<b>()</b>`
             found struct `<b>futures::compat::Compat01As03&lt;futures::Map&lt;futures::stream::Forward&lt;std::boxed::Box&lt;dyn futures::Stream&lt;Error = (), Item = event::Event&gt; + std::marker::Send&gt;, topology::fanout::Fanout&gt;, [closure@src/topology/builder.rs:144:22: 144:44]&gt;&gt;</b>`
</pre>

FYI @Hoverbear
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs16
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs1
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs1
-rw-r--r--src/test/ui/match/match-incompat-type-semi.rs10
-rw-r--r--src/test/ui/match/match-incompat-type-semi.stderr40
5 files changed, 53 insertions, 15 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 795c5a64d26..3a0ec6327c1 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -619,6 +619,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 scrut_hir_id,
                 opt_suggest_box_span,
                 arm_span,
+                scrut_span,
                 ..
             }) => match source {
                 hir::MatchSource::IfLetDesugar { .. } => {
@@ -664,18 +665,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                         Some(ty::error::ExpectedFound { expected, .. }) => expected,
                         _ => last_ty,
                     });
-                    let msg = "`match` arms have incompatible types";
-                    err.span_label(cause.span, msg);
+                    let source_map = self.tcx.sess.source_map();
+                    let mut any_multiline_arm = source_map.is_multiline(arm_span);
                     if prior_arms.len() <= 4 {
                         for sp in prior_arms {
+                            any_multiline_arm |= source_map.is_multiline(*sp);
                             err.span_label(*sp, format!("this is found to be of type `{}`", t));
                         }
                     } else if let Some(sp) = prior_arms.last() {
+                        any_multiline_arm |= source_map.is_multiline(*sp);
                         err.span_label(
                             *sp,
                             format!("this and all prior arms are found to be of type `{}`", t),
                         );
                     }
+                    let outer_error_span = if any_multiline_arm {
+                        // Cover just `match` and the scrutinee expression, not
+                        // the entire match body, to reduce diagram noise.
+                        cause.span.shrink_to_lo().to(scrut_span)
+                    } else {
+                        cause.span
+                    };
+                    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,
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 26962aa1083..bbc46b8d608 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -343,6 +343,7 @@ static_assert_size!(ObligationCauseCode<'_>, 32);
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
 pub struct MatchExpressionArmCause<'tcx> {
     pub arm_span: Span,
+    pub scrut_span: Span,
     pub semi_span: Option<Span>,
     pub source: hir::MatchSource,
     pub prior_arms: Vec<Span>,
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 7cb23dc0537..398e013e62f 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -201,6 +201,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         expr.span,
                         ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
                             arm_span,
+                            scrut_span: scrut.span,
                             semi_span,
                             source: match_src,
                             prior_arms: other_arms.clone(),
diff --git a/src/test/ui/match/match-incompat-type-semi.rs b/src/test/ui/match/match-incompat-type-semi.rs
index 9ab40fa3cce..37f6beabd33 100644
--- a/src/test/ui/match/match-incompat-type-semi.rs
+++ b/src/test/ui/match/match-incompat-type-semi.rs
@@ -39,4 +39,14 @@ fn main() {
         None => { //~ ERROR incompatible types
         },
     };
+
+    let _ = match Some(42) {
+        Some(x) => "rust-lang.org"
+            .chars()
+            .skip(1)
+            .chain(Some(x as u8 as char))
+            .take(10)
+            .any(char::is_alphanumeric),
+        None => {} //~ ERROR incompatible types
+    };
 }
diff --git a/src/test/ui/match/match-incompat-type-semi.stderr b/src/test/ui/match/match-incompat-type-semi.stderr
index 701f15fdc4b..008b1c1e93d 100644
--- a/src/test/ui/match/match-incompat-type-semi.stderr
+++ b/src/test/ui/match/match-incompat-type-semi.stderr
@@ -56,19 +56,33 @@ LL | |     };
 error[E0308]: `match` arms have incompatible types
   --> $DIR/match-incompat-type-semi.rs:39:17
    |
-LL |        let _ = match Some(42) {
-   |   _____________-
-LL |  |         Some(x) => {
-LL |  |             x
-   |  |             - this is found to be of type `{integer}`
-LL |  |         },
-LL |  |         None => {
-   |  |_________________^
-LL | ||         },
-   | ||_________^ expected integer, found `()`
-LL |  |     };
-   |  |_____- `match` arms have incompatible types
+LL |       let _ = match Some(42) {
+   |               -------------- `match` arms have incompatible types
+LL |           Some(x) => {
+LL |               x
+   |               - this is found to be of type `{integer}`
+LL |           },
+LL |           None => {
+   |  _________________^
+LL | |         },
+   | |_________^ expected integer, found `()`
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/match-incompat-type-semi.rs:50:17
+   |
+LL |       let _ = match Some(42) {
+   |               -------------- `match` arms have incompatible types
+LL |           Some(x) => "rust-lang.org"
+   |  ____________________-
+LL | |             .chars()
+LL | |             .skip(1)
+LL | |             .chain(Some(x as u8 as char))
+LL | |             .take(10)
+LL | |             .any(char::is_alphanumeric),
+   | |_______________________________________- this is found to be of type `bool`
+LL |           None => {}
+   |                   ^^ expected `bool`, found `()`
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0308`.