Skip to content

Commit 8733550

Browse files
authored
Rollup merge of #100309 - compiler-errors:issue-100300, r=sanxiyn
Extend comma suggestion to cases where fields arent missing Fixes #100300
2 parents 4d1c273 + f4bf8cd commit 8733550

File tree

4 files changed

+96
-30
lines changed

4 files changed

+96
-30
lines changed

compiler/rustc_typeck/src/check/expr.rs

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15561556
let mut error_happened = false;
15571557

15581558
// Type-check each field.
1559-
for field in ast_fields {
1559+
for (idx, field) in ast_fields.iter().enumerate() {
15601560
let ident = tcx.adjust_ident(field.ident, variant.def_id);
15611561
let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) {
15621562
seen_fields.insert(ident, field.span);
@@ -1594,7 +1594,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15941594

15951595
// Make sure to give a type to the field even if there's
15961596
// an error, so we can continue type-checking.
1597-
self.check_expr_coercable_to_type(&field.expr, field_type, None);
1597+
let ty = self.check_expr_with_hint(&field.expr, field_type);
1598+
let (_, diag) =
1599+
self.demand_coerce_diag(&field.expr, ty, field_type, None, AllowTwoPhase::No);
1600+
1601+
if let Some(mut diag) = diag {
1602+
if idx == ast_fields.len() - 1 && remaining_fields.is_empty() {
1603+
self.suggest_fru_from_range(field, variant, substs, &mut diag);
1604+
}
1605+
diag.emit();
1606+
}
15981607
}
15991608

16001609
// Make sure the programmer specified correct number of fields.
@@ -1822,25 +1831,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18221831
);
18231832
err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}"));
18241833

1825-
// If the last field is a range literal, but it isn't supposed to be, then they probably
1826-
// meant to use functional update syntax.
1827-
//
1834+
if let Some(last) = ast_fields.last() {
1835+
self.suggest_fru_from_range(last, variant, substs, &mut err);
1836+
}
1837+
1838+
err.emit();
1839+
}
1840+
1841+
/// If the last field is a range literal, but it isn't supposed to be, then they probably
1842+
/// meant to use functional update syntax.
1843+
fn suggest_fru_from_range(
1844+
&self,
1845+
last_expr_field: &hir::ExprField<'tcx>,
1846+
variant: &ty::VariantDef,
1847+
substs: SubstsRef<'tcx>,
1848+
err: &mut Diagnostic,
1849+
) {
18281850
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
1829-
if let Some((
1830-
last,
1831-
ExprKind::Struct(
1851+
if let ExprKind::Struct(
18321852
QPath::LangItem(LangItem::Range, ..),
18331853
&[ref range_start, ref range_end],
18341854
_,
1835-
),
1836-
)) = ast_fields.last().map(|last| (last, &last.expr.kind)) &&
1837-
let variant_field =
1838-
variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) &&
1839-
let range_def_id = self.tcx.lang_items().range_struct() &&
1840-
variant_field
1841-
.and_then(|field| field.ty(self.tcx, substs).ty_adt_def())
1842-
.map(|adt| adt.did())
1843-
!= range_def_id
1855+
) = last_expr_field.expr.kind
1856+
&& let variant_field =
1857+
variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident)
1858+
&& let range_def_id = self.tcx.lang_items().range_struct()
1859+
&& variant_field
1860+
.and_then(|field| field.ty(self.tcx, substs).ty_adt_def())
1861+
.map(|adt| adt.did())
1862+
!= range_def_id
18441863
{
18451864
let instead = self
18461865
.tcx
@@ -1856,8 +1875,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18561875
Applicability::MaybeIncorrect,
18571876
);
18581877
}
1859-
1860-
err.emit();
18611878
}
18621879

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

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

Lines changed: 21 additions & 3 deletions
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

Lines changed: 21 additions & 3 deletions
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+
}
Lines changed: 18 additions & 5 deletions
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)