Skip to content

Commit f4bf8cd

Browse files
Extend comma suggestion to cases where fields arent missing
1 parent d394408 commit f4bf8cd

File tree

4 files changed

+96
-30
lines changed

4 files changed

+96
-30
lines changed

compiler/rustc_typeck/src/check/expr.rs

+36-19
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15181518
let mut error_happened = false;
15191519

15201520
// Type-check each field.
1521-
for field in ast_fields {
1521+
for (idx, field) in ast_fields.iter().enumerate() {
15221522
let ident = tcx.adjust_ident(field.ident, variant.def_id);
15231523
let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) {
15241524
seen_fields.insert(ident, field.span);
@@ -1556,7 +1556,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15561556

15571557
// Make sure to give a type to the field even if there's
15581558
// an error, so we can continue type-checking.
1559-
self.check_expr_coercable_to_type(&field.expr, field_type, None);
1559+
let ty = self.check_expr_with_hint(&field.expr, field_type);
1560+
let (_, diag) =
1561+
self.demand_coerce_diag(&field.expr, ty, field_type, None, AllowTwoPhase::No);
1562+
1563+
if let Some(mut diag) = diag {
1564+
if idx == ast_fields.len() - 1 && remaining_fields.is_empty() {
1565+
self.suggest_fru_from_range(field, variant, substs, &mut diag);
1566+
}
1567+
diag.emit();
1568+
}
15601569
}
15611570

15621571
// Make sure the programmer specified correct number of fields.
@@ -1784,25 +1793,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17841793
);
17851794
err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}"));
17861795

1787-
// If the last field is a range literal, but it isn't supposed to be, then they probably
1788-
// meant to use functional update syntax.
1789-
//
1796+
if let Some(last) = ast_fields.last() {
1797+
self.suggest_fru_from_range(last, variant, substs, &mut err);
1798+
}
1799+
1800+
err.emit();
1801+
}
1802+
1803+
/// If the last field is a range literal, but it isn't supposed to be, then they probably
1804+
/// meant to use functional update syntax.
1805+
fn suggest_fru_from_range(
1806+
&self,
1807+
last_expr_field: &hir::ExprField<'tcx>,
1808+
variant: &ty::VariantDef,
1809+
substs: SubstsRef<'tcx>,
1810+
err: &mut Diagnostic,
1811+
) {
17901812
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
1791-
if let Some((
1792-
last,
1793-
ExprKind::Struct(
1813+
if let ExprKind::Struct(
17941814
QPath::LangItem(LangItem::Range, ..),
17951815
&[ref range_start, ref range_end],
17961816
_,
1797-
),
1798-
)) = ast_fields.last().map(|last| (last, &last.expr.kind)) &&
1799-
let variant_field =
1800-
variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) &&
1801-
let range_def_id = self.tcx.lang_items().range_struct() &&
1802-
variant_field
1803-
.and_then(|field| field.ty(self.tcx, substs).ty_adt_def())
1804-
.map(|adt| adt.did())
1805-
!= range_def_id
1817+
) = last_expr_field.expr.kind
1818+
&& let variant_field =
1819+
variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident)
1820+
&& let range_def_id = self.tcx.lang_items().range_struct()
1821+
&& variant_field
1822+
.and_then(|field| field.ty(self.tcx, substs).ty_adt_def())
1823+
.map(|adt| adt.did())
1824+
!= range_def_id
18061825
{
18071826
let instead = self
18081827
.tcx
@@ -1818,8 +1837,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18181837
Applicability::MaybeIncorrect,
18191838
);
18201839
}
1821-
1822-
err.emit();
18231840
}
18241841

18251842
/// Report an error for a struct field expression when there are invisible fields.

src/test/ui/structs/struct-record-suggestion.fixed

+21-3
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,29 @@ struct A {
66
d: usize,
77
}
88

9-
fn main() {
10-
let q = A { c: 5, .. Default::default() };
9+
fn a() {
10+
let q = A { c: 5,..Default::default() };
1111
//~^ ERROR mismatched types
1212
//~| ERROR missing fields
1313
//~| HELP separate the last named field with a comma
14-
let r = A { c: 5, .. Default::default() };
14+
let r = A { c: 5, ..Default::default() };
1515
assert_eq!(q, r);
1616
}
17+
18+
#[derive(Debug, Default, Eq, PartialEq)]
19+
struct B {
20+
b: u32,
21+
}
22+
23+
fn b() {
24+
let q = B { b: 1,..Default::default() };
25+
//~^ ERROR mismatched types
26+
//~| HELP separate the last named field with a comma
27+
let r = B { b: 1 };
28+
assert_eq!(q, r);
29+
}
30+
31+
fn main() {
32+
a();
33+
b();
34+
}

src/test/ui/structs/struct-record-suggestion.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,29 @@ struct A {
66
d: usize,
77
}
88

9-
fn main() {
10-
let q = A { c: 5 .. Default::default() };
9+
fn a() {
10+
let q = A { c: 5..Default::default() };
1111
//~^ ERROR mismatched types
1212
//~| ERROR missing fields
1313
//~| HELP separate the last named field with a comma
14-
let r = A { c: 5, .. Default::default() };
14+
let r = A { c: 5, ..Default::default() };
1515
assert_eq!(q, r);
1616
}
17+
18+
#[derive(Debug, Default, Eq, PartialEq)]
19+
struct B {
20+
b: u32,
21+
}
22+
23+
fn b() {
24+
let q = B { b: 1..Default::default() };
25+
//~^ ERROR mismatched types
26+
//~| HELP separate the last named field with a comma
27+
let r = B { b: 1 };
28+
assert_eq!(q, r);
29+
}
30+
31+
fn main() {
32+
a();
33+
b();
34+
}
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
error[E0308]: mismatched types
22
--> $DIR/struct-record-suggestion.rs:10:20
33
|
4-
LL | let q = A { c: 5 .. Default::default() };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found struct `std::ops::Range`
4+
LL | let q = A { c: 5..Default::default() };
5+
| ^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found struct `std::ops::Range`
66
|
77
= note: expected type `u64`
88
found struct `std::ops::Range<{integer}>`
99

1010
error[E0063]: missing fields `b` and `d` in initializer of `A`
1111
--> $DIR/struct-record-suggestion.rs:10:13
1212
|
13-
LL | let q = A { c: 5 .. Default::default() };
13+
LL | let q = A { c: 5..Default::default() };
1414
| ^ missing `b` and `d`
1515
|
1616
help: to set the remaining fields from `Default::default()`, separate the last named field with a comma
1717
|
18-
LL | let q = A { c: 5, .. Default::default() };
18+
LL | let q = A { c: 5,..Default::default() };
1919
| +
2020

21-
error: aborting due to 2 previous errors
21+
error[E0308]: mismatched types
22+
--> $DIR/struct-record-suggestion.rs:24:20
23+
|
24+
LL | let q = B { b: 1..Default::default() };
25+
| ^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found struct `std::ops::Range`
26+
|
27+
= note: expected type `u32`
28+
found struct `std::ops::Range<{integer}>`
29+
help: to set the remaining fields from `Default::default()`, separate the last named field with a comma
30+
|
31+
LL | let q = B { b: 1,..Default::default() };
32+
| +
33+
34+
error: aborting due to 3 previous errors
2235

2336
Some errors have detailed explanations: E0063, E0308.
2437
For more information about an error, try `rustc --explain E0063`.

0 commit comments

Comments
 (0)