Skip to content

Commit fc6e34f

Browse files
authored
Rollup merge of #127891 - estebank:enum-type-sugg, r=estebank
Tweak suggestions when using incorrect type of enum literal More accurate suggestions when writing wrong style of enum variant literal: ``` error[E0533]: expected value, found struct variant `E::Empty3` --> $DIR/empty-struct-braces-expr.rs:18:14 | LL | let e3 = E::Empty3; | ^^^^^^^^^ not a value | help: you might have meant to create a new value of the struct | LL | let e3 = E::Empty3 {}; | ++ ``` ``` error[E0533]: expected value, found struct variant `E::V` --> $DIR/struct-literal-variant-in-if.rs:10:13 | LL | if x == E::V { field } {} | ^^^^ not a value | help: you might have meant to create a new value of the struct | LL | if x == (E::V { field }) {} | + + ``` ``` error[E0618]: expected function, found enum variant `Enum::Unit` --> $DIR/suggestion-highlights.rs:15:5 | LL | Unit, | ---- enum variant `Enum::Unit` defined here ... LL | Enum::Unit(); | ^^^^^^^^^^-- | | | call expression requires function | help: `Enum::Unit` is a unit enum variant, and does not take parentheses to be constructed | LL - Enum::Unit(); LL + Enum::Unit; | ``` ``` error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope --> $DIR/suggestion-highlights.rs:36:11 | LL | enum Enum { | --------- variant or associated item `tuple` not found for this enum ... LL | Enum::tuple; | ^^^^^ variant or associated item not found in `Enum` | help: there is a variant with a similar name | LL | Enum::Tuple(/* i32 */); | ~~~~~~~~~~~~~~~~; | ``` Tweak "field not found" suggestion when giving struct literal for tuple struct type: ``` error[E0560]: struct `S` has no field named `x` --> $DIR/nested-non-tuple-tuple-struct.rs:8:19 | LL | pub struct S(f32, f32); | - `S` defined here ... LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); | ^ field does not exist | help: `S` is a tuple struct, use the appropriate syntax | LL | let _x = (S(/* f32 */, /* f32 */), S { x: 3.0, y: 4.0 }); | ~~~~~~~~~~~~~~~~~~~~~~~
2 parents 39ccb8a + 33bd4bd commit fc6e34f

24 files changed

+1508
-79
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+60-6
Original file line numberDiff line numberDiff line change
@@ -1087,20 +1087,74 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
10871087
);
10881088

10891089
let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
1090-
if let Some(suggested_name) = find_best_match_for_name(
1090+
if let Some(variant_name) = find_best_match_for_name(
10911091
&adt_def
10921092
.variants()
10931093
.iter()
10941094
.map(|variant| variant.name)
10951095
.collect::<Vec<Symbol>>(),
10961096
assoc_ident.name,
10971097
None,
1098-
) {
1099-
err.span_suggestion(
1100-
assoc_ident.span,
1098+
) && let Some(variant) =
1099+
adt_def.variants().iter().find(|s| s.name == variant_name)
1100+
{
1101+
let mut suggestion = vec![(assoc_ident.span, variant_name.to_string())];
1102+
if let hir::Node::Stmt(hir::Stmt {
1103+
kind: hir::StmtKind::Semi(ref expr),
1104+
..
1105+
})
1106+
| hir::Node::Expr(ref expr) = tcx.parent_hir_node(hir_ref_id)
1107+
&& let hir::ExprKind::Struct(..) = expr.kind
1108+
{
1109+
match variant.ctor {
1110+
None => {
1111+
// struct
1112+
suggestion = vec![(
1113+
assoc_ident.span.with_hi(expr.span.hi()),
1114+
if variant.fields.is_empty() {
1115+
format!("{variant_name} {{}}")
1116+
} else {
1117+
format!(
1118+
"{variant_name} {{ {} }}",
1119+
variant
1120+
.fields
1121+
.iter()
1122+
.map(|f| format!("{}: /* value */", f.name))
1123+
.collect::<Vec<_>>()
1124+
.join(", ")
1125+
)
1126+
},
1127+
)];
1128+
}
1129+
Some((hir::def::CtorKind::Fn, def_id)) => {
1130+
// tuple
1131+
let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
1132+
let inputs = fn_sig.inputs().skip_binder();
1133+
suggestion = vec![(
1134+
assoc_ident.span.with_hi(expr.span.hi()),
1135+
format!(
1136+
"{variant_name}({})",
1137+
inputs
1138+
.iter()
1139+
.map(|i| format!("/* {i} */"))
1140+
.collect::<Vec<_>>()
1141+
.join(", ")
1142+
),
1143+
)];
1144+
}
1145+
Some((hir::def::CtorKind::Const, _)) => {
1146+
// unit
1147+
suggestion = vec![(
1148+
assoc_ident.span.with_hi(expr.span.hi()),
1149+
variant_name.to_string(),
1150+
)];
1151+
}
1152+
}
1153+
}
1154+
err.multipart_suggestion_verbose(
11011155
"there is a variant with a similar name",
1102-
suggested_name,
1103-
Applicability::MaybeIncorrect,
1156+
suggestion,
1157+
Applicability::HasPlaceholders,
11041158
);
11051159
} else {
11061160
err.span_label(

compiler/rustc_hir_typeck/src/expr.rs

+34-10
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
519519
Ty::new_error(tcx, e)
520520
}
521521
Res::Def(DefKind::Variant, _) => {
522-
let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, E0533, "value");
522+
let e = report_unexpected_variant_res(
523+
tcx,
524+
res,
525+
Some(expr),
526+
qpath,
527+
expr.span,
528+
E0533,
529+
"value",
530+
);
523531
Ty::new_error(tcx, e)
524532
}
525533
_ => {
@@ -2210,8 +2218,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22102218
);
22112219

22122220
let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap();
2213-
match variant.ctor_kind() {
2214-
Some(CtorKind::Fn) => match ty.kind() {
2221+
match variant.ctor {
2222+
Some((CtorKind::Fn, def_id)) => match ty.kind() {
22152223
ty::Adt(adt, ..) if adt.is_enum() => {
22162224
err.span_label(
22172225
variant_ident_span,
@@ -2222,28 +2230,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22222230
),
22232231
);
22242232
err.span_label(field.ident.span, "field does not exist");
2233+
let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity();
2234+
let inputs = fn_sig.inputs().skip_binder();
2235+
let fields = format!(
2236+
"({})",
2237+
inputs.iter().map(|i| format!("/* {i} */")).collect::<Vec<_>>().join(", ")
2238+
);
2239+
let (replace_span, sugg) = match expr.kind {
2240+
hir::ExprKind::Struct(qpath, ..) => {
2241+
(qpath.span().shrink_to_hi().with_hi(expr.span.hi()), fields)
2242+
}
2243+
_ => {
2244+
(expr.span, format!("{ty}::{variant}{fields}", variant = variant.name))
2245+
}
2246+
};
22252247
err.span_suggestion_verbose(
2226-
expr.span,
2248+
replace_span,
22272249
format!(
22282250
"`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax",
22292251
adt = ty,
22302252
variant = variant.name,
22312253
),
2232-
format!(
2233-
"{adt}::{variant}(/* fields */)",
2234-
adt = ty,
2235-
variant = variant.name,
2236-
),
2254+
sugg,
22372255
Applicability::HasPlaceholders,
22382256
);
22392257
}
22402258
_ => {
22412259
err.span_label(variant_ident_span, format!("`{ty}` defined here"));
22422260
err.span_label(field.ident.span, "field does not exist");
2261+
let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity();
2262+
let inputs = fn_sig.inputs().skip_binder();
2263+
let fields = format!(
2264+
"({})",
2265+
inputs.iter().map(|i| format!("/* {i} */")).collect::<Vec<_>>().join(", ")
2266+
);
22432267
err.span_suggestion_verbose(
22442268
expr.span,
22452269
format!("`{ty}` is a tuple {kind_name}, use the appropriate syntax",),
2246-
format!("{ty}(/* fields */)"),
2270+
format!("{ty}{fields}"),
22472271
Applicability::HasPlaceholders,
22482272
);
22492273
}

compiler/rustc_hir_typeck/src/lib.rs

+58-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use crate::expectation::Expectation;
5353
use crate::fn_ctxt::LoweredTy;
5454
use crate::gather_locals::GatherLocalsVisitor;
5555
use rustc_data_structures::unord::UnordSet;
56-
use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed};
56+
use rustc_errors::{codes::*, struct_span_code_err, Applicability, ErrorGuaranteed};
5757
use rustc_hir as hir;
5858
use rustc_hir::def::{DefKind, Res};
5959
use rustc_hir::intravisit::Visitor;
@@ -346,6 +346,7 @@ impl<'tcx> EnclosingBreakables<'tcx> {
346346
fn report_unexpected_variant_res(
347347
tcx: TyCtxt<'_>,
348348
res: Res,
349+
expr: Option<&hir::Expr<'_>>,
349350
qpath: &hir::QPath<'_>,
350351
span: Span,
351352
err_code: ErrCode,
@@ -356,7 +357,7 @@ fn report_unexpected_variant_res(
356357
_ => res.descr(),
357358
};
358359
let path_str = rustc_hir_pretty::qpath_to_string(&tcx, qpath);
359-
let err = tcx
360+
let mut err = tcx
360361
.dcx()
361362
.struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`"))
362363
.with_code(err_code);
@@ -366,6 +367,61 @@ fn report_unexpected_variant_res(
366367
err.with_span_label(span, "`fn` calls are not allowed in patterns")
367368
.with_help(format!("for more information, visit {patterns_url}"))
368369
}
370+
Res::Def(DefKind::Variant, _) if let Some(expr) = expr => {
371+
err.span_label(span, format!("not a {expected}"));
372+
let variant = tcx.expect_variant_res(res);
373+
let sugg = if variant.fields.is_empty() {
374+
" {}".to_string()
375+
} else {
376+
format!(
377+
" {{ {} }}",
378+
variant
379+
.fields
380+
.iter()
381+
.map(|f| format!("{}: /* value */", f.name))
382+
.collect::<Vec<_>>()
383+
.join(", ")
384+
)
385+
};
386+
let descr = "you might have meant to create a new value of the struct";
387+
let mut suggestion = vec![];
388+
match tcx.parent_hir_node(expr.hir_id) {
389+
hir::Node::Expr(hir::Expr {
390+
kind: hir::ExprKind::Call(..),
391+
span: call_span,
392+
..
393+
}) => {
394+
suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg));
395+
}
396+
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => {
397+
suggestion.push((expr.span.shrink_to_lo(), "(".to_string()));
398+
if let hir::Node::Expr(drop_temps) = tcx.parent_hir_node(*hir_id)
399+
&& let hir::ExprKind::DropTemps(_) = drop_temps.kind
400+
&& let hir::Node::Expr(parent) = tcx.parent_hir_node(drop_temps.hir_id)
401+
&& let hir::ExprKind::If(condition, block, None) = parent.kind
402+
&& condition.hir_id == drop_temps.hir_id
403+
&& let hir::ExprKind::Block(block, _) = block.kind
404+
&& block.stmts.is_empty()
405+
&& let Some(expr) = block.expr
406+
&& let hir::ExprKind::Path(..) = expr.kind
407+
{
408+
// Special case: you can incorrectly write an equality condition:
409+
// if foo == Struct { field } { /* if body */ }
410+
// which should have been written
411+
// if foo == (Struct { field }) { /* if body */ }
412+
suggestion.push((block.span.shrink_to_hi(), ")".to_string()));
413+
} else {
414+
suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg));
415+
}
416+
}
417+
_ => {
418+
suggestion.push((span.shrink_to_hi(), sugg));
419+
}
420+
}
421+
422+
err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect);
423+
err
424+
}
369425
_ => err.with_span_label(span, format!("not a {expected}")),
370426
}
371427
.emit()

compiler/rustc_hir_typeck/src/method/suggest.rs

+116-5
Original file line numberDiff line numberDiff line change
@@ -1596,16 +1596,127 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15961596
// that had unsatisfied trait bounds
15971597
if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
15981598
let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
1599-
if let Some(suggestion) = edit_distance::find_best_match_for_name(
1599+
if let Some(var_name) = edit_distance::find_best_match_for_name(
16001600
&adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
16011601
item_name.name,
16021602
None,
1603-
) {
1604-
err.span_suggestion(
1605-
span,
1603+
) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name)
1604+
{
1605+
let mut suggestion = vec![(span, var_name.to_string())];
1606+
if let SelfSource::QPath(ty) = source
1607+
&& let hir::Node::Expr(ref path_expr) = self.tcx.parent_hir_node(ty.hir_id)
1608+
&& let hir::ExprKind::Path(_) = path_expr.kind
1609+
&& let hir::Node::Stmt(hir::Stmt {
1610+
kind: hir::StmtKind::Semi(ref parent), ..
1611+
})
1612+
| hir::Node::Expr(ref parent) = self.tcx.parent_hir_node(path_expr.hir_id)
1613+
{
1614+
let replacement_span =
1615+
if let hir::ExprKind::Call(..) | hir::ExprKind::Struct(..) = parent.kind {
1616+
// We want to replace the parts that need to go, like `()` and `{}`.
1617+
span.with_hi(parent.span.hi())
1618+
} else {
1619+
span
1620+
};
1621+
match (variant.ctor, parent.kind) {
1622+
(None, hir::ExprKind::Struct(..)) => {
1623+
// We want a struct and we have a struct. We won't suggest changing
1624+
// the fields (at least for now).
1625+
suggestion = vec![(span, var_name.to_string())];
1626+
}
1627+
(None, _) => {
1628+
// struct
1629+
suggestion = vec![(
1630+
replacement_span,
1631+
if variant.fields.is_empty() {
1632+
format!("{var_name} {{}}")
1633+
} else {
1634+
format!(
1635+
"{var_name} {{ {} }}",
1636+
variant
1637+
.fields
1638+
.iter()
1639+
.map(|f| format!("{}: /* value */", f.name))
1640+
.collect::<Vec<_>>()
1641+
.join(", ")
1642+
)
1643+
},
1644+
)];
1645+
}
1646+
(Some((hir::def::CtorKind::Const, _)), _) => {
1647+
// unit, remove the `()`.
1648+
suggestion = vec![(replacement_span, var_name.to_string())];
1649+
}
1650+
(
1651+
Some((hir::def::CtorKind::Fn, def_id)),
1652+
hir::ExprKind::Call(rcvr, args),
1653+
) => {
1654+
let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity();
1655+
let inputs = fn_sig.inputs().skip_binder();
1656+
// FIXME: reuse the logic for "change args" suggestion to account for types
1657+
// involved and detect things like substitution.
1658+
match (inputs, args) {
1659+
(inputs, []) => {
1660+
// Add arguments.
1661+
suggestion.push((
1662+
rcvr.span.shrink_to_hi().with_hi(parent.span.hi()),
1663+
format!(
1664+
"({})",
1665+
inputs
1666+
.iter()
1667+
.map(|i| format!("/* {i} */"))
1668+
.collect::<Vec<String>>()
1669+
.join(", ")
1670+
),
1671+
));
1672+
}
1673+
(_, [arg]) if inputs.len() != args.len() => {
1674+
// Replace arguments.
1675+
suggestion.push((
1676+
arg.span,
1677+
inputs
1678+
.iter()
1679+
.map(|i| format!("/* {i} */"))
1680+
.collect::<Vec<String>>()
1681+
.join(", "),
1682+
));
1683+
}
1684+
(_, [arg_start, .., arg_end]) if inputs.len() != args.len() => {
1685+
// Replace arguments.
1686+
suggestion.push((
1687+
arg_start.span.to(arg_end.span),
1688+
inputs
1689+
.iter()
1690+
.map(|i| format!("/* {i} */"))
1691+
.collect::<Vec<String>>()
1692+
.join(", "),
1693+
));
1694+
}
1695+
// Argument count is the same, keep as is.
1696+
_ => {}
1697+
}
1698+
}
1699+
(Some((hir::def::CtorKind::Fn, def_id)), _) => {
1700+
let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity();
1701+
let inputs = fn_sig.inputs().skip_binder();
1702+
suggestion = vec![(
1703+
replacement_span,
1704+
format!(
1705+
"{var_name}({})",
1706+
inputs
1707+
.iter()
1708+
.map(|i| format!("/* {i} */"))
1709+
.collect::<Vec<String>>()
1710+
.join(", ")
1711+
),
1712+
)];
1713+
}
1714+
}
1715+
}
1716+
err.multipart_suggestion_verbose(
16061717
"there is a variant with a similar name",
16071718
suggestion,
1608-
Applicability::MaybeIncorrect,
1719+
Applicability::HasPlaceholders,
16091720
);
16101721
}
16111722
}

0 commit comments

Comments
 (0)