From 2259db6af0d20dbc3496ec566bf37456d2ca7b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 10 Jul 2024 18:05:50 +0000 Subject: [PATCH 1/3] Add svg test for incorrect literal type suggestion --- .../suggestions/incorrect-variant-literal.rs | 55 ++ .../suggestions/incorrect-variant-literal.svg | 850 ++++++++++++++++++ 2 files changed, 905 insertions(+) create mode 100644 tests/ui/suggestions/incorrect-variant-literal.rs create mode 100644 tests/ui/suggestions/incorrect-variant-literal.svg diff --git a/tests/ui/suggestions/incorrect-variant-literal.rs b/tests/ui/suggestions/incorrect-variant-literal.rs new file mode 100644 index 0000000000000..aac2cc549044f --- /dev/null +++ b/tests/ui/suggestions/incorrect-variant-literal.rs @@ -0,0 +1,55 @@ +//@ only-linux +//@ compile-flags: --error-format=human --color=always + +enum Enum { + Unit, + Tuple(i32), + Struct { x: i32 }, +} + +fn main() { + Enum::Unit; + Enum::Tuple; + Enum::Struct; + Enum::Unit(); + Enum::Tuple(); + Enum::Struct(); + Enum::Unit {}; + Enum::Tuple {}; + Enum::Struct {}; + Enum::Unit(0); + Enum::Tuple(0); + Enum::Struct(0); + Enum::Unit { x: 0 }; + Enum::Tuple { x: 0 }; + Enum::Struct { x: 0 }; // ok + Enum::Unit(0, 0); + Enum::Tuple(0, 0); + Enum::Struct(0, 0); + Enum::Unit { x: 0, y: 0 }; + + Enum::Tuple { x: 0, y: 0 }; + + Enum::Struct { x: 0, y: 0 }; + Enum::unit; + Enum::tuple; + Enum::r#struct; + Enum::unit(); + Enum::tuple(); + Enum::r#struct(); + Enum::unit {}; + Enum::tuple {}; + Enum::r#struct {}; + Enum::unit(0); + Enum::tuple(0); + Enum::r#struct(0); + Enum::unit { x: 0 }; + Enum::tuple { x: 0 }; + Enum::r#struct { x: 0 }; + Enum::unit(0, 0); + Enum::tuple(0, 0); + Enum::r#struct(0, 0); + Enum::unit { x: 0, y: 0 }; + Enum::tuple { x: 0, y: 0 }; + Enum::r#struct { x: 0, y: 0 }; +} diff --git a/tests/ui/suggestions/incorrect-variant-literal.svg b/tests/ui/suggestions/incorrect-variant-literal.svg new file mode 100644 index 0000000000000..7d5353d02a747 --- /dev/null +++ b/tests/ui/suggestions/incorrect-variant-literal.svg @@ -0,0 +1,850 @@ + + + + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:13:5 + + | + + LL | Enum::Struct; + + | ^^^^^^^^^^^^ not a value + + + + error[E0618]: expected function, found enum variant `Enum::Unit` + + --> $DIR/incorrect-variant-literal.rs:14: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[E0061]: this enum variant takes 1 argument but 0 arguments were supplied + + --> $DIR/incorrect-variant-literal.rs:15:5 + + | + + LL | Enum::Tuple(); + + | ^^^^^^^^^^^-- argument #1 of type `i32` is missing + + | + + note: tuple variant defined here + + --> $DIR/incorrect-variant-literal.rs:6:5 + + | + + LL | Tuple(i32), + + | ^^^^^ + + help: provide the argument + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~ + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:16:5 + + | + + LL | Enum::Struct(); + + | ^^^^^^^^^^^^ not a value + + + + error[E0063]: missing field `0` in initializer of `Enum` + + --> $DIR/incorrect-variant-literal.rs:18:5 + + | + + LL | Enum::Tuple {}; + + | ^^^^^^^^^^^ missing `0` + + + + error[E0063]: missing field `x` in initializer of `Enum` + + --> $DIR/incorrect-variant-literal.rs:19:5 + + | + + LL | Enum::Struct {}; + + | ^^^^^^^^^^^^ missing `x` + + + + error[E0618]: expected function, found `Enum` + + --> $DIR/incorrect-variant-literal.rs:20:5 + + | + + LL | Unit, + + | ---- `Enum::Unit` defined here + + ... + + LL | Enum::Unit(0); + + | ^^^^^^^^^^--- + + | | + + | call expression requires function + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:22:5 + + | + + LL | Enum::Struct(0); + + | ^^^^^^^^^^^^ not a value + + + + error[E0559]: variant `Enum::Unit` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:23:18 + + | + + LL | Enum::Unit { x: 0 }; + + | ^ `Enum::Unit` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0559]: variant `Enum::Tuple` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:24:19 + + | + + LL | Tuple(i32), + + | ----- `Enum::Tuple` defined here + + ... + + LL | Enum::Tuple { x: 0 }; + + | ^ field does not exist + + | + + help: `Enum::Tuple` is a tuple variant, use the appropriate syntax + + | + + LL | Enum::Tuple(/* fields */); + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0618]: expected function, found `Enum` + + --> $DIR/incorrect-variant-literal.rs:26:5 + + | + + LL | Unit, + + | ---- `Enum::Unit` defined here + + ... + + LL | Enum::Unit(0, 0); + + | ^^^^^^^^^^------ + + | | + + | call expression requires function + + + + error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied + + --> $DIR/incorrect-variant-literal.rs:27:5 + + | + + LL | Enum::Tuple(0, 0); + + | ^^^^^^^^^^^ - unexpected argument #2 of type `{integer}` + + | + + note: tuple variant defined here + + --> $DIR/incorrect-variant-literal.rs:6:5 + + | + + LL | Tuple(i32), + + | ^^^^^ + + help: remove the extra argument + + | + + LL - Enum::Tuple(0, 0); + + LL + Enum::Tuple(0); + + | + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:28:5 + + | + + LL | Enum::Struct(0, 0); + + | ^^^^^^^^^^^^ not a value + + + + error[E0559]: variant `Enum::Unit` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:29:18 + + | + + LL | Enum::Unit { x: 0, y: 0 }; + + | ^ `Enum::Unit` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0559]: variant `Enum::Unit` has no field named `y` + + --> $DIR/incorrect-variant-literal.rs:29:24 + + | + + LL | Enum::Unit { x: 0, y: 0 }; + + | ^ `Enum::Unit` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0559]: variant `Enum::Tuple` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:31:19 + + | + + LL | Tuple(i32), + + | ----- `Enum::Tuple` defined here + + ... + + LL | Enum::Tuple { x: 0, y: 0 }; + + | ^ field does not exist + + | + + help: `Enum::Tuple` is a tuple variant, use the appropriate syntax + + | + + LL | Enum::Tuple(/* fields */); + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0559]: variant `Enum::Tuple` has no field named `y` + + --> $DIR/incorrect-variant-literal.rs:31:25 + + | + + LL | Tuple(i32), + + | ----- `Enum::Tuple` defined here + + ... + + LL | Enum::Tuple { x: 0, y: 0 }; + + | ^ field does not exist + + | + + help: `Enum::Tuple` is a tuple variant, use the appropriate syntax + + | + + LL | Enum::Tuple(/* fields */); + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0559]: variant `Enum::Struct` has no field named `y` + + --> $DIR/incorrect-variant-literal.rs:33:26 + + | + + LL | Enum::Struct { x: 0, y: 0 }; + + | ^ `Enum::Struct` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:34:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit; + + | ^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name (notice the capitalization): `Unit` + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:35: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: `Tuple` + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:36:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct; + + | ^^^^^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name: `Struct` + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:37:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit(); + + | ^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name (notice the capitalization): `Unit` + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:38: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: `Tuple` + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:39:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct(); + + | ^^^^^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name: `Struct` + + + + error[E0599]: no variant named `unit` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:40:11 + + | + + LL | enum Enum { + + | --------- variant `unit` not found here + + ... + + LL | Enum::unit {}; + + | ^^^^ help: there is a variant with a similar name (notice the capitalization): `Unit` + + + + error[E0599]: no variant named `tuple` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:41:11 + + | + + LL | enum Enum { + + | --------- variant `tuple` not found here + + ... + + LL | Enum::tuple {}; + + | ^^^^^ help: there is a variant with a similar name: `Tuple` + + + + error[E0599]: no variant named `r#struct` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:42:11 + + | + + LL | enum Enum { + + | --------- variant `r#struct` not found here + + ... + + LL | Enum::r#struct {}; + + | ^^^^^^^^ help: there is a variant with a similar name: `Struct` + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:43:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit(0); + + | ^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name (notice the capitalization): `Unit` + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:44:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `tuple` not found for this enum + + ... + + LL | Enum::tuple(0); + + | ^^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name: `Tuple` + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:45:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct(0); + + | ^^^^^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name: `Struct` + + + + error[E0599]: no variant named `unit` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:46:11 + + | + + LL | enum Enum { + + | --------- variant `unit` not found here + + ... + + LL | Enum::unit { x: 0 }; + + | ^^^^ help: there is a variant with a similar name (notice the capitalization): `Unit` + + + + error[E0599]: no variant named `tuple` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:47:11 + + | + + LL | enum Enum { + + | --------- variant `tuple` not found here + + ... + + LL | Enum::tuple { x: 0 }; + + | ^^^^^ help: there is a variant with a similar name: `Tuple` + + + + error[E0599]: no variant named `r#struct` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:48:11 + + | + + LL | enum Enum { + + | --------- variant `r#struct` not found here + + ... + + LL | Enum::r#struct { x: 0 }; + + | ^^^^^^^^ help: there is a variant with a similar name: `Struct` + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:49:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit(0, 0); + + | ^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name (notice the capitalization): `Unit` + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:50:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `tuple` not found for this enum + + ... + + LL | Enum::tuple(0, 0); + + | ^^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name: `Tuple` + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:51:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct(0, 0); + + | ^^^^^^^^ + + | | + + | variant or associated item not found in `Enum` + + | help: there is a variant with a similar name: `Struct` + + + + error[E0599]: no variant named `unit` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:52:11 + + | + + LL | enum Enum { + + | --------- variant `unit` not found here + + ... + + LL | Enum::unit { x: 0, y: 0 }; + + | ^^^^ help: there is a variant with a similar name (notice the capitalization): `Unit` + + + + error[E0599]: no variant named `tuple` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:53:11 + + | + + LL | enum Enum { + + | --------- variant `tuple` not found here + + ... + + LL | Enum::tuple { x: 0, y: 0 }; + + | ^^^^^ help: there is a variant with a similar name: `Tuple` + + + + error[E0599]: no variant named `r#struct` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:54:11 + + | + + LL | enum Enum { + + | --------- variant `r#struct` not found here + + ... + + LL | Enum::r#struct { x: 0, y: 0 }; + + | ^^^^^^^^ help: there is a variant with a similar name: `Struct` + + + + error: aborting due to 39 previous errors + + + + Some errors have detailed explanations: E0061, E0063, E0533, E0559, E0599, E0618. + + For more information about an error, try `rustc --explain E0061`. + + + + + + From ec7a188f16a6ec1f2e29f5d721f8ee5b92e2c99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 11 Jul 2024 16:23:03 +0000 Subject: [PATCH 2/3] 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 */); | ~~~~~~~~~~~~~~~~; | ``` --- .../src/hir_ty_lowering/mod.rs | 66 +- compiler/rustc_hir_typeck/src/expr.rs | 10 +- compiler/rustc_hir_typeck/src/lib.rs | 60 +- .../rustc_hir_typeck/src/method/suggest.rs | 121 ++- compiler/rustc_hir_typeck/src/pat.rs | 6 +- .../ui/empty/empty-struct-braces-expr.stderr | 37 +- .../error-variant-with-turbofishes.stderr | 5 + tests/ui/expr/issue-22933-2.stderr | 10 +- tests/ui/issues/issue-23217.stderr | 10 +- tests/ui/issues/issue-28971.stderr | 10 +- tests/ui/issues/issue-34209.stderr | 7 +- .../struct-literal-variant-in-if.stderr | 5 + tests/ui/resolve/issue-18252.stderr | 5 + tests/ui/resolve/issue-19452.stderr | 10 + tests/ui/resolve/privacy-enum-ctor.stderr | 20 + .../fn-or-tuple-struct-without-args.stderr | 5 + .../suggestions/incorrect-variant-literal.svg | 946 +++++++++++------- tests/ui/suggestions/suggest-variants.stderr | 34 +- ...t-variant-form-through-alias-caught.stderr | 5 + 19 files changed, 940 insertions(+), 432 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a665306f2c6a8..44a469514591b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1087,7 +1087,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggested_name) = find_best_match_for_name( + if let Some(variant_name) = find_best_match_for_name( &adt_def .variants() .iter() @@ -1095,12 +1095,66 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect::>(), assoc_ident.name, None, - ) { - err.span_suggestion( - assoc_ident.span, + ) && let Some(variant) = + adt_def.variants().iter().find(|s| s.name == variant_name) + { + let mut suggestion = vec![(assoc_ident.span, variant_name.to_string())]; + if let hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Semi(ref expr), + .. + }) + | hir::Node::Expr(ref expr) = tcx.parent_hir_node(hir_ref_id) + && let hir::ExprKind::Struct(..) = expr.kind + { + match variant.ctor { + None => { + // struct + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + if variant.fields.is_empty() { + format!("{variant_name} {{}}") + } else { + format!( + "{variant_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + Some((hir::def::CtorKind::Fn, def_id)) => { + // tuple + let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + format!( + "{variant_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + Some((hir::def::CtorKind::Const, _)) => { + // unit + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + variant_name.to_string(), + )]; + } + } + } + err.multipart_suggestion_verbose( "there is a variant with a similar name", - suggested_name, - Applicability::MaybeIncorrect, + suggestion, + Applicability::HasPlaceholders, ); } else { err.span_label( diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 035a3429ed76e..dc677595f05e0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -519,7 +519,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_error(tcx, e) } Res::Def(DefKind::Variant, _) => { - let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, E0533, "value"); + let e = report_unexpected_variant_res( + tcx, + res, + Some(expr), + qpath, + expr.span, + E0533, + "value", + ); Ty::new_error(tcx, e) } _ => { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index bdbdcee6446dc..2c79366450909 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use crate::expectation::Expectation; use crate::fn_ctxt::LoweredTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; +use rustc_errors::{codes::*, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; @@ -346,6 +346,7 @@ impl<'tcx> EnclosingBreakables<'tcx> { fn report_unexpected_variant_res( tcx: TyCtxt<'_>, res: Res, + expr: Option<&hir::Expr<'_>>, qpath: &hir::QPath<'_>, span: Span, err_code: ErrCode, @@ -356,7 +357,7 @@ fn report_unexpected_variant_res( _ => res.descr(), }; let path_str = rustc_hir_pretty::qpath_to_string(&tcx, qpath); - let err = tcx + let mut err = tcx .dcx() .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) .with_code(err_code); @@ -366,6 +367,61 @@ fn report_unexpected_variant_res( err.with_span_label(span, "`fn` calls are not allowed in patterns") .with_help(format!("for more information, visit {patterns_url}")) } + Res::Def(DefKind::Variant, _) if let Some(expr) = expr => { + err.span_label(span, format!("not a {expected}")); + let variant = tcx.expect_variant_res(res); + let sugg = if variant.fields.is_empty() { + " {}".to_string() + } else { + format!( + " {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }; + let descr = "you might have meant to create a new value of the struct"; + let mut suggestion = vec![]; + match tcx.parent_hir_node(expr.hir_id) { + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(..), + span: call_span, + .. + }) => { + suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg)); + } + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => { + suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); + if let hir::Node::Expr(drop_temps) = tcx.parent_hir_node(*hir_id) + && let hir::ExprKind::DropTemps(_) = drop_temps.kind + && let hir::Node::Expr(parent) = tcx.parent_hir_node(drop_temps.hir_id) + && let hir::ExprKind::If(condition, block, None) = parent.kind + && condition.hir_id == drop_temps.hir_id + && let hir::ExprKind::Block(block, _) = block.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + && let hir::ExprKind::Path(..) = expr.kind + { + // Special case: you can incorrectly write an equality condition: + // if foo == Struct { field } { /* if body */ } + // which should have been written + // if foo == (Struct { field }) { /* if body */ } + suggestion.push((block.span.shrink_to_hi(), ")".to_string())); + } else { + suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg)); + } + } + _ => { + suggestion.push((span.shrink_to_hi(), sugg)); + } + } + + err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect); + err + } _ => err.with_span_label(span, format!("not a {expected}")), } .emit() diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 425289ce3c526..1cc7cf67ee31e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1596,16 +1596,127 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggestion) = edit_distance::find_best_match_for_name( + if let Some(var_name) = edit_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::>(), item_name.name, None, - ) { - err.span_suggestion( - span, + ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name) + { + let mut suggestion = vec![(span, var_name.to_string())]; + if let SelfSource::QPath(ty) = source + && let hir::Node::Expr(ref path_expr) = self.tcx.parent_hir_node(ty.hir_id) + && let hir::ExprKind::Path(_) = path_expr.kind + && let hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Semi(ref parent), .. + }) + | hir::Node::Expr(ref parent) = self.tcx.parent_hir_node(path_expr.hir_id) + { + let replacement_span = + if let hir::ExprKind::Call(..) | hir::ExprKind::Struct(..) = parent.kind { + // We want to replace the parts that need to go, like `()` and `{}`. + span.with_hi(parent.span.hi()) + } else { + span + }; + match (variant.ctor, parent.kind) { + (None, hir::ExprKind::Struct(..)) => { + // We want a struct and we have a struct. We won't suggest changing + // the fields (at least for now). + suggestion = vec![(span, var_name.to_string())]; + } + (None, _) => { + // struct + suggestion = vec![( + replacement_span, + if variant.fields.is_empty() { + format!("{var_name} {{}}") + } else { + format!( + "{var_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + (Some((hir::def::CtorKind::Const, _)), _) => { + // unit, remove the `()`. + suggestion = vec![(replacement_span, var_name.to_string())]; + } + ( + Some((hir::def::CtorKind::Fn, def_id)), + hir::ExprKind::Call(rcvr, args), + ) => { + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + // FIXME: reuse the logic for "change args" suggestion to account for types + // involved and detect things like substitution. + match (inputs, args) { + (inputs, []) => { + // Add arguments. + suggestion.push(( + rcvr.span.shrink_to_hi().with_hi(parent.span.hi()), + format!( + "({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )); + } + (_, [arg]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg.span, + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + (_, [arg_start, .., arg_end]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg_start.span.to(arg_end.span), + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + // Argument count is the same, keep as is. + _ => {} + } + } + (Some((hir::def::CtorKind::Fn, def_id)), _) => { + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + replacement_span, + format!( + "{var_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + } + } + err.multipart_suggestion_verbose( "there is a variant with a similar name", suggestion, - Applicability::MaybeIncorrect, + Applicability::HasPlaceholders, ); } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 6d1e9ff1f9527..8afc6a48dfc5c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1023,7 +1023,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, E0533, expected); + let e = + report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0533, expected); return Ty::new_error(tcx, e); } Res::SelfCtor(def_id) => { @@ -1036,6 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let e = report_unexpected_variant_res( tcx, res, + None, qpath, pat.span, E0533, @@ -1189,7 +1191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let report_unexpected_res = |res: Res| { let expected = "tuple struct or tuple variant"; - let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, E0164, expected); + let e = report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0164, expected); on_error(e); e }; diff --git a/tests/ui/empty/empty-struct-braces-expr.stderr b/tests/ui/empty/empty-struct-braces-expr.stderr index 4604ebeaa8b98..28c701443de4e 100644 --- a/tests/ui/empty/empty-struct-braces-expr.stderr +++ b/tests/ui/empty/empty-struct-braces-expr.stderr @@ -71,12 +71,22 @@ error[E0533]: expected value, found struct variant `E::Empty3` | 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::Empty3` --> $DIR/empty-struct-braces-expr.rs:19: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[E0423]: expected function, tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-expr.rs:23:15 @@ -104,25 +114,34 @@ error[E0599]: no variant or associated item named `Empty3` found for enum `empty --> $DIR/empty-struct-braces-expr.rs:25:19 | LL | let xe3 = XE::Empty3; - | ^^^^^^ - | | - | variant or associated item not found in `XE` - | help: there is a variant with a similar name: `XEmpty3` + | ^^^^^^ variant or associated item not found in `XE` + | +help: there is a variant with a similar name + | +LL | let xe3 = XE::XEmpty3; + | ~~~~~~~ error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:26:19 | LL | let xe3 = XE::Empty3(); - | ^^^^^^ - | | - | variant or associated item not found in `XE` - | help: there is a variant with a similar name: `XEmpty3` + | ^^^^^^ variant or associated item not found in `XE` + | +help: there is a variant with a similar name + | +LL | let xe3 = XE::XEmpty3 {}; + | ~~~~~~~~~~ error[E0599]: no variant named `Empty1` found for enum `empty_struct::XE` --> $DIR/empty-struct-braces-expr.rs:28:9 | LL | XE::Empty1 {}; - | ^^^^^^ help: there is a variant with a similar name: `XEmpty3` + | ^^^^^^ + | +help: there is a variant with a similar name + | +LL | XE::XEmpty3 {}; + | ~~~~~~~~~~ error: aborting due to 9 previous errors diff --git a/tests/ui/enum/error-variant-with-turbofishes.stderr b/tests/ui/enum/error-variant-with-turbofishes.stderr index 66bed1c0d8505..ffc2862bfe02d 100644 --- a/tests/ui/enum/error-variant-with-turbofishes.stderr +++ b/tests/ui/enum/error-variant-with-turbofishes.stderr @@ -3,6 +3,11 @@ error[E0533]: expected value, found struct variant `Struct<0>::Variant` | LL | let x = Struct::<0>::Variant; | ^^^^^^^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let x = Struct::<0>::Variant { x: /* value */ }; + | ++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/expr/issue-22933-2.stderr b/tests/ui/expr/issue-22933-2.stderr index 8cda8598f3c4f..dadc31213621c 100644 --- a/tests/ui/expr/issue-22933-2.stderr +++ b/tests/ui/expr/issue-22933-2.stderr @@ -5,10 +5,12 @@ LL | enum Delicious { | -------------- variant or associated item `PIE` not found for this enum ... LL | ApplePie = Delicious::Apple as isize | Delicious::PIE as isize, - | ^^^ - | | - | variant or associated item not found in `Delicious` - | help: there is a variant with a similar name: `Pie` + | ^^^ variant or associated item not found in `Delicious` + | +help: there is a variant with a similar name + | +LL | ApplePie = Delicious::Apple as isize | Delicious::Pie as isize, + | ~~~ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-23217.stderr b/tests/ui/issues/issue-23217.stderr index 05ee0474c7838..d14da75ab72e6 100644 --- a/tests/ui/issues/issue-23217.stderr +++ b/tests/ui/issues/issue-23217.stderr @@ -4,10 +4,12 @@ error[E0599]: no variant or associated item named `A` found for enum `SomeEnum` LL | pub enum SomeEnum { | ----------------- variant or associated item `A` not found for this enum LL | B = SomeEnum::A, - | ^ - | | - | variant or associated item not found in `SomeEnum` - | help: there is a variant with a similar name: `B` + | ^ variant or associated item not found in `SomeEnum` + | +help: there is a variant with a similar name + | +LL | B = SomeEnum::B, + | ~ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-28971.stderr b/tests/ui/issues/issue-28971.stderr index 26057cbc2d1d8..7ca57d6b99817 100644 --- a/tests/ui/issues/issue-28971.stderr +++ b/tests/ui/issues/issue-28971.stderr @@ -5,10 +5,12 @@ LL | enum Foo { | -------- variant or associated item `Baz` not found for this enum ... LL | Foo::Baz(..) => (), - | ^^^ - | | - | variant or associated item not found in `Foo` - | help: there is a variant with a similar name: `Bar` + | ^^^ variant or associated item not found in `Foo` + | +help: there is a variant with a similar name + | +LL | Foo::Bar(..) => (), + | ~~~ error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable --> $DIR/issue-28971.rs:15:5 diff --git a/tests/ui/issues/issue-34209.stderr b/tests/ui/issues/issue-34209.stderr index 41bc60d03dd81..4c61d250f52d1 100644 --- a/tests/ui/issues/issue-34209.stderr +++ b/tests/ui/issues/issue-34209.stderr @@ -5,7 +5,12 @@ LL | enum S { | ------ variant `B` not found here ... LL | S::B {} => {}, - | ^ help: there is a variant with a similar name: `A` + | ^ + | +help: there is a variant with a similar name + | +LL | S::A {} => {}, + | ~ error: aborting due to 1 previous error diff --git a/tests/ui/parser/struct-literal-variant-in-if.stderr b/tests/ui/parser/struct-literal-variant-in-if.stderr index 9f0c0074d674c..15f059f145bbb 100644 --- a/tests/ui/parser/struct-literal-variant-in-if.stderr +++ b/tests/ui/parser/struct-literal-variant-in-if.stderr @@ -47,6 +47,11 @@ error[E0533]: expected value, found struct variant `E::V` | 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[E0308]: mismatched types --> $DIR/struct-literal-variant-in-if.rs:10:20 diff --git a/tests/ui/resolve/issue-18252.stderr b/tests/ui/resolve/issue-18252.stderr index 511b8da716fd4..6cb9c1f1dd211 100644 --- a/tests/ui/resolve/issue-18252.stderr +++ b/tests/ui/resolve/issue-18252.stderr @@ -3,6 +3,11 @@ error[E0533]: expected value, found struct variant `Foo::Variant` | LL | let f = Foo::Variant(42); | ^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let f = Foo::Variant { x: /* value */ }; + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/resolve/issue-19452.stderr b/tests/ui/resolve/issue-19452.stderr index eff89241fd218..aa7b752ca50b1 100644 --- a/tests/ui/resolve/issue-19452.stderr +++ b/tests/ui/resolve/issue-19452.stderr @@ -3,12 +3,22 @@ error[E0533]: expected value, found struct variant `Homura::Madoka` | LL | let homura = Homura::Madoka; | ^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let homura = Homura::Madoka { age: /* value */ }; + | ++++++++++++++++++++ error[E0533]: expected value, found struct variant `issue_19452_aux::Homura::Madoka` --> $DIR/issue-19452.rs:13:18 | LL | let homura = issue_19452_aux::Homura::Madoka; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let homura = issue_19452_aux::Homura::Madoka { age: /* value */ }; + | ++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/privacy-enum-ctor.stderr b/tests/ui/resolve/privacy-enum-ctor.stderr index ee3aecddcc342..12a6580048e17 100644 --- a/tests/ui/resolve/privacy-enum-ctor.stderr +++ b/tests/ui/resolve/privacy-enum-ctor.stderr @@ -295,6 +295,11 @@ error[E0533]: expected value, found struct variant `Z::Struct` | LL | let _: Z = Z::Struct; | ^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: Z = Z::Struct { s: /* value */ }; + | ++++++++++++++++++ error[E0618]: expected function, found enum variant `Z::Unit` --> $DIR/privacy-enum-ctor.rs:31:17 @@ -336,6 +341,11 @@ error[E0533]: expected value, found struct variant `m::E::Struct` | LL | let _: E = m::E::Struct; | ^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: E = m::E::Struct { s: /* value */ }; + | ++++++++++++++++++ error[E0618]: expected function, found enum variant `m::E::Unit` --> $DIR/privacy-enum-ctor.rs:47:16 @@ -377,6 +387,11 @@ error[E0533]: expected value, found struct variant `E::Struct` | LL | let _: E = E::Struct; | ^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: E = E::Struct { s: /* value */ }; + | ++++++++++++++++++ error[E0618]: expected function, found enum variant `E::Unit` --> $DIR/privacy-enum-ctor.rs:55:16 @@ -400,6 +415,11 @@ error[E0533]: expected value, found struct variant `m::n::Z::Struct` | LL | let _: Z = m::n::Z::Struct; | ^^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: Z = m::n::Z::Struct { s: /* value */ }; + | ++++++++++++++++++ error: aborting due to 23 previous errors diff --git a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr index 40bb87c8a4066..1af86860ce1a6 100644 --- a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -129,6 +129,11 @@ error[E0533]: expected value, found struct variant `E::B` | LL | let _: E = E::B; | ^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: E = E::B { a: /* value */ }; + | ++++++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:37:20 diff --git a/tests/ui/suggestions/incorrect-variant-literal.svg b/tests/ui/suggestions/incorrect-variant-literal.svg index 7d5353d02a747..c287bd768c483 100644 --- a/tests/ui/suggestions/incorrect-variant-literal.svg +++ b/tests/ui/suggestions/incorrect-variant-literal.svg @@ -1,4 +1,4 @@ - +