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
This commit is contained in:
commit
ff1b653cdb
5 changed files with 135 additions and 94 deletions
|
@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase;
|
|||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use super::method::probe;
|
||||
|
@ -24,7 +24,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pub fn emit_coerce_suggestions(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
expr_ty: Ty<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
|
@ -109,7 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
pub fn demand_coerce(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
checked_ty: Ty<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
|
@ -129,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// will be permitted if the diverges flag is currently "always".
|
||||
pub fn demand_coerce_diag(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
checked_ty: Ty<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
|
@ -338,17 +338,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
if let [variant] = &compatible_variants[..] {
|
||||
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
|
||||
Some(ident) => format!("{}: ", ident),
|
||||
None => format!(""),
|
||||
};
|
||||
|
||||
match &compatible_variants[..] {
|
||||
[] => { /* No variants to format */ }
|
||||
[variant] => {
|
||||
// Just a single matching variant.
|
||||
err.multipart_suggestion(
|
||||
err.multipart_suggestion_verbose(
|
||||
&format!("try wrapping the expression in `{}`", variant),
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
|
||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else if compatible_variants.len() > 1 {
|
||||
}
|
||||
_ => {
|
||||
// More than one matching variant.
|
||||
err.multipart_suggestions(
|
||||
&format!(
|
||||
|
@ -357,7 +365,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
),
|
||||
compatible_variants.into_iter().map(|variant| {
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
|
||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
}),
|
||||
|
@ -366,6 +374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_conversion_methods(
|
||||
&self,
|
||||
|
@ -483,33 +492,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
crate fn is_hir_id_from_struct_pattern_shorthand_field(
|
||||
crate fn maybe_get_struct_pattern_shorthand_field(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
sp: Span,
|
||||
) -> bool {
|
||||
let sm = self.sess().source_map();
|
||||
let parent_id = self.tcx.hir().get_parent_node(hir_id);
|
||||
if let Some(parent) = self.tcx.hir().find(parent_id) {
|
||||
// Account for fields
|
||||
if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent
|
||||
{
|
||||
if let Ok(src) = sm.span_to_snippet(sp) {
|
||||
expr: &hir::Expr<'_>,
|
||||
) -> Option<Symbol> {
|
||||
let hir = self.tcx.hir();
|
||||
let local = match expr {
|
||||
hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(
|
||||
None,
|
||||
hir::Path {
|
||||
res: hir::def::Res::Local(_),
|
||||
segments: [hir::PathSegment { ident, .. }],
|
||||
..
|
||||
},
|
||||
)),
|
||||
..
|
||||
} => Some(ident),
|
||||
_ => None,
|
||||
}?;
|
||||
|
||||
match hir.find(hir.get_parent_node(expr.hir_id))? {
|
||||
Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => {
|
||||
for field in *fields {
|
||||
if field.ident.as_str() == src && field.is_shorthand {
|
||||
return true;
|
||||
if field.ident.name == local.name && field.is_shorthand {
|
||||
return Some(local.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// If the given `HirId` corresponds to a block with a trailing expression, return that expression
|
||||
crate fn maybe_get_block_expr(&self, hir_id: hir::HirId) -> Option<&'tcx hir::Expr<'tcx>> {
|
||||
match self.tcx.hir().find(hir_id)? {
|
||||
Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, ..), .. }) => block.expr,
|
||||
crate fn maybe_get_block_expr(&self, expr: &hir::Expr<'tcx>) -> Option<&'tcx hir::Expr<'tcx>> {
|
||||
match expr {
|
||||
hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -547,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// `&mut`!".
|
||||
pub fn check_ref(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
checked_ty: Ty<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> {
|
||||
|
@ -565,9 +586,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
|
||||
};
|
||||
|
||||
let is_struct_pat_shorthand_field =
|
||||
self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp);
|
||||
|
||||
// `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
|
||||
let expr = expr.peel_drop_temps();
|
||||
|
||||
|
@ -661,11 +679,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
false,
|
||||
));
|
||||
}
|
||||
let field_name = if is_struct_pat_shorthand_field {
|
||||
format!("{}: ", sugg_expr)
|
||||
} else {
|
||||
String::new()
|
||||
|
||||
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
|
||||
Some(ident) => format!("{}: ", ident),
|
||||
None => format!(""),
|
||||
};
|
||||
|
||||
if let Some(hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Assign(left_expr, ..),
|
||||
..
|
||||
|
@ -695,14 +714,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
hir::Mutability::Mut => (
|
||||
sp,
|
||||
"consider mutably borrowing here",
|
||||
format!("{}&mut {}", field_name, sugg_expr),
|
||||
format!("{}&mut {}", prefix, sugg_expr),
|
||||
Applicability::MachineApplicable,
|
||||
false,
|
||||
),
|
||||
hir::Mutability::Not => (
|
||||
sp,
|
||||
"consider borrowing here",
|
||||
format!("{}&{}", field_name, sugg_expr),
|
||||
format!("{}&{}", prefix, sugg_expr),
|
||||
Applicability::MachineApplicable,
|
||||
false,
|
||||
),
|
||||
|
@ -846,7 +865,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp)
|
||||
|| checked_ty.is_box()
|
||||
{
|
||||
if let Ok(code) = sm.span_to_snippet(expr.span) {
|
||||
let message = if checked_ty.is_box() {
|
||||
"consider unboxing the value"
|
||||
} else if checked_ty.is_region_ptr() {
|
||||
|
@ -854,15 +872,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
"consider dereferencing the type"
|
||||
};
|
||||
let (span, suggestion) = if is_struct_pat_shorthand_field {
|
||||
(expr.span, format!("{}: *{}", code, code))
|
||||
} else if self.is_else_if_block(expr) {
|
||||
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
|
||||
Some(ident) => format!("{}: ", ident),
|
||||
None => format!(""),
|
||||
};
|
||||
let (span, suggestion) = if self.is_else_if_block(expr) {
|
||||
// Don't suggest nonsense like `else *if`
|
||||
return None;
|
||||
} else if let Some(expr) = self.maybe_get_block_expr(expr.hir_id) {
|
||||
} else if let Some(expr) = self.maybe_get_block_expr(expr) {
|
||||
// prefix should be empty here..
|
||||
(expr.span.shrink_to_lo(), "*".to_string())
|
||||
} else {
|
||||
(expr.span.shrink_to_lo(), "*".to_string())
|
||||
(expr.span.shrink_to_lo(), format!("{}*", prefix))
|
||||
};
|
||||
return Some((
|
||||
span,
|
||||
|
@ -875,7 +896,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pub fn suggest_deref_ref_or_into(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
|
@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
|
||||
let is_struct_pat_shorthand_field =
|
||||
self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
|
||||
self.maybe_get_struct_pattern_shorthand_field(expr).is_some();
|
||||
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
|
||||
if !methods.is_empty() {
|
||||
if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
|
||||
|
|
|
@ -3,6 +3,10 @@ enum Hey<A, B> {
|
|||
B(B),
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
bar: Option<i32>,
|
||||
}
|
||||
|
||||
fn f() {}
|
||||
|
||||
fn a() -> Option<()> {
|
||||
|
@ -40,4 +44,8 @@ fn main() {
|
|||
let _: Hey<i32, bool> = false;
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP try wrapping
|
||||
let bar = 1i32;
|
||||
let _ = Foo { bar };
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP try wrapping
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:9:5
|
||||
--> $DIR/compatible-variants.rs:13:5
|
||||
|
|
||||
LL | fn a() -> Option<()> {
|
||||
| ---------- expected `Option<()>` because of return type
|
||||
|
@ -21,7 +21,7 @@ LL + Some(())
|
|||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:17:5
|
||||
--> $DIR/compatible-variants.rs:21:5
|
||||
|
|
||||
LL | fn b() -> Result<(), ()> {
|
||||
| -------------- expected `Result<(), ()>` because of return type
|
||||
|
@ -37,7 +37,7 @@ LL + Ok(())
|
|||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:23:25
|
||||
--> $DIR/compatible-variants.rs:27:25
|
||||
|
|
||||
LL | let _: Option<()> = while false {};
|
||||
| ---------- ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
|
||||
|
@ -52,7 +52,7 @@ LL | let _: Option<()> = Some(while false {});
|
|||
| +++++ +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:27:9
|
||||
--> $DIR/compatible-variants.rs:31:9
|
||||
|
|
||||
LL | while false {}
|
||||
| ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
|
||||
|
@ -69,7 +69,7 @@ LL + Some(())
|
|||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:31:31
|
||||
--> $DIR/compatible-variants.rs:35:31
|
||||
|
|
||||
LL | let _: Result<i32, i32> = 1;
|
||||
| ---------------- ^ expected enum `Result`, found integer
|
||||
|
@ -86,7 +86,7 @@ LL | let _: Result<i32, i32> = Err(1);
|
|||
| ++++ +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:34:26
|
||||
--> $DIR/compatible-variants.rs:38:26
|
||||
|
|
||||
LL | let _: Option<i32> = 1;
|
||||
| ----------- ^ expected enum `Option`, found integer
|
||||
|
@ -101,7 +101,7 @@ LL | let _: Option<i32> = Some(1);
|
|||
| +++++ +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:37:28
|
||||
--> $DIR/compatible-variants.rs:41:28
|
||||
|
|
||||
LL | let _: Hey<i32, i32> = 1;
|
||||
| ------------- ^ expected enum `Hey`, found integer
|
||||
|
@ -118,7 +118,7 @@ LL | let _: Hey<i32, i32> = Hey::B(1);
|
|||
| +++++++ +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:40:29
|
||||
--> $DIR/compatible-variants.rs:44:29
|
||||
|
|
||||
LL | let _: Hey<i32, bool> = false;
|
||||
| -------------- ^^^^^ expected enum `Hey`, found `bool`
|
||||
|
@ -132,6 +132,19 @@ help: try wrapping the expression in `Hey::B`
|
|||
LL | let _: Hey<i32, bool> = Hey::B(false);
|
||||
| +++++++ +
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:48:19
|
||||
|
|
||||
LL | let _ = Foo { bar };
|
||||
| ^^^ expected enum `Option`, found `i32`
|
||||
|
|
||||
= note: expected enum `Option<i32>`
|
||||
found type `i32`
|
||||
help: try wrapping the expression in `Some`
|
||||
|
|
||||
LL | let _ = Foo { bar: Some(bar) };
|
||||
| ++++++++++ +
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
|
@ -87,7 +87,7 @@ LL | let r = R { i };
|
|||
help: consider dereferencing the borrow
|
||||
|
|
||||
LL | let r = R { i: *i };
|
||||
| ~~~~~
|
||||
| ++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/deref-suggestion.rs:46:20
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue