Skip to content

Commit ff1b653

Browse files
authored
Rollup merge of #92808 - compiler-errors:wrap-struct-shorthand-field-in-variant, r=davidtwco
Fix `try wrapping expression in variant` suggestion with struct field shorthand Fixes a broken suggestion: [playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=83fe2dbfe1485f8cfca1aef2a6582e77) before: ``` error[E0308]: mismatched types --> src/main.rs:7:19 | 7 | let x = Foo { bar }; | ^^^ expected enum `Option`, found integer | = note: expected enum `Option<i32>` found type `{integer}` help: try wrapping the expression in `Some` | 7 | let x = Foo { Some(bar) }; | +++++ + ``` after: ``` error[E0308]: mismatched types --> src/main.rs:7:19 | 7 | let x = Foo { bar }; | ^^^ expected enum `Option`, found integer | = note: expected enum `Option<i32>` found type `{integer}` help: try wrapping the expression in `Some` | 7 | let x = Foo { bar: Some(bar) }; | ~~~~~~~~~~~~~~ ``` r? ``@m-ou-se`` since you touched the code last in #91080
2 parents 3de7276 + 272fb23 commit ff1b653

File tree

5 files changed

+135
-94
lines changed

5 files changed

+135
-94
lines changed

compiler/rustc_typeck/src/check/demand.rs

+102-82
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase;
1313
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1414
use rustc_middle::ty::print::with_no_trimmed_paths;
1515
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
16-
use rustc_span::symbol::sym;
16+
use rustc_span::symbol::{sym, Symbol};
1717
use rustc_span::{BytePos, Span};
1818

1919
use super::method::probe;
@@ -24,7 +24,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2424
pub fn emit_coerce_suggestions(
2525
&self,
2626
err: &mut DiagnosticBuilder<'_>,
27-
expr: &hir::Expr<'_>,
27+
expr: &hir::Expr<'tcx>,
2828
expr_ty: Ty<'tcx>,
2929
expected: Ty<'tcx>,
3030
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -109,7 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
109109

110110
pub fn demand_coerce(
111111
&self,
112-
expr: &hir::Expr<'_>,
112+
expr: &hir::Expr<'tcx>,
113113
checked_ty: Ty<'tcx>,
114114
expected: Ty<'tcx>,
115115
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -129,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
129129
/// will be permitted if the diverges flag is currently "always".
130130
pub fn demand_coerce_diag(
131131
&self,
132-
expr: &hir::Expr<'_>,
132+
expr: &hir::Expr<'tcx>,
133133
checked_ty: Ty<'tcx>,
134134
expected: Ty<'tcx>,
135135
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -338,31 +338,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
338338
})
339339
.collect();
340340

341-
if let [variant] = &compatible_variants[..] {
342-
// Just a single matching variant.
343-
err.multipart_suggestion(
344-
&format!("try wrapping the expression in `{}`", variant),
345-
vec![
346-
(expr.span.shrink_to_lo(), format!("{}(", variant)),
347-
(expr.span.shrink_to_hi(), ")".to_string()),
348-
],
349-
Applicability::MaybeIncorrect,
350-
);
351-
} else if compatible_variants.len() > 1 {
352-
// More than one matching variant.
353-
err.multipart_suggestions(
354-
&format!(
355-
"try wrapping the expression in a variant of `{}`",
356-
self.tcx.def_path_str(expected_adt.did)
357-
),
358-
compatible_variants.into_iter().map(|variant| {
341+
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
342+
Some(ident) => format!("{}: ", ident),
343+
None => format!(""),
344+
};
345+
346+
match &compatible_variants[..] {
347+
[] => { /* No variants to format */ }
348+
[variant] => {
349+
// Just a single matching variant.
350+
err.multipart_suggestion_verbose(
351+
&format!("try wrapping the expression in `{}`", variant),
359352
vec![
360-
(expr.span.shrink_to_lo(), format!("{}(", variant)),
353+
(expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
361354
(expr.span.shrink_to_hi(), ")".to_string()),
362-
]
363-
}),
364-
Applicability::MaybeIncorrect,
365-
);
355+
],
356+
Applicability::MaybeIncorrect,
357+
);
358+
}
359+
_ => {
360+
// More than one matching variant.
361+
err.multipart_suggestions(
362+
&format!(
363+
"try wrapping the expression in a variant of `{}`",
364+
self.tcx.def_path_str(expected_adt.did)
365+
),
366+
compatible_variants.into_iter().map(|variant| {
367+
vec![
368+
(expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
369+
(expr.span.shrink_to_hi(), ")".to_string()),
370+
]
371+
}),
372+
Applicability::MaybeIncorrect,
373+
);
374+
}
366375
}
367376
}
368377
}
@@ -483,33 +492,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
483492
}
484493
}
485494

486-
crate fn is_hir_id_from_struct_pattern_shorthand_field(
495+
crate fn maybe_get_struct_pattern_shorthand_field(
487496
&self,
488-
hir_id: hir::HirId,
489-
sp: Span,
490-
) -> bool {
491-
let sm = self.sess().source_map();
492-
let parent_id = self.tcx.hir().get_parent_node(hir_id);
493-
if let Some(parent) = self.tcx.hir().find(parent_id) {
494-
// Account for fields
495-
if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent
496-
{
497-
if let Ok(src) = sm.span_to_snippet(sp) {
498-
for field in *fields {
499-
if field.ident.as_str() == src && field.is_shorthand {
500-
return true;
501-
}
497+
expr: &hir::Expr<'_>,
498+
) -> Option<Symbol> {
499+
let hir = self.tcx.hir();
500+
let local = match expr {
501+
hir::Expr {
502+
kind:
503+
hir::ExprKind::Path(hir::QPath::Resolved(
504+
None,
505+
hir::Path {
506+
res: hir::def::Res::Local(_),
507+
segments: [hir::PathSegment { ident, .. }],
508+
..
509+
},
510+
)),
511+
..
512+
} => Some(ident),
513+
_ => None,
514+
}?;
515+
516+
match hir.find(hir.get_parent_node(expr.hir_id))? {
517+
Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => {
518+
for field in *fields {
519+
if field.ident.name == local.name && field.is_shorthand {
520+
return Some(local.name);
502521
}
503522
}
504523
}
524+
_ => {}
505525
}
506-
false
526+
527+
None
507528
}
508529

509530
/// If the given `HirId` corresponds to a block with a trailing expression, return that expression
510-
crate fn maybe_get_block_expr(&self, hir_id: hir::HirId) -> Option<&'tcx hir::Expr<'tcx>> {
511-
match self.tcx.hir().find(hir_id)? {
512-
Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, ..), .. }) => block.expr,
531+
crate fn maybe_get_block_expr(&self, expr: &hir::Expr<'tcx>) -> Option<&'tcx hir::Expr<'tcx>> {
532+
match expr {
533+
hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr,
513534
_ => None,
514535
}
515536
}
@@ -547,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
547568
/// `&mut`!".
548569
pub fn check_ref(
549570
&self,
550-
expr: &hir::Expr<'_>,
571+
expr: &hir::Expr<'tcx>,
551572
checked_ty: Ty<'tcx>,
552573
expected: Ty<'tcx>,
553574
) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> {
@@ -565,9 +586,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
565586
s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
566587
};
567588

568-
let is_struct_pat_shorthand_field =
569-
self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp);
570-
571589
// `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
572590
let expr = expr.peel_drop_temps();
573591

@@ -661,11 +679,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
661679
false,
662680
));
663681
}
664-
let field_name = if is_struct_pat_shorthand_field {
665-
format!("{}: ", sugg_expr)
666-
} else {
667-
String::new()
682+
683+
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
684+
Some(ident) => format!("{}: ", ident),
685+
None => format!(""),
668686
};
687+
669688
if let Some(hir::Node::Expr(hir::Expr {
670689
kind: hir::ExprKind::Assign(left_expr, ..),
671690
..
@@ -695,14 +714,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
695714
hir::Mutability::Mut => (
696715
sp,
697716
"consider mutably borrowing here",
698-
format!("{}&mut {}", field_name, sugg_expr),
717+
format!("{}&mut {}", prefix, sugg_expr),
699718
Applicability::MachineApplicable,
700719
false,
701720
),
702721
hir::Mutability::Not => (
703722
sp,
704723
"consider borrowing here",
705-
format!("{}&{}", field_name, sugg_expr),
724+
format!("{}&{}", prefix, sugg_expr),
706725
Applicability::MachineApplicable,
707726
false,
708727
),
@@ -846,32 +865,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
846865
if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp)
847866
|| checked_ty.is_box()
848867
{
849-
if let Ok(code) = sm.span_to_snippet(expr.span) {
850-
let message = if checked_ty.is_box() {
851-
"consider unboxing the value"
852-
} else if checked_ty.is_region_ptr() {
853-
"consider dereferencing the borrow"
854-
} else {
855-
"consider dereferencing the type"
856-
};
857-
let (span, suggestion) = if is_struct_pat_shorthand_field {
858-
(expr.span, format!("{}: *{}", code, code))
859-
} else if self.is_else_if_block(expr) {
860-
// Don't suggest nonsense like `else *if`
861-
return None;
862-
} else if let Some(expr) = self.maybe_get_block_expr(expr.hir_id) {
863-
(expr.span.shrink_to_lo(), "*".to_string())
864-
} else {
865-
(expr.span.shrink_to_lo(), "*".to_string())
866-
};
867-
return Some((
868-
span,
869-
message,
870-
suggestion,
871-
Applicability::MachineApplicable,
872-
true,
873-
));
874-
}
868+
let message = if checked_ty.is_box() {
869+
"consider unboxing the value"
870+
} else if checked_ty.is_region_ptr() {
871+
"consider dereferencing the borrow"
872+
} else {
873+
"consider dereferencing the type"
874+
};
875+
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
876+
Some(ident) => format!("{}: ", ident),
877+
None => format!(""),
878+
};
879+
let (span, suggestion) = if self.is_else_if_block(expr) {
880+
// Don't suggest nonsense like `else *if`
881+
return None;
882+
} else if let Some(expr) = self.maybe_get_block_expr(expr) {
883+
// prefix should be empty here..
884+
(expr.span.shrink_to_lo(), "*".to_string())
885+
} else {
886+
(expr.span.shrink_to_lo(), format!("{}*", prefix))
887+
};
888+
return Some((
889+
span,
890+
message,
891+
suggestion,
892+
Applicability::MachineApplicable,
893+
true,
894+
));
875895
}
876896
}
877897
}

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
208208
pub fn suggest_deref_ref_or_into(
209209
&self,
210210
err: &mut DiagnosticBuilder<'_>,
211-
expr: &hir::Expr<'_>,
211+
expr: &hir::Expr<'tcx>,
212212
expected: Ty<'tcx>,
213213
found: Ty<'tcx>,
214214
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
231231
}
232232
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
233233
let is_struct_pat_shorthand_field =
234-
self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
234+
self.maybe_get_struct_pattern_shorthand_field(expr).is_some();
235235
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
236236
if !methods.is_empty() {
237237
if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {

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

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ enum Hey<A, B> {
33
B(B),
44
}
55

6+
struct Foo {
7+
bar: Option<i32>,
8+
}
9+
610
fn f() {}
711

812
fn a() -> Option<()> {
@@ -40,4 +44,8 @@ fn main() {
4044
let _: Hey<i32, bool> = false;
4145
//~^ ERROR mismatched types
4246
//~| HELP try wrapping
47+
let bar = 1i32;
48+
let _ = Foo { bar };
49+
//~^ ERROR mismatched types
50+
//~| HELP try wrapping
4351
}

0 commit comments

Comments
 (0)