From f4bf8cd100deb76bd424897c9796f0886468c77a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 9 Aug 2022 07:04:37 +0000 Subject: [PATCH] Extend comma suggestion to cases where fields arent missing --- compiler/rustc_typeck/src/check/expr.rs | 55 ++++++++++++------- .../ui/structs/struct-record-suggestion.fixed | 24 +++++++- .../ui/structs/struct-record-suggestion.rs | 24 +++++++- .../structs/struct-record-suggestion.stderr | 23 ++++++-- 4 files changed, 96 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 6e97b0bf2ab7d..a0fa27a1018ec 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1518,7 +1518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut error_happened = false; // Type-check each field. - for field in ast_fields { + for (idx, field) in ast_fields.iter().enumerate() { let ident = tcx.adjust_ident(field.ident, variant.def_id); let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { seen_fields.insert(ident, field.span); @@ -1556,7 +1556,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. - self.check_expr_coercable_to_type(&field.expr, field_type, None); + let ty = self.check_expr_with_hint(&field.expr, field_type); + let (_, diag) = + self.demand_coerce_diag(&field.expr, ty, field_type, None, AllowTwoPhase::No); + + if let Some(mut diag) = diag { + if idx == ast_fields.len() - 1 && remaining_fields.is_empty() { + self.suggest_fru_from_range(field, variant, substs, &mut diag); + } + diag.emit(); + } } // Make sure the programmer specified correct number of fields. @@ -1784,25 +1793,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); - // If the last field is a range literal, but it isn't supposed to be, then they probably - // meant to use functional update syntax. - // + if let Some(last) = ast_fields.last() { + self.suggest_fru_from_range(last, variant, substs, &mut err); + } + + err.emit(); + } + + /// If the last field is a range literal, but it isn't supposed to be, then they probably + /// meant to use functional update syntax. + fn suggest_fru_from_range( + &self, + last_expr_field: &hir::ExprField<'tcx>, + variant: &ty::VariantDef, + substs: SubstsRef<'tcx>, + err: &mut Diagnostic, + ) { // I don't use 'is_range_literal' because only double-sided, half-open ranges count. - if let Some(( - last, - ExprKind::Struct( + if let ExprKind::Struct( QPath::LangItem(LangItem::Range, ..), &[ref range_start, ref range_end], _, - ), - )) = ast_fields.last().map(|last| (last, &last.expr.kind)) && - let variant_field = - variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) && - let range_def_id = self.tcx.lang_items().range_struct() && - variant_field - .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) - .map(|adt| adt.did()) - != range_def_id + ) = last_expr_field.expr.kind + && let variant_field = + variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident) + && let range_def_id = self.tcx.lang_items().range_struct() + && variant_field + .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) + .map(|adt| adt.did()) + != range_def_id { let instead = self .tcx @@ -1818,8 +1837,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - - err.emit(); } /// Report an error for a struct field expression when there are invisible fields. diff --git a/src/test/ui/structs/struct-record-suggestion.fixed b/src/test/ui/structs/struct-record-suggestion.fixed index 48144cd1ce2d1..49e38b196deb2 100644 --- a/src/test/ui/structs/struct-record-suggestion.fixed +++ b/src/test/ui/structs/struct-record-suggestion.fixed @@ -6,11 +6,29 @@ struct A { d: usize, } -fn main() { - let q = A { c: 5, .. Default::default() }; +fn a() { + let q = A { c: 5,..Default::default() }; //~^ ERROR mismatched types //~| ERROR missing fields //~| HELP separate the last named field with a comma - let r = A { c: 5, .. Default::default() }; + let r = A { c: 5, ..Default::default() }; assert_eq!(q, r); } + +#[derive(Debug, Default, Eq, PartialEq)] +struct B { + b: u32, +} + +fn b() { + let q = B { b: 1,..Default::default() }; + //~^ ERROR mismatched types + //~| HELP separate the last named field with a comma + let r = B { b: 1 }; + assert_eq!(q, r); +} + +fn main() { + a(); + b(); +} diff --git a/src/test/ui/structs/struct-record-suggestion.rs b/src/test/ui/structs/struct-record-suggestion.rs index 6d169d5c6dbfd..901f310c8bdb2 100644 --- a/src/test/ui/structs/struct-record-suggestion.rs +++ b/src/test/ui/structs/struct-record-suggestion.rs @@ -6,11 +6,29 @@ struct A { d: usize, } -fn main() { - let q = A { c: 5 .. Default::default() }; +fn a() { + let q = A { c: 5..Default::default() }; //~^ ERROR mismatched types //~| ERROR missing fields //~| HELP separate the last named field with a comma - let r = A { c: 5, .. Default::default() }; + let r = A { c: 5, ..Default::default() }; assert_eq!(q, r); } + +#[derive(Debug, Default, Eq, PartialEq)] +struct B { + b: u32, +} + +fn b() { + let q = B { b: 1..Default::default() }; + //~^ ERROR mismatched types + //~| HELP separate the last named field with a comma + let r = B { b: 1 }; + assert_eq!(q, r); +} + +fn main() { + a(); + b(); +} diff --git a/src/test/ui/structs/struct-record-suggestion.stderr b/src/test/ui/structs/struct-record-suggestion.stderr index e5bd03117b9fe..66e9f021ed68f 100644 --- a/src/test/ui/structs/struct-record-suggestion.stderr +++ b/src/test/ui/structs/struct-record-suggestion.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types --> $DIR/struct-record-suggestion.rs:10:20 | -LL | let q = A { c: 5 .. Default::default() }; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found struct `std::ops::Range` +LL | let q = A { c: 5..Default::default() }; + | ^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found struct `std::ops::Range` | = note: expected type `u64` found struct `std::ops::Range<{integer}>` @@ -10,15 +10,28 @@ LL | let q = A { c: 5 .. Default::default() }; error[E0063]: missing fields `b` and `d` in initializer of `A` --> $DIR/struct-record-suggestion.rs:10:13 | -LL | let q = A { c: 5 .. Default::default() }; +LL | let q = A { c: 5..Default::default() }; | ^ missing `b` and `d` | help: to set the remaining fields from `Default::default()`, separate the last named field with a comma | -LL | let q = A { c: 5, .. Default::default() }; +LL | let q = A { c: 5,..Default::default() }; | + -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/struct-record-suggestion.rs:24:20 + | +LL | let q = B { b: 1..Default::default() }; + | ^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found struct `std::ops::Range` + | + = note: expected type `u32` + found struct `std::ops::Range<{integer}>` +help: to set the remaining fields from `Default::default()`, separate the last named field with a comma + | +LL | let q = B { b: 1,..Default::default() }; + | + + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0063, E0308. For more information about an error, try `rustc --explain E0063`.