Skip to content

Commit 20a5e9f

Browse files
authored
Rollup merge of #99986 - WaffleLapkin:record_struct_wrap_suggestion, r=compiler-errors
Add wrap suggestions for record variants This PR adds a suggestions to wrap an expression in a record struct/variant when encountering mismatched types, similarly to a suggestion to wrap expression in a tuple struct that was added before. An example: ```rust struct B { f: u8, } enum E { A(u32), B { f: u8 }, } fn main() { let _: B = 1; let _: E = 1; } ``` ```text error[E0308]: mismatched types --> ./t.rs:11:16 | 11 | let _: B = 1; | - ^ expected struct `B`, found integer | | | expected due to this | help: try wrapping the expression in `B` | 11 | let _: B = B { f: 1 }; | ++++++ + error[E0308]: mismatched types --> ./t.rs:12:16 | 12 | let _: E = 1; | - ^ expected enum `E`, found integer | | | expected due to this | help: try wrapping the expression in a variant of `E` | 12 | let _: E = E::A(1); | +++++ + 12 | let _: E = E::B { f: 1 }; | +++++++++ + ``` r? `@compiler-errors`
2 parents e4fcee5 + 1c2ea78 commit 20a5e9f

File tree

5 files changed

+48
-24
lines changed

5 files changed

+48
-24
lines changed

compiler/rustc_typeck/src/check/demand.rs

+33-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::check::FnCtxt;
22
use rustc_infer::infer::InferOk;
3+
use rustc_middle::middle::stability::EvalResult;
34
use rustc_trait_selection::infer::InferCtxtExt as _;
45
use rustc_trait_selection::traits::ObligationCause;
56

@@ -363,18 +364,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
363364
}
364365
}
365366

366-
let compatible_variants: Vec<(String, Option<String>)> = expected_adt
367+
let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
367368
.variants()
368369
.iter()
369370
.filter(|variant| {
370-
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
371+
variant.fields.len() == 1
371372
})
372373
.filter_map(|variant| {
373374
let sole_field = &variant.fields[0];
374375

375376
let field_is_local = sole_field.did.is_local();
376377
let field_is_accessible =
377-
sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx);
378+
sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
379+
// Skip suggestions for unstable public fields (for example `Pin::pointer`)
380+
&& matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
378381

379382
if !field_is_local && !field_is_accessible {
380383
return None;
@@ -391,33 +394,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
391394
if let Some(path) = variant_path.strip_prefix("std::prelude::")
392395
&& let Some((_, path)) = path.split_once("::")
393396
{
394-
return Some((path.to_string(), note_about_variant_field_privacy));
397+
return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy));
395398
}
396-
Some((variant_path, note_about_variant_field_privacy))
399+
Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy))
397400
} else {
398401
None
399402
}
400403
})
401404
.collect();
402405

403-
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
404-
Some(ident) => format!("{ident}: "),
405-
None => String::new(),
406+
let suggestions_for = |variant: &_, ctor, field_name| {
407+
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
408+
Some(ident) => format!("{ident}: "),
409+
None => String::new(),
410+
};
411+
412+
let (open, close) = match ctor {
413+
hir::def::CtorKind::Fn => ("(".to_owned(), ")"),
414+
hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"),
415+
416+
// unit variants don't have fields
417+
hir::def::CtorKind::Const => unreachable!(),
418+
};
419+
420+
vec![
421+
(expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
422+
(expr.span.shrink_to_hi(), close.to_owned()),
423+
]
406424
};
407425

408426
match &compatible_variants[..] {
409427
[] => { /* No variants to format */ }
410-
[(variant, note)] => {
428+
[(variant, ctor_kind, field_name, note)] => {
411429
// Just a single matching variant.
412430
err.multipart_suggestion_verbose(
413431
&format!(
414432
"try wrapping the expression in `{variant}`{note}",
415433
note = note.as_deref().unwrap_or("")
416434
),
417-
vec![
418-
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
419-
(expr.span.shrink_to_hi(), ")".to_string()),
420-
],
435+
suggestions_for(&**variant, *ctor_kind, *field_name),
421436
Applicability::MaybeIncorrect,
422437
);
423438
}
@@ -428,12 +443,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
428443
"try wrapping the expression in a variant of `{}`",
429444
self.tcx.def_path_str(expected_adt.did())
430445
),
431-
compatible_variants.into_iter().map(|(variant, _)| {
432-
vec![
433-
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
434-
(expr.span.shrink_to_hi(), ")".to_string()),
435-
]
436-
}),
446+
compatible_variants.into_iter().map(
447+
|(variant, ctor_kind, field_name, _)| {
448+
suggestions_for(&variant, ctor_kind, field_name)
449+
},
450+
),
437451
Applicability::MaybeIncorrect,
438452
);
439453
}

src/test/ui/did_you_mean/compatible-variants.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fn main() {
6666
}
6767

6868
enum A {
69-
B { b: B},
69+
B { b: B },
7070
}
7171

7272
struct A2(B);
@@ -77,13 +77,12 @@ enum B {
7777
}
7878

7979
fn foo() {
80-
// We don't want to suggest `A::B(B::Fst)` here.
8180
let a: A = B::Fst;
8281
//~^ ERROR mismatched types
82+
//~| HELP try wrapping
8383
}
8484

8585
fn bar() {
86-
// But we _do_ want to suggest `A2(B::Fst)` here!
8786
let a: A2 = B::Fst;
8887
//~^ ERROR mismatched types
8988
//~| HELP try wrapping

src/test/ui/did_you_mean/compatible-variants.stderr

+7-2
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,20 @@ LL | let _ = Foo { bar: Some(bar) };
191191
| ++++++++++ +
192192

193193
error[E0308]: mismatched types
194-
--> $DIR/compatible-variants.rs:81:16
194+
--> $DIR/compatible-variants.rs:80:16
195195
|
196196
LL | let a: A = B::Fst;
197197
| - ^^^^^^ expected enum `A`, found enum `B`
198198
| |
199199
| expected due to this
200+
|
201+
help: try wrapping the expression in `A::B`
202+
|
203+
LL | let a: A = A::B { b: B::Fst };
204+
| +++++++++ +
200205

201206
error[E0308]: mismatched types
202-
--> $DIR/compatible-variants.rs:87:17
207+
--> $DIR/compatible-variants.rs:86:17
203208
|
204209
LL | let a: A2 = B::Fst;
205210
| -- ^^^^^^ expected struct `A2`, found enum `B`

src/test/ui/did_you_mean/issue-42764.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ struct Context { wrapper: Wrapper }
2626
fn overton() {
2727
let _c = Context { wrapper: Payload{} };
2828
//~^ ERROR mismatched types
29+
//~| try wrapping the expression in `Wrapper`
2930
}

src/test/ui/did_you_mean/issue-42764.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ error[E0308]: mismatched types
2525
|
2626
LL | let _c = Context { wrapper: Payload{} };
2727
| ^^^^^^^^^ expected struct `Wrapper`, found struct `Payload`
28+
|
29+
help: try wrapping the expression in `Wrapper`
30+
|
31+
LL | let _c = Context { wrapper: Wrapper { payload: Payload{} } };
32+
| ++++++++++++++++++ +
2833

2934
error: aborting due to 2 previous errors
3035

0 commit comments

Comments
 (0)