From a85700a1a8f5aa729bd19e5a2a78f8a80d623093 Mon Sep 17 00:00:00 2001 From: matt rice Date: Fri, 23 Feb 2024 16:05:28 -0800 Subject: [PATCH 01/20] Handle .init_array link_section specially on wasm --- compiler/rustc_codegen_llvm/src/consts.rs | 10 ++++++++-- compiler/rustc_hir_analysis/src/check/mod.rs | 20 +++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index ec2fb2c6e54..55477ff5d29 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -481,8 +481,14 @@ fn codegen_static(&self, def_id: DefId, is_mutable: bool) { } // Wasm statics with custom link sections get special treatment as they - // go into custom sections of the wasm executable. - if self.tcx.sess.target.is_like_wasm { + // go into custom sections of the wasm executable. The exception to this + // is the `.init_array` section which are treated specially by the wasm linker. + if self.tcx.sess.target.is_like_wasm + && attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap_or(true) + { if let Some(section) = attrs.link_section { let section = llvm::LLVMMDStringInContext2( self.llcx, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 36a92a4cf7c..16a1a41eb19 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -159,21 +159,27 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { return; } - // For the wasm32 target statics with `#[link_section]` are placed into custom - // sections of the final output file, but this isn't link custom sections of - // other executable formats. Namely we can only embed a list of bytes, - // nothing with provenance (pointers to anything else). If any provenance - // show up, reject it here. + // For the wasm32 target statics with `#[link_section]` other than `.init_array` + // are placed into custom sections of the final output file, but this isn't link + // custom sections of other executable formats. Namely we can only embed a list + // of bytes, nothing with provenance (pointers to anything else). If any + // provenance show up, reject it here. // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) && alloc.inner().provenance().ptrs().len() != 0 { - let msg = "statics with a custom `#[link_section]` must be a \ + if attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap_or(true) + { + let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ extra levels of indirection such as references"; - tcx.dcx().span_err(tcx.def_span(id), msg); + tcx.dcx().span_err(tcx.def_span(id), msg); + } } } From 1de046fa24a8a0304be274963b43819cfe56013a Mon Sep 17 00:00:00 2001 From: matt rice Date: Fri, 14 Jun 2024 07:53:37 -0700 Subject: [PATCH 02/20] Update for review --- compiler/rustc_hir_analysis/src/check/mod.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 16a1a41eb19..9ce60bffea6 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -160,20 +160,35 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { } // For the wasm32 target statics with `#[link_section]` other than `.init_array` - // are placed into custom sections of the final output file, but this isn't link + // are placed into custom sections of the final output file, but this isn't like // custom sections of other executable formats. Namely we can only embed a list // of bytes, nothing with provenance (pointers to anything else). If any // provenance show up, reject it here. // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. + // + // The `.init_array` section is left to go through the normal custom section code path. + // When dealing with `.init_array` wasm-ld currently has several limitations. This manifests + // in workarounds in user-code. + // + // * The linker fails to merge multiple items in a crate into the .init_array section. + // To work around this, a single array can be used placing multiple items in the array. + // #[link_section = ".init_array"] + // static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor]; + // * Even symbols marked used get gc'd from dependant crates unless at least one symbol + // in the crate is marked with an `#[export_name]` + // + // Once `.init_array` support in wasm-ld is complete, the user code workarounds should + // continue to work, but would no longer be necessary. + if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) && alloc.inner().provenance().ptrs().len() != 0 { if attrs .link_section .map(|link_section| !link_section.as_str().starts_with(".init_array")) - .unwrap_or(true) + .unwrap() { let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ From 06dcdbb2eea7eef79eff3f8cd1419c4e05a0e642 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 16 Jul 2024 14:59:56 -0400 Subject: [PATCH 03/20] rewrite macos-fat-archive to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/macos-fat-archive/Makefile | 10 ---------- tests/run-make/macos-fat-archive/rmake.rs | 20 +++++++++++++++++++ 3 files changed, 20 insertions(+), 11 deletions(-) delete mode 100644 tests/run-make/macos-fat-archive/Makefile create mode 100644 tests/run-make/macos-fat-archive/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index f33cde110a5..d562a5f4d1b 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -72,7 +72,6 @@ run-make/lto-linkage-used-attr/Makefile run-make/lto-no-link-whole-rlib/Makefile run-make/lto-smoke-c/Makefile run-make/macos-deployment-target/Makefile -run-make/macos-fat-archive/Makefile run-make/manual-link/Makefile run-make/min-global-align/Makefile run-make/missing-crate-dependency/Makefile diff --git a/tests/run-make/macos-fat-archive/Makefile b/tests/run-make/macos-fat-archive/Makefile deleted file mode 100644 index 0feb39a23cb..00000000000 --- a/tests/run-make/macos-fat-archive/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# only-apple - -include ../tools.mk - -"$(TMPDIR)"/libnative-library.a: native-library.c - $(CC) -arch arm64 -arch x86_64 native-library.c -c -o "$(TMPDIR)"/native-library.o - $(AR) crs "$(TMPDIR)"/libnative-library.a "$(TMPDIR)"/native-library.o - -all: "$(TMPDIR)"/libnative-library.a - $(RUSTC) lib.rs --crate-type=lib -L "native=$(TMPDIR)" -l static=native-library diff --git a/tests/run-make/macos-fat-archive/rmake.rs b/tests/run-make/macos-fat-archive/rmake.rs new file mode 100644 index 00000000000..c9f0fa07693 --- /dev/null +++ b/tests/run-make/macos-fat-archive/rmake.rs @@ -0,0 +1,20 @@ +// macOS (and iOS) has a concept of universal (fat) binaries which contain code for multiple CPU +// architectures in the same file. Apple is migrating from x86_64 to aarch64 CPUs, +// so for the next few years it will be important for macOS developers to +// build "fat" binaries (executables and cdylibs). + +// Rustc used to be unable to handle these special libraries, which was fixed in #98736. If +// compilation in this test is successful, the native fat library was successfully linked to. +// See https://github.com/rust-lang/rust/issues/55235 + +//@ only-apple + +use run_make_support::{cc, llvm_ar, rustc}; + +fn main() { + cc().args(&["-arch", "arm64", "-arch", "x86_64", "native-library.c", "-c"]) + .out_exe("native-library.o") + .run(); + llvm_ar().obj_to_ar().output_input("libnative-library.a", "native-library.o").run(); + rustc().input("lib.rs").crate_type("lib").arg("-lstatic=native-library").run(); +} From aeca91e08df5c1cd5ee5ce858c24e6d02ff1ca69 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 16 Jul 2024 15:07:10 -0400 Subject: [PATCH 04/20] rewrite manual-link to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/manual-link/Makefile | 7 ------- tests/run-make/manual-link/rmake.rs | 16 ++++++++++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 tests/run-make/manual-link/Makefile create mode 100644 tests/run-make/manual-link/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index d562a5f4d1b..c7d460659a1 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -72,7 +72,6 @@ run-make/lto-linkage-used-attr/Makefile run-make/lto-no-link-whole-rlib/Makefile run-make/lto-smoke-c/Makefile run-make/macos-deployment-target/Makefile -run-make/manual-link/Makefile run-make/min-global-align/Makefile run-make/missing-crate-dependency/Makefile run-make/native-link-modifier-bundle/Makefile diff --git a/tests/run-make/manual-link/Makefile b/tests/run-make/manual-link/Makefile deleted file mode 100644 index 8dbf0460fff..00000000000 --- a/tests/run-make/manual-link/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: $(TMPDIR)/libbar.a - $(RUSTC) foo.rs -lstatic=bar - $(RUSTC) main.rs - $(call RUN,main) diff --git a/tests/run-make/manual-link/rmake.rs b/tests/run-make/manual-link/rmake.rs new file mode 100644 index 00000000000..1d362172263 --- /dev/null +++ b/tests/run-make/manual-link/rmake.rs @@ -0,0 +1,16 @@ +// A smoke test for the `-l` command line rustc flag, which manually links to the selected +// library. Useful for native libraries, this is roughly equivalent to `#[link]` in Rust code. +// If compilation succeeds, the flag successfully linked the native library. +// See https://github.com/rust-lang/rust/pull/18470 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib, run, rustc}; + +fn main() { + build_native_static_lib("bar"); + rustc().input("foo.rs").arg("-lstatic=bar").run(); + rustc().input("main.rs").run(); + run("main"); +} From 47c21012efc27b8a008ae11276ea95d9f818fcc9 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Tue, 16 Jul 2024 15:30:41 -0400 Subject: [PATCH 05/20] rewrite archive-duplicate-names to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - .../run-make/archive-duplicate-names/Makefile | 16 -------- .../run-make/archive-duplicate-names/rmake.rs | 37 +++++++++++++++++++ 3 files changed, 37 insertions(+), 17 deletions(-) delete mode 100644 tests/run-make/archive-duplicate-names/Makefile create mode 100644 tests/run-make/archive-duplicate-names/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index c7d460659a1..c7675386fa5 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,4 +1,3 @@ -run-make/archive-duplicate-names/Makefile run-make/branch-protection-check-IBT/Makefile run-make/c-dynamic-dylib/Makefile run-make/c-dynamic-rlib/Makefile diff --git a/tests/run-make/archive-duplicate-names/Makefile b/tests/run-make/archive-duplicate-names/Makefile deleted file mode 100644 index 207eee39299..00000000000 --- a/tests/run-make/archive-duplicate-names/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# When two object archives with the same filename are present, an iterator is supposed to inspect each object, recognize the duplication and extract each one to a different directory. -# This test checks that this duplicate handling behaviour has not been broken. -# See https://github.com/rust-lang/rust/pull/24439 - -# ignore-cross-compile -include ../tools.mk - -all: - mkdir $(TMPDIR)/a - mkdir $(TMPDIR)/b - $(call COMPILE_OBJ,$(TMPDIR)/a/foo.o,foo.c) - $(call COMPILE_OBJ,$(TMPDIR)/b/foo.o,bar.c) - $(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o - $(RUSTC) foo.rs - $(RUSTC) bar.rs - $(call RUN,bar) diff --git a/tests/run-make/archive-duplicate-names/rmake.rs b/tests/run-make/archive-duplicate-names/rmake.rs new file mode 100644 index 00000000000..62a35566199 --- /dev/null +++ b/tests/run-make/archive-duplicate-names/rmake.rs @@ -0,0 +1,37 @@ +// When two object archives with the same filename are present, an iterator is supposed to +// inspect each object, recognize the duplication and extract each one to a different directory. +// This test checks that this duplicate handling behaviour has not been broken. +// See https://github.com/rust-lang/rust/pull/24439 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{cc, is_msvc, llvm_ar, rfs, run, rustc}; + +fn main() { + rfs::create_dir("a"); + rfs::create_dir("b"); + compile_obj_force_foo("a", "foo"); + compile_obj_force_foo("b", "bar"); + let mut ar = llvm_ar(); + ar.obj_to_ar().arg("libfoo.a"); + if is_msvc() { + ar.arg("a/foo.obj").arg("b/foo.obj").run(); + } else { + ar.arg("a/foo.o").arg("b/foo.o").run(); + } + rustc().input("foo.rs").run(); + rustc().input("bar.rs").run(); + run("bar"); +} + +#[track_caller] +pub fn compile_obj_force_foo(dir: &str, lib_name: &str) { + let obj_file = if is_msvc() { format!("{dir}/foo") } else { format!("{dir}/foo.o") }; + let src = format!("{lib_name}.c"); + if is_msvc() { + cc().arg("-c").out_exe(&obj_file).input(src).run(); + } else { + cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run(); + }; +} From 741cf9167849d39cc31edf27a11c7f77da437445 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Thu, 18 Jul 2024 13:42:15 -0400 Subject: [PATCH 06/20] rewrite lto-smoke-c to rmake --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/lto-smoke-c/Makefile | 12 ----------- tests/run-make/lto-smoke-c/rmake.rs | 20 +++++++++++++++++++ 3 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 tests/run-make/lto-smoke-c/Makefile create mode 100644 tests/run-make/lto-smoke-c/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 745f00c4f52..8b847771e7f 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -62,7 +62,6 @@ run-make/long-linker-command-lines-cmd-exe/Makefile run-make/long-linker-command-lines/Makefile run-make/lto-linkage-used-attr/Makefile run-make/lto-no-link-whole-rlib/Makefile -run-make/lto-smoke-c/Makefile run-make/macos-deployment-target/Makefile run-make/macos-fat-archive/Makefile run-make/manual-link/Makefile diff --git a/tests/run-make/lto-smoke-c/Makefile b/tests/run-make/lto-smoke-c/Makefile deleted file mode 100644 index f1ba3d95da2..00000000000 --- a/tests/run-make/lto-smoke-c/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Apparently older versions of GCC segfault if -g is passed... -CC := $(CC:-g=) - -all: - $(RUSTC) foo.rs -C lto - $(CC) bar.c $(call STATICLIB,foo) \ - $(call OUT_EXE,bar) \ - $(EXTRACFLAGS) $(EXTRACXXFLAGS) - $(call RUN,bar) diff --git a/tests/run-make/lto-smoke-c/rmake.rs b/tests/run-make/lto-smoke-c/rmake.rs new file mode 100644 index 00000000000..66e19ae7482 --- /dev/null +++ b/tests/run-make/lto-smoke-c/rmake.rs @@ -0,0 +1,20 @@ +// LLVM's link-time-optimization (LTO) is a useful feature added to Rust in response +// to #10741. This test uses this feature with `-C lto` alongside a native C library, +// and checks that compilation and execution is successful. +// See https://github.com/rust-lang/rust/issues/10741 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name}; + +fn main() { + rustc().input("foo.rs").arg("-Clto").run(); + cc().input("bar.c") + .arg(static_lib_name("foo")) + .out_exe("bar") + .args(&extra_c_flags()) + .args(&extra_cxx_flags()) + .run(); + run("bar"); +} 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 07/20] 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 00000000000..aac2cc54904 --- /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 00000000000..7d5353d02a7 --- /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 08/20] 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 a665306f2c6..44a46951459 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 @@ pub fn lower_assoc_path( ); 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 @@ pub fn lower_assoc_path( .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 035a3429ed7..dc677595f05 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -519,7 +519,15 @@ pub(crate) fn check_expr_path( 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 bdbdcee6446..2c793664509 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ 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 @@ fn opt_find_breakable(&mut self, target_id: HirId) -> Option<&mut BreakableCtxt< 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 425289ce3c5..1cc7cf67ee3 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1596,16 +1596,127 @@ fn report_no_match_method_error( // 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 6d1e9ff1f95..8afc6a48dfc 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1023,7 +1023,8 @@ fn check_pat_path( } 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 @@ fn check_pat_path( let e = report_unexpected_variant_res( tcx, res, + None, qpath, pat.span, E0533, @@ -1189,7 +1191,7 @@ fn check_pat_tuple_struct( }; 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 4604ebeaa8b..28c701443de 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 66bed1c0d85..ffc2862bfe0 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 8cda8598f3c..dadc3121362 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 05ee0474c78..d14da75ab72 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 26057cbc2d1..7ca57d6b998 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 41bc60d03dd..4c61d250f52 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 9f0c0074d67..15f059f145b 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 511b8da716f..6cb9c1f1dd2 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 eff89241fd2..aa7b752ca50 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 ee3aecddcc3..12a6580048e 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 40bb87c8a40..1af86860ce1 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 7d5353d02a7..c287bd768c4 100644 --- a/tests/ui/suggestions/incorrect-variant-literal.svg +++ b/tests/ui/suggestions/incorrect-variant-literal.svg @@ -1,4 +1,4 @@ - +