Skip to content

Add wrap suggestions for record variants #99986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 33 additions & 19 deletions compiler/rustc_typeck/src/check/demand.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::check::FnCtxt;
use rustc_infer::infer::InferOk;
use rustc_middle::middle::stability::EvalResult;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::ObligationCause;

Expand Down Expand Up @@ -363,18 +364,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

let compatible_variants: Vec<(String, Option<String>)> = expected_adt
let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
.variants()
.iter()
.filter(|variant| {
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
variant.fields.len() == 1
})
.filter_map(|variant| {
let sole_field = &variant.fields[0];

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

if !field_is_local && !field_is_accessible {
return None;
Expand All @@ -391,33 +394,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(path) = variant_path.strip_prefix("std::prelude::")
&& let Some((_, path)) = path.split_once("::")
{
return Some((path.to_string(), note_about_variant_field_privacy));
return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy));
}
Some((variant_path, note_about_variant_field_privacy))
Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy))
} else {
None
}
})
.collect();

let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
Some(ident) => format!("{ident}: "),
None => String::new(),
let suggestions_for = |variant: &_, ctor, field_name| {
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
Some(ident) => format!("{ident}: "),
None => String::new(),
};

let (open, close) = match ctor {
hir::def::CtorKind::Fn => ("(".to_owned(), ")"),
hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"),

// unit variants don't have fields
hir::def::CtorKind::Const => unreachable!(),
};

vec![
(expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
(expr.span.shrink_to_hi(), close.to_owned()),
]
};

match &compatible_variants[..] {
[] => { /* No variants to format */ }
[(variant, note)] => {
[(variant, ctor_kind, field_name, note)] => {
// Just a single matching variant.
err.multipart_suggestion_verbose(
&format!(
"try wrapping the expression in `{variant}`{note}",
note = note.as_deref().unwrap_or("")
),
vec![
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
(expr.span.shrink_to_hi(), ")".to_string()),
],
suggestions_for(&**variant, *ctor_kind, *field_name),
Applicability::MaybeIncorrect,
);
}
Expand All @@ -428,12 +443,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"try wrapping the expression in a variant of `{}`",
self.tcx.def_path_str(expected_adt.did())
),
compatible_variants.into_iter().map(|(variant, _)| {
vec![
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
(expr.span.shrink_to_hi(), ")".to_string()),
]
}),
compatible_variants.into_iter().map(
|(variant, ctor_kind, field_name, _)| {
suggestions_for(&variant, ctor_kind, field_name)
},
),
Applicability::MaybeIncorrect,
);
}
Expand Down
5 changes: 2 additions & 3 deletions src/test/ui/did_you_mean/compatible-variants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn main() {
}

enum A {
B { b: B},
B { b: B },
}

struct A2(B);
Expand All @@ -77,13 +77,12 @@ enum B {
}

fn foo() {
// We don't want to suggest `A::B(B::Fst)` here.
let a: A = B::Fst;
//~^ ERROR mismatched types
//~| HELP try wrapping
}

fn bar() {
// But we _do_ want to suggest `A2(B::Fst)` here!
let a: A2 = B::Fst;
//~^ ERROR mismatched types
//~| HELP try wrapping
Expand Down
9 changes: 7 additions & 2 deletions src/test/ui/did_you_mean/compatible-variants.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,20 @@ LL | let _ = Foo { bar: Some(bar) };
| ++++++++++ +

error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:81:16
--> $DIR/compatible-variants.rs:80:16
|
LL | let a: A = B::Fst;
| - ^^^^^^ expected enum `A`, found enum `B`
| |
| expected due to this
|
help: try wrapping the expression in `A::B`
|
LL | let a: A = A::B { b: B::Fst };
| +++++++++ +

error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:87:17
--> $DIR/compatible-variants.rs:86:17
|
LL | let a: A2 = B::Fst;
| -- ^^^^^^ expected struct `A2`, found enum `B`
Expand Down
1 change: 1 addition & 0 deletions src/test/ui/did_you_mean/issue-42764.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ struct Context { wrapper: Wrapper }
fn overton() {
let _c = Context { wrapper: Payload{} };
//~^ ERROR mismatched types
//~| try wrapping the expression in `Wrapper`
}
5 changes: 5 additions & 0 deletions src/test/ui/did_you_mean/issue-42764.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ error[E0308]: mismatched types
|
LL | let _c = Context { wrapper: Payload{} };
| ^^^^^^^^^ expected struct `Wrapper`, found struct `Payload`
|
help: try wrapping the expression in `Wrapper`
|
LL | let _c = Context { wrapper: Wrapper { payload: Payload{} } };
| ++++++++++++++++++ +

error: aborting due to 2 previous errors

Expand Down