From 0fa3b4f940b4705dc5a1089e917f521b093fd0bc Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Wed, 18 Aug 2021 16:13:52 -0700 Subject: [PATCH] Make E0023 spans even more precise --- compiler/rustc_ty_utils/src/ty.rs | 13 +++- compiler/rustc_typeck/src/check/pat.rs | 39 ++++++++-- .../tuple_struct_destructure_fail.stderr | 32 +++----- src/test/ui/error-codes/E0023.stderr | 36 ++++----- src/test/ui/issues/issue-72574-2.stderr | 8 +- .../match/match-pattern-field-mismatch.stderr | 8 +- ...7-pat-tup-scrut-ty-diff-less-fields.stderr | 4 +- src/test/ui/pattern/issue-74539.stderr | 8 +- .../ui/pattern/pat-tuple-overfield.stderr | 76 ++++++++----------- .../ui/pattern/pat-tuple-underfield.stderr | 48 +++++------- .../ui/pattern/pattern-error-continue.stderr | 8 +- 11 files changed, 131 insertions(+), 149 deletions(-) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index b0d644ae028..7a6ace070de 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -223,7 +223,18 @@ fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> { } fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - tcx.hir().get_if_local(def_id).and_then(|node| node.ident()).map(|ident| ident.span) + tcx.hir() + .get_if_local(def_id) + .and_then(|node| match node { + // A `Ctor` doesn't have an identifier itself, but its parent + // struct/variant does. Compare with `hir::Map::opt_span`. + hir::Node::Ctor(ctor) => ctor + .ctor_hir_id() + .and_then(|ctor_id| tcx.hir().find(tcx.hir().get_parent_node(ctor_id))) + .and_then(|parent| parent.ident()), + _ => node.ident(), + }) + .map(|ident| ident.span) } /// If the given `DefId` describes an item belonging to a trait, diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 2ef79c7686c..341385731e7 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -15,7 +15,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Ident; -use rustc_span::{BytePos, DUMMY_SP}; +use rustc_span::{BytePos, MultiSpan, DUMMY_SP}; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use ty::VariantDef; @@ -990,11 +990,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let subpats_ending = pluralize!(subpats.len()); let fields_ending = pluralize!(fields.len()); - let fields_span = pat_span.trim_start(qpath.span()).unwrap_or(pat_span); + + let subpat_spans = if subpats.is_empty() { + vec![pat_span.trim_start(qpath.span()).unwrap_or(pat_span)] + } else { + subpats.iter().map(|p| p.span).collect() + }; + let last_subpat_span = *subpat_spans.last().unwrap(); let res_span = self.tcx.def_span(res.def_id()); + let def_ident_span = self.tcx.def_ident_span(res.def_id()).unwrap_or(res_span); + let field_def_spans = if fields.is_empty() { + vec![res_span.trim_start(def_ident_span).unwrap_or(res_span)] + } else { + fields.iter().map(|f| f.ident.span).collect() + }; + let last_field_def_span = *field_def_spans.last().unwrap(); + let mut err = struct_span_err!( self.tcx.sess, - fields_span, + MultiSpan::from_spans(subpat_spans.clone()), E0023, "this pattern has {} field{}, but the corresponding {} has {} field{}", subpats.len(), @@ -1004,11 +1018,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields_ending, ); err.span_label( - fields_span, - format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len(),), - ) - .span_label(qpath.span(), format!("this {}", res.descr())) - .span_label(res_span, format!("{} defined here", res.descr())); + last_subpat_span, + &format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len()), + ); + err.span_label(qpath.span(), ""); + if self.tcx.sess.source_map().is_multiline(def_ident_span.between(field_def_spans[0])) { + err.span_label(def_ident_span, format!("{} defined here", res.descr())); + } + for span in &field_def_spans[..field_def_spans.len() - 1] { + err.span_label(*span, ""); + } + err.span_label( + last_field_def_span, + &format!("{} has {} field{}", res.descr(), fields.len(), fields_ending), + ); // Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`. // More generally, the expected type wants a tuple variant with one field of an diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr index ec7d76f4fe5..9a47ddf0479 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -15,26 +15,22 @@ LL | Enum::SingleVariant(a, .., b, ..) = Enum::SingleVariant(0, 1); | previously used here error[E0023]: this pattern has 3 fields, but the corresponding tuple struct has 2 fields - --> $DIR/tuple_struct_destructure_fail.rs:30:16 + --> $DIR/tuple_struct_destructure_fail.rs:30:17 | LL | struct TupleStruct(S, T); - | ------------------------------- tuple struct defined here + | - - tuple struct has 2 fields ... LL | TupleStruct(a, a, b) = TupleStruct(1, 2); - | -----------^^^^^^^^^ expected 2 fields, found 3 - | | - | this tuple struct + | ----------- ^ ^ ^ expected 2 fields, found 3 error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields - --> $DIR/tuple_struct_destructure_fail.rs:32:16 + --> $DIR/tuple_struct_destructure_fail.rs:32:17 | LL | struct TupleStruct(S, T); - | ------------------------------- tuple struct defined here + | - - tuple struct has 2 fields ... LL | TupleStruct(_) = TupleStruct(1, 2); - | -----------^^^ expected 2 fields, found 1 - | | - | this tuple struct + | ----------- ^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | @@ -46,26 +42,22 @@ LL | TupleStruct(..) = TupleStruct(1, 2); | ~~ error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields - --> $DIR/tuple_struct_destructure_fail.rs:34:24 + --> $DIR/tuple_struct_destructure_fail.rs:34:25 | LL | SingleVariant(S, T) - | ------------------- tuple variant defined here + | - - tuple variant has 2 fields ... LL | Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2); - | -------------------^^^^^^^^^ expected 2 fields, found 3 - | | - | this tuple variant + | ------------------- ^ ^ ^ expected 2 fields, found 3 error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields - --> $DIR/tuple_struct_destructure_fail.rs:36:24 + --> $DIR/tuple_struct_destructure_fail.rs:36:25 | LL | SingleVariant(S, T) - | ------------------- tuple variant defined here + | - - tuple variant has 2 fields ... LL | Enum::SingleVariant(_) = Enum::SingleVariant(1, 2); - | -------------------^^^ expected 2 fields, found 1 - | | - | this tuple variant + | ------------------- ^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | diff --git a/src/test/ui/error-codes/E0023.stderr b/src/test/ui/error-codes/E0023.stderr index ecc075b6fb4..85e1b2cb4ce 100644 --- a/src/test/ui/error-codes/E0023.stderr +++ b/src/test/ui/error-codes/E0023.stderr @@ -1,13 +1,11 @@ error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields - --> $DIR/E0023.rs:11:21 + --> $DIR/E0023.rs:11:22 | LL | Apple(String, String), - | --------------------- tuple variant defined here + | ------ ------ tuple variant has 2 fields ... LL | Fruit::Apple(a) => {}, - | ------------^^^ expected 2 fields, found 1 - | | - | this tuple variant + | ------------ ^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | @@ -15,37 +13,31 @@ LL | Fruit::Apple(a, _) => {}, | +++ error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields - --> $DIR/E0023.rs:12:21 + --> $DIR/E0023.rs:12:22 | LL | Apple(String, String), - | --------------------- tuple variant defined here + | ------ ------ tuple variant has 2 fields ... LL | Fruit::Apple(a, b, c) => {}, - | ------------^^^^^^^^^ expected 2 fields, found 3 - | | - | this tuple variant + | ------------ ^ ^ ^ expected 2 fields, found 3 error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 1 field - --> $DIR/E0023.rs:13:20 + --> $DIR/E0023.rs:13:21 | LL | Pear(u32), - | --------- tuple variant defined here + | --- tuple variant has 1 field ... LL | Fruit::Pear(1, 2) => {}, - | -----------^^^^^^ expected 1 field, found 2 - | | - | this tuple variant + | ----------- ^ ^ expected 1 field, found 2 error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 1 field - --> $DIR/E0023.rs:14:22 + --> $DIR/E0023.rs:14:23 | LL | Orange((String, String)), - | ------------------------ tuple variant defined here + | ---------------- tuple variant has 1 field ... LL | Fruit::Orange(a, b) => {}, - | -------------^^^^^^ expected 1 field, found 2 - | | - | this tuple variant + | ------------- ^ ^ expected 1 field, found 2 | help: missing parentheses | @@ -56,12 +48,10 @@ error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has --> $DIR/E0023.rs:15:22 | LL | Banana(()), - | ---------- tuple variant defined here + | -- tuple variant has 1 field ... LL | Fruit::Banana() => {}, | -------------^^ expected 1 field, found 0 - | | - | this tuple variant | help: missing parentheses | diff --git a/src/test/ui/issues/issue-72574-2.stderr b/src/test/ui/issues/issue-72574-2.stderr index 4c38d2c31ed..3f8ff4f0bac 100644 --- a/src/test/ui/issues/issue-72574-2.stderr +++ b/src/test/ui/issues/issue-72574-2.stderr @@ -19,15 +19,13 @@ LL | Binder(_a, _x @ ..) => {} = note: only allowed in tuple, tuple struct, and slice patterns error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 3 fields - --> $DIR/issue-72574-2.rs:6:15 + --> $DIR/issue-72574-2.rs:6:16 | LL | struct Binder(i32, i32, i32); - | ----------------------------- tuple struct defined here + | --- --- --- tuple struct has 3 fields ... LL | Binder(_a, _x @ ..) => {} - | ------^^^^^^^^^^^^^ expected 3 fields, found 2 - | | - | this tuple struct + | ------ ^^ ^^^^^^^ expected 3 fields, found 2 | help: use `_` to explicitly ignore each field | diff --git a/src/test/ui/match/match-pattern-field-mismatch.stderr b/src/test/ui/match/match-pattern-field-mismatch.stderr index 776cdfc7083..01d7cf0d054 100644 --- a/src/test/ui/match/match-pattern-field-mismatch.stderr +++ b/src/test/ui/match/match-pattern-field-mismatch.stderr @@ -1,13 +1,11 @@ error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 3 fields - --> $DIR/match-pattern-field-mismatch.rs:10:21 + --> $DIR/match-pattern-field-mismatch.rs:10:22 | LL | Rgb(usize, usize, usize), - | ------------------------ tuple variant defined here + | ----- ----- ----- tuple variant has 3 fields ... LL | Color::Rgb(_, _) => { } - | ----------^^^^^^ expected 3 fields, found 2 - | | - | this tuple variant + | ---------- ^ ^ expected 3 fields, found 2 | help: use `_` to explicitly ignore each field | diff --git a/src/test/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr b/src/test/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr index ea5f970d1d6..c87b70625b4 100644 --- a/src/test/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr +++ b/src/test/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr @@ -13,12 +13,10 @@ error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:10 | LL | struct P(T); // 1 type parameter wanted - | --------------- tuple struct defined here + | - tuple struct has 1 field ... LL | let P() = U {}; | -^^ expected 1 field, found 0 - | | - | this tuple struct | help: use `_` to explicitly ignore each field | diff --git a/src/test/ui/pattern/issue-74539.stderr b/src/test/ui/pattern/issue-74539.stderr index 87c3842d28f..d7cbcf2cfa1 100644 --- a/src/test/ui/pattern/issue-74539.stderr +++ b/src/test/ui/pattern/issue-74539.stderr @@ -19,15 +19,13 @@ LL | E::A(x @ ..) => { = note: only allowed in tuple, tuple struct, and slice patterns error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields - --> $DIR/issue-74539.rs:8:13 + --> $DIR/issue-74539.rs:8:14 | LL | A(u8, u8), - | --------- tuple variant defined here + | -- -- tuple variant has 2 fields ... LL | E::A(x @ ..) => { - | ----^^^^^^^^ expected 2 fields, found 1 - | | - | this tuple variant + | ---- ^^^^^^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | diff --git a/src/test/ui/pattern/pat-tuple-overfield.stderr b/src/test/ui/pattern/pat-tuple-overfield.stderr index cc4ca4326ec..1df9c1c11b8 100644 --- a/src/test/ui/pattern/pat-tuple-overfield.stderr +++ b/src/test/ui/pattern/pat-tuple-overfield.stderr @@ -171,87 +171,77 @@ LL | (1, 2, .., 3, 4) => {} found tuple `(_, _, _, _)` error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields - --> $DIR/pat-tuple-overfield.rs:26:10 + --> $DIR/pat-tuple-overfield.rs:26:11 | LL | struct S(u8, u8, u8); - | --------------------- tuple struct defined here + | -- -- -- tuple struct has 3 fields ... LL | S(1, 2, 3, 4) => {} - | -^^^^^^^^^^^^ expected 3 fields, found 4 - | | - | this tuple struct + | - ^ ^ ^ ^ expected 3 fields, found 4 error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields - --> $DIR/pat-tuple-overfield.rs:28:10 + --> $DIR/pat-tuple-overfield.rs:28:11 | LL | struct S(u8, u8, u8); - | --------------------- tuple struct defined here + | -- -- -- tuple struct has 3 fields ... LL | S(1, 2, .., 3, 4) => {} - | -^^^^^^^^^^^^^^^^ expected 3 fields, found 4 - | | - | this tuple struct + | - ^ ^ ^ ^ expected 3 fields, found 4 error[E0023]: this pattern has 6 fields, but the corresponding tuple struct has 5 fields - --> $DIR/pat-tuple-overfield.rs:33:10 + --> $DIR/pat-tuple-overfield.rs:33:11 | -LL | / struct M( -LL | | u8, -LL | | u8, -LL | | u8, -LL | | u8, -LL | | u8, -LL | | ); - | |__- tuple struct defined here +LL | struct M( + | - tuple struct defined here +LL | u8, + | -- +LL | u8, + | -- +LL | u8, + | -- +LL | u8, + | -- +LL | u8, + | -- tuple struct has 5 fields ... -LL | M(1, 2, 3, 4, 5, 6) => {} - | -^^^^^^^^^^^^^^^^^^ expected 5 fields, found 6 - | | - | this tuple struct +LL | M(1, 2, 3, 4, 5, 6) => {} + | - ^ ^ ^ ^ ^ ^ expected 5 fields, found 6 error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 0 fields - --> $DIR/pat-tuple-overfield.rs:45:11 + --> $DIR/pat-tuple-overfield.rs:45:12 | LL | struct Z1(); - | ------------ tuple struct defined here + | --- tuple struct has 0 fields ... LL | Z1(_) => {} - | --^^^ expected 0 fields, found 1 - | | - | this tuple struct + | -- ^ expected 0 fields, found 1 error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 0 fields - --> $DIR/pat-tuple-overfield.rs:46:11 + --> $DIR/pat-tuple-overfield.rs:46:12 | LL | struct Z1(); - | ------------ tuple struct defined here + | --- tuple struct has 0 fields ... LL | Z1(_, _) => {} - | --^^^^^^ expected 0 fields, found 2 - | | - | this tuple struct + | -- ^ ^ expected 0 fields, found 2 error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 0 fields - --> $DIR/pat-tuple-overfield.rs:57:15 + --> $DIR/pat-tuple-overfield.rs:57:16 | LL | Z1(), - | ---- tuple variant defined here + | -- tuple variant has 0 fields ... LL | E1::Z1(_) => {} - | ------^^^ expected 0 fields, found 1 - | | - | this tuple variant + | ------ ^ expected 0 fields, found 1 error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 0 fields - --> $DIR/pat-tuple-overfield.rs:58:15 + --> $DIR/pat-tuple-overfield.rs:58:16 | LL | Z1(), - | ---- tuple variant defined here + | -- tuple variant has 0 fields ... LL | E1::Z1(_, _) => {} - | ------^^^^^^ expected 0 fields, found 2 - | | - | this tuple variant + | ------ ^ ^ expected 0 fields, found 2 error: aborting due to 17 previous errors diff --git a/src/test/ui/pattern/pat-tuple-underfield.stderr b/src/test/ui/pattern/pat-tuple-underfield.stderr index 4cc20a4ac0e..4c21ad0be3e 100644 --- a/src/test/ui/pattern/pat-tuple-underfield.stderr +++ b/src/test/ui/pattern/pat-tuple-underfield.stderr @@ -8,15 +8,13 @@ LL | E::S => {} | ^^^^ help: use the tuple variant pattern syntax instead: `E::S(_, _)` error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields - --> $DIR/pat-tuple-underfield.rs:9:10 + --> $DIR/pat-tuple-underfield.rs:9:11 | LL | struct S(i32, f32); - | ------------------- tuple struct defined here + | --- --- tuple struct has 2 fields ... LL | S(x) => {} - | -^^^ expected 2 fields, found 1 - | | - | this tuple struct + | - ^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | @@ -24,15 +22,13 @@ LL | S(x, _) => {} | +++ error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields - --> $DIR/pat-tuple-underfield.rs:14:10 + --> $DIR/pat-tuple-underfield.rs:14:11 | LL | struct S(i32, f32); - | ------------------- tuple struct defined here + | --- --- tuple struct has 2 fields ... LL | S(_) => {} - | -^^^ expected 2 fields, found 1 - | | - | this tuple struct + | - ^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | @@ -47,12 +43,10 @@ error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has --> $DIR/pat-tuple-underfield.rs:20:10 | LL | struct S(i32, f32); - | ------------------- tuple struct defined here + | --- --- tuple struct has 2 fields ... LL | S() => {} | -^^ expected 2 fields, found 0 - | | - | this tuple struct | help: use `_` to explicitly ignore each field | @@ -64,15 +58,13 @@ LL | S(..) => {} | ++ error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields - --> $DIR/pat-tuple-underfield.rs:27:13 + --> $DIR/pat-tuple-underfield.rs:27:14 | LL | S(i32, f32), - | ----------- tuple variant defined here + | --- --- tuple variant has 2 fields ... LL | E::S(x) => {} - | ----^^^ expected 2 fields, found 1 - | | - | this tuple variant + | ---- ^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | @@ -80,15 +72,13 @@ LL | E::S(x, _) => {} | +++ error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields - --> $DIR/pat-tuple-underfield.rs:32:13 + --> $DIR/pat-tuple-underfield.rs:32:14 | LL | S(i32, f32), - | ----------- tuple variant defined here + | --- --- tuple variant has 2 fields ... LL | E::S(_) => {} - | ----^^^ expected 2 fields, found 1 - | | - | this tuple variant + | ---- ^ expected 2 fields, found 1 | help: use `_` to explicitly ignore each field | @@ -103,12 +93,10 @@ error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has --> $DIR/pat-tuple-underfield.rs:38:13 | LL | S(i32, f32), - | ----------- tuple variant defined here + | --- --- tuple variant has 2 fields ... LL | E::S() => {} | ----^^ expected 2 fields, found 0 - | | - | this tuple variant | help: use `_` to explicitly ignore each field | @@ -120,15 +108,13 @@ LL | E::S(..) => {} | ++ error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 4 fields - --> $DIR/pat-tuple-underfield.rs:50:15 + --> $DIR/pat-tuple-underfield.rs:50:19 | LL | struct Point4(i32, i32, i32, i32); - | ---------------------------------- tuple struct defined here + | --- --- --- --- tuple struct has 4 fields ... LL | Point4( a , _ ) => {} - | ------^^^^^^^^^^^^^^^^^^^^ expected 4 fields, found 2 - | | - | this tuple struct + | ------ ^ ^ expected 4 fields, found 2 | help: use `_` to explicitly ignore each field | diff --git a/src/test/ui/pattern/pattern-error-continue.stderr b/src/test/ui/pattern/pattern-error-continue.stderr index 216c3ca47d3..efc6723e9ef 100644 --- a/src/test/ui/pattern/pattern-error-continue.stderr +++ b/src/test/ui/pattern/pattern-error-continue.stderr @@ -26,15 +26,13 @@ LL | A::B(_) => (), | ~ error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields - --> $DIR/pattern-error-continue.rs:17:13 + --> $DIR/pattern-error-continue.rs:17:14 | LL | B(isize, isize), - | --------------- tuple variant defined here + | ----- ----- tuple variant has 2 fields ... LL | A::B(_, _, _) => (), - | ----^^^^^^^^^ expected 2 fields, found 3 - | | - | this tuple variant + | ---- ^ ^ ^ expected 2 fields, found 3 error[E0308]: mismatched types --> $DIR/pattern-error-continue.rs:22:9