From d2b13ba46694b73cf2cd64b3a18c3abd55983f2b Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Wed, 1 Sep 2021 20:11:05 -0700 Subject: [PATCH 1/2] Fix span used for structured tuple struct suggestion (And same for tuple variants.) Previously, the span was just for the constructor name, which meant it would result in syntactically-invalid code when applied. Now, the span is for the entire expression. --- compiler/rustc_typeck/src/check/expr.rs | 12 ++- src/test/ui/issues/issue-4736.stderr | 5 +- src/test/ui/issues/issue-80607.stderr | 5 +- src/test/ui/numeric/numeric-fields.stderr | 5 +- .../nested-non-tuple-tuple-struct.rs | 18 ++++ .../nested-non-tuple-tuple-struct.stderr | 100 ++++++++++++++++++ 6 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/suggestions/nested-non-tuple-tuple-struct.rs create mode 100644 src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 10f5b000aca..1a4dfdcfcbf 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1236,6 +1236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant, fields, base_expr.is_none(), + expr.span, ); if let Some(base_expr) = base_expr { // If check_expr_struct_fields hit an error, do not attempt to populate @@ -1283,6 +1284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant: &'tcx ty::VariantDef, ast_fields: &'tcx [hir::ExprField<'tcx>], check_completeness: bool, + expr_span: Span, ) -> bool { let tcx = self.tcx; @@ -1334,7 +1336,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ident, }); } else { - self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); + self.report_unknown_field( + adt_ty, variant, field, ast_fields, kind_name, expr_span, + ); } tcx.ty_error() @@ -1467,7 +1471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: &hir::ExprField<'_>, skip_fields: &[hir::ExprField<'_>], kind_name: &str, - ty_span: Span, + expr_span: Span, ) { if variant.is_recovered() { self.set_tainted_by_errors(); @@ -1511,7 +1515,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(field.ident.span, "field does not exist"); err.span_suggestion( - ty_span, + expr_span, &format!( "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, @@ -1529,7 +1533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); err.span_label(field.ident.span, "field does not exist"); err.span_suggestion( - ty_span, + expr_span, &format!( "`{adt}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, diff --git a/src/test/ui/issues/issue-4736.stderr b/src/test/ui/issues/issue-4736.stderr index 9be4eb1c7fd..7df506b1981 100644 --- a/src/test/ui/issues/issue-4736.stderr +++ b/src/test/ui/issues/issue-4736.stderr @@ -5,8 +5,9 @@ LL | struct NonCopyable(()); | ----------- `NonCopyable` defined here ... LL | let z = NonCopyable{ p: () }; - | ----------- ^ field does not exist - | | + | -------------^------ + | | | + | | field does not exist | help: `NonCopyable` is a tuple struct, use the appropriate syntax: `NonCopyable(/* fields */)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-80607.stderr b/src/test/ui/issues/issue-80607.stderr index 22a660b4167..6172a7cc8aa 100644 --- a/src/test/ui/issues/issue-80607.stderr +++ b/src/test/ui/issues/issue-80607.stderr @@ -5,8 +5,9 @@ LL | V1(i32), | -- `Enum::V1` defined here ... LL | Enum::V1 { x } - | -------- ^ field does not exist - | | + | -----------^-- + | | | + | | field does not exist | help: `Enum::V1` is a tuple variant, use the appropriate syntax: `Enum::V1(/* fields */)` error: aborting due to previous error diff --git a/src/test/ui/numeric/numeric-fields.stderr b/src/test/ui/numeric/numeric-fields.stderr index 13b6cfae4ec..daf1cbb65bf 100644 --- a/src/test/ui/numeric/numeric-fields.stderr +++ b/src/test/ui/numeric/numeric-fields.stderr @@ -5,8 +5,9 @@ LL | struct S(u8, u16); | - `S` defined here ... LL | let s = S{0b1: 10, 0: 11}; - | - ^^^ field does not exist - | | + | --^^^------------ + | | | + | | field does not exist | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` error[E0026]: struct `S` does not have a field named `0x1` diff --git a/src/test/ui/suggestions/nested-non-tuple-tuple-struct.rs b/src/test/ui/suggestions/nested-non-tuple-tuple-struct.rs new file mode 100644 index 00000000000..308adcf01fe --- /dev/null +++ b/src/test/ui/suggestions/nested-non-tuple-tuple-struct.rs @@ -0,0 +1,18 @@ +pub struct S(f32, f32); + +pub enum E { + V(f32, f32), +} + +fn main() { + let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); + //~^ ERROR struct `S` has no field named `x` + //~| ERROR struct `S` has no field named `y` + //~| ERROR struct `S` has no field named `x` + //~| ERROR struct `S` has no field named `y` + let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); + //~^ ERROR variant `E::V` has no field named `x` + //~| ERROR variant `E::V` has no field named `y` + //~| ERROR variant `E::V` has no field named `x` + //~| ERROR variant `E::V` has no field named `y` +} diff --git a/src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr b/src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr new file mode 100644 index 00000000000..5a234e2a75f --- /dev/null +++ b/src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr @@ -0,0 +1,100 @@ +error[E0560]: struct `S` has no field named `x` + --> $DIR/nested-non-tuple-tuple-struct.rs:8:19 + | +LL | pub struct S(f32, f32); + | - `S` defined here +... +LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); + | ----^--------------- + | | | + | | field does not exist + | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + +error[E0560]: struct `S` has no field named `y` + --> $DIR/nested-non-tuple-tuple-struct.rs:8:27 + | +LL | pub struct S(f32, f32); + | - `S` defined here +... +LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); + | ------------^------- + | | | + | | field does not exist + | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + +error[E0560]: struct `S` has no field named `x` + --> $DIR/nested-non-tuple-tuple-struct.rs:8:41 + | +LL | pub struct S(f32, f32); + | - `S` defined here +... +LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); + | ----^--------------- + | | | + | | field does not exist + | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + +error[E0560]: struct `S` has no field named `y` + --> $DIR/nested-non-tuple-tuple-struct.rs:8:49 + | +LL | pub struct S(f32, f32); + | - `S` defined here +... +LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); + | ------------^------- + | | | + | | field does not exist + | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + +error[E0559]: variant `E::V` has no field named `x` + --> $DIR/nested-non-tuple-tuple-struct.rs:13:22 + | +LL | V(f32, f32), + | - `E::V` defined here +... +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); + | -------^--------------- + | | | + | | field does not exist + | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + +error[E0559]: variant `E::V` has no field named `y` + --> $DIR/nested-non-tuple-tuple-struct.rs:13:30 + | +LL | V(f32, f32), + | - `E::V` defined here +... +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); + | ---------------^------- + | | | + | | field does not exist + | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + +error[E0559]: variant `E::V` has no field named `x` + --> $DIR/nested-non-tuple-tuple-struct.rs:13:47 + | +LL | V(f32, f32), + | - `E::V` defined here +... +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); + | -------^--------------- + | | | + | | field does not exist + | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + +error[E0559]: variant `E::V` has no field named `y` + --> $DIR/nested-non-tuple-tuple-struct.rs:13:55 + | +LL | V(f32, f32), + | - `E::V` defined here +... +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); + | ---------------^------- + | | | + | | field does not exist + | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0559, E0560. +For more information about an error, try `rustc --explain E0559`. From 2226977a875c149effeb7d6b85bce5a5ba0c440c Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Wed, 1 Sep 2021 20:20:46 -0700 Subject: [PATCH 2/2] Ensure suggestion is in its own diagnostic window For two reasons: 1. Now that the suggestion span has been corrected, the output is a bit cluttered and hard to read. Putting the suggestion its own window creates more space. 2. It's easier to see what's being suggested, since now the version after the suggestion is applied is shown. --- compiler/rustc_typeck/src/check/expr.rs | 4 +- src/test/ui/issues/issue-4736.stderr | 10 ++- src/test/ui/issues/issue-80607.stderr | 10 ++- src/test/ui/numeric/numeric-fields.stderr | 10 ++- .../nested-non-tuple-tuple-struct.stderr | 80 +++++++++++-------- 5 files changed, 68 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 1a4dfdcfcbf..0a08988069c 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1514,7 +1514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); err.span_label(field.ident.span, "field does not exist"); - err.span_suggestion( + err.span_suggestion_verbose( expr_span, &format!( "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", @@ -1532,7 +1532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); err.span_label(field.ident.span, "field does not exist"); - err.span_suggestion( + err.span_suggestion_verbose( expr_span, &format!( "`{adt}` is a tuple {kind_name}, use the appropriate syntax", diff --git a/src/test/ui/issues/issue-4736.stderr b/src/test/ui/issues/issue-4736.stderr index 7df506b1981..2a1f1819c33 100644 --- a/src/test/ui/issues/issue-4736.stderr +++ b/src/test/ui/issues/issue-4736.stderr @@ -5,10 +5,12 @@ LL | struct NonCopyable(()); | ----------- `NonCopyable` defined here ... LL | let z = NonCopyable{ p: () }; - | -------------^------ - | | | - | | field does not exist - | help: `NonCopyable` is a tuple struct, use the appropriate syntax: `NonCopyable(/* fields */)` + | ^ field does not exist + | +help: `NonCopyable` is a tuple struct, use the appropriate syntax + | +LL | let z = NonCopyable(/* fields */); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-80607.stderr b/src/test/ui/issues/issue-80607.stderr index 6172a7cc8aa..38e46683b08 100644 --- a/src/test/ui/issues/issue-80607.stderr +++ b/src/test/ui/issues/issue-80607.stderr @@ -5,10 +5,12 @@ LL | V1(i32), | -- `Enum::V1` defined here ... LL | Enum::V1 { x } - | -----------^-- - | | | - | | field does not exist - | help: `Enum::V1` is a tuple variant, use the appropriate syntax: `Enum::V1(/* fields */)` + | ^ field does not exist + | +help: `Enum::V1` is a tuple variant, use the appropriate syntax + | +LL | Enum::V1(/* fields */) + | ~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to previous error diff --git a/src/test/ui/numeric/numeric-fields.stderr b/src/test/ui/numeric/numeric-fields.stderr index daf1cbb65bf..b328fbe2cfb 100644 --- a/src/test/ui/numeric/numeric-fields.stderr +++ b/src/test/ui/numeric/numeric-fields.stderr @@ -5,10 +5,12 @@ LL | struct S(u8, u16); | - `S` defined here ... LL | let s = S{0b1: 10, 0: 11}; - | --^^^------------ - | | | - | | field does not exist - | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + | ^^^ field does not exist + | +help: `S` is a tuple struct, use the appropriate syntax + | +LL | let s = S(/* fields */); + | ~~~~~~~~~~~~~~~ error[E0026]: struct `S` does not have a field named `0x1` --> $DIR/numeric-fields.rs:7:17 diff --git a/src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr b/src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr index 5a234e2a75f..948f09fc3fa 100644 --- a/src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr +++ b/src/test/ui/suggestions/nested-non-tuple-tuple-struct.stderr @@ -5,10 +5,12 @@ LL | pub struct S(f32, f32); | - `S` defined here ... LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); - | ----^--------------- - | | | - | | field does not exist - | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + | ^ field does not exist + | +help: `S` is a tuple struct, use the appropriate syntax + | +LL | let _x = (S(/* fields */), S { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~ error[E0560]: struct `S` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:8:27 @@ -17,10 +19,12 @@ LL | pub struct S(f32, f32); | - `S` defined here ... LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); - | ------------^------- - | | | - | | field does not exist - | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + | ^ field does not exist + | +help: `S` is a tuple struct, use the appropriate syntax + | +LL | let _x = (S(/* fields */), S { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~ error[E0560]: struct `S` has no field named `x` --> $DIR/nested-non-tuple-tuple-struct.rs:8:41 @@ -29,10 +33,12 @@ LL | pub struct S(f32, f32); | - `S` defined here ... LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); - | ----^--------------- - | | | - | | field does not exist - | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + | ^ field does not exist + | +help: `S` is a tuple struct, use the appropriate syntax + | +LL | let _x = (S { x: 1.0, y: 2.0 }, S(/* fields */)); + | ~~~~~~~~~~~~~~~ error[E0560]: struct `S` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:8:49 @@ -41,10 +47,12 @@ LL | pub struct S(f32, f32); | - `S` defined here ... LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); - | ------------^------- - | | | - | | field does not exist - | help: `S` is a tuple struct, use the appropriate syntax: `S(/* fields */)` + | ^ field does not exist + | +help: `S` is a tuple struct, use the appropriate syntax + | +LL | let _x = (S { x: 1.0, y: 2.0 }, S(/* fields */)); + | ~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `x` --> $DIR/nested-non-tuple-tuple-struct.rs:13:22 @@ -53,10 +61,12 @@ LL | V(f32, f32), | - `E::V` defined here ... LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); - | -------^--------------- - | | | - | | field does not exist - | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + | ^ field does not exist + | +help: `E::V` is a tuple variant, use the appropriate syntax + | +LL | let _y = (E::V(/* fields */), E::V { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:13:30 @@ -65,10 +75,12 @@ LL | V(f32, f32), | - `E::V` defined here ... LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); - | ---------------^------- - | | | - | | field does not exist - | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + | ^ field does not exist + | +help: `E::V` is a tuple variant, use the appropriate syntax + | +LL | let _y = (E::V(/* fields */), E::V { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `x` --> $DIR/nested-non-tuple-tuple-struct.rs:13:47 @@ -77,10 +89,12 @@ LL | V(f32, f32), | - `E::V` defined here ... LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); - | -------^--------------- - | | | - | | field does not exist - | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + | ^ field does not exist + | +help: `E::V` is a tuple variant, use the appropriate syntax + | +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V(/* fields */)); + | ~~~~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:13:55 @@ -89,10 +103,12 @@ LL | V(f32, f32), | - `E::V` defined here ... LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); - | ---------------^------- - | | | - | | field does not exist - | help: `E::V` is a tuple variant, use the appropriate syntax: `E::V(/* fields */)` + | ^ field does not exist + | +help: `E::V` is a tuple variant, use the appropriate syntax + | +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V(/* fields */)); + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 8 previous errors