From b2480a0251465ddda9a45fc375197c6fbad4f764 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 22 May 2022 11:48:35 +0900 Subject: [PATCH 1/6] suggest `extern crate foo` when failing to resolve `use foo` fix ci error --- compiler/rustc_resolve/src/diagnostics.rs | 15 ++++++++++++--- compiler/rustc_resolve/src/imports.rs | 4 ++++ .../intra-doc/unresolved-import-recovery.stderr | 2 ++ src/test/rustdoc-ui/issue-61732.stderr | 2 ++ .../field-attributes-vis-unresolved.stderr | 4 ++++ src/test/ui/error-codes/E0432.stderr | 2 ++ .../feature-gate-extern_absolute_paths.stderr | 4 ++++ src/test/ui/imports/import3.stderr | 2 ++ src/test/ui/imports/issue-1697.stderr | 2 ++ src/test/ui/imports/issue-33464.stderr | 6 ++++++ src/test/ui/imports/issue-36881.stderr | 2 ++ src/test/ui/imports/issue-37887.stderr | 2 ++ src/test/ui/imports/issue-53269.stderr | 2 ++ src/test/ui/imports/issue-55457.stderr | 2 ++ src/test/ui/imports/tool-mod-child.stderr | 8 ++++++++ .../ui/imports/unresolved-imports-used.stderr | 8 ++++++++ .../keyword-extern-as-identifier-use.stderr | 2 ++ src/test/ui/privacy/restricted/test.stderr | 2 ++ .../ui/resolve/editions-crate-root-2015.stderr | 4 ++++ src/test/ui/resolve/extern-prelude-fail.stderr | 4 ++++ src/test/ui/resolve/issue-82865.stderr | 2 ++ src/test/ui/resolve/resolve-bad-visibility.stderr | 4 ++++ .../simd/portable-intrinsics-arent-exposed.stderr | 2 ++ .../unresolved/unresolved-asterisk-imports.stderr | 2 ++ src/test/ui/unresolved/unresolved-import.rs | 1 + src/test/ui/unresolved/unresolved-import.stderr | 12 +++++++----- 26 files changed, 94 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 95e608da012d5..75eb03c46cbc3 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1836,9 +1836,18 @@ impl<'a> Resolver<'a> { )), ) } else if self.session.edition() == Edition::Edition2015 { - (format!("maybe a missing crate `{}`?", ident), None) + ( + format!("maybe a missing crate `{ident}`?"), + Some(( + vec![], + format!( + "consider adding `extern crate {ident}` to use the `{ident}` crate" + ), + Applicability::MaybeIncorrect, + )), + ) } else { - (format!("could not find `{}` in the crate root", ident), None) + (format!("could not find `{ident}` in the crate root"), None) } } else if i > 0 { let parent = path[i - 1].ident.name; @@ -1849,7 +1858,7 @@ impl<'a> Resolver<'a> { "the list of imported crates".to_owned() } kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => format!("`{}`", parent), + _ => format!("`{parent}`"), }; let mut msg = format!("could not find `{}` in {}", ident, parent); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 3d0e2b9921d00..d8695b67d83c8 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -475,6 +475,10 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } if let Some((suggestions, msg, applicability)) = err.suggestion { + if suggestions.is_empty() { + diag.help(&msg); + continue; + } diag.multipart_suggestion(&msg, suggestions, applicability); } } diff --git a/src/test/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr b/src/test/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr index b60ab6050d79c..b54f820066600 100644 --- a/src/test/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr +++ b/src/test/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr @@ -3,6 +3,8 @@ error[E0433]: failed to resolve: maybe a missing crate `unresolved_crate`? | LL | use unresolved_crate::module::Name; | ^^^^^^^^^^^^^^^^ maybe a missing crate `unresolved_crate`? + | + = help: consider adding `extern crate unresolved_crate` to use the `unresolved_crate` crate error: Compilation failed, aborting rustdoc diff --git a/src/test/rustdoc-ui/issue-61732.stderr b/src/test/rustdoc-ui/issue-61732.stderr index 8213422491120..38fadaa44358f 100644 --- a/src/test/rustdoc-ui/issue-61732.stderr +++ b/src/test/rustdoc-ui/issue-61732.stderr @@ -3,6 +3,8 @@ error[E0433]: failed to resolve: maybe a missing crate `r#mod`? | LL | pub(in crate::r#mod) fn main() {} | ^^^^^ maybe a missing crate `r#mod`? + | + = help: consider adding `extern crate r#mod` to use the `r#mod` crate error: Compilation failed, aborting rustdoc diff --git a/src/test/ui/attributes/field-attributes-vis-unresolved.stderr b/src/test/ui/attributes/field-attributes-vis-unresolved.stderr index 41c3cea302135..439762546381e 100644 --- a/src/test/ui/attributes/field-attributes-vis-unresolved.stderr +++ b/src/test/ui/attributes/field-attributes-vis-unresolved.stderr @@ -3,12 +3,16 @@ error[E0433]: failed to resolve: maybe a missing crate `nonexistent`? | LL | pub(in nonexistent) field: u8 | ^^^^^^^^^^^ maybe a missing crate `nonexistent`? + | + = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate error[E0433]: failed to resolve: maybe a missing crate `nonexistent`? --> $DIR/field-attributes-vis-unresolved.rs:22:12 | LL | pub(in nonexistent) u8 | ^^^^^^^^^^^ maybe a missing crate `nonexistent`? + | + = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0432.stderr b/src/test/ui/error-codes/E0432.stderr index afb031c225234..ed9536f164ef0 100644 --- a/src/test/ui/error-codes/E0432.stderr +++ b/src/test/ui/error-codes/E0432.stderr @@ -3,6 +3,8 @@ error[E0432]: unresolved import `something` | LL | use something::Foo; | ^^^^^^^^^ maybe a missing crate `something`? + | + = help: consider adding `extern crate something` to use the `something` crate error: aborting due to previous error diff --git a/src/test/ui/feature-gates/feature-gate-extern_absolute_paths.stderr b/src/test/ui/feature-gates/feature-gate-extern_absolute_paths.stderr index 2f4c220ee9505..3bae23a4aaa78 100644 --- a/src/test/ui/feature-gates/feature-gate-extern_absolute_paths.stderr +++ b/src/test/ui/feature-gates/feature-gate-extern_absolute_paths.stderr @@ -3,12 +3,16 @@ error[E0432]: unresolved import `core` | LL | use core::default; | ^^^^ maybe a missing crate `core`? + | + = help: consider adding `extern crate core` to use the `core` crate error[E0433]: failed to resolve: maybe a missing crate `core`? --> $DIR/feature-gate-extern_absolute_paths.rs:4:19 | LL | let _: u8 = ::core::default::Default(); | ^^^^ maybe a missing crate `core`? + | + = help: consider adding `extern crate core` to use the `core` crate error: aborting due to 2 previous errors diff --git a/src/test/ui/imports/import3.stderr b/src/test/ui/imports/import3.stderr index 7bb413be59ff3..ca75c9c18bd28 100644 --- a/src/test/ui/imports/import3.stderr +++ b/src/test/ui/imports/import3.stderr @@ -3,6 +3,8 @@ error[E0432]: unresolved import `main` | LL | use main::bar; | ^^^^ maybe a missing crate `main`? + | + = help: consider adding `extern crate main` to use the `main` crate error: aborting due to previous error diff --git a/src/test/ui/imports/issue-1697.stderr b/src/test/ui/imports/issue-1697.stderr index a76fd30991493..019ef9ad56a1f 100644 --- a/src/test/ui/imports/issue-1697.stderr +++ b/src/test/ui/imports/issue-1697.stderr @@ -3,6 +3,8 @@ error[E0432]: unresolved import `unresolved` | LL | use unresolved::*; | ^^^^^^^^^^ maybe a missing crate `unresolved`? + | + = help: consider adding `extern crate unresolved` to use the `unresolved` crate error: aborting due to previous error diff --git a/src/test/ui/imports/issue-33464.stderr b/src/test/ui/imports/issue-33464.stderr index d3bf404c99a97..c4e5c55589914 100644 --- a/src/test/ui/imports/issue-33464.stderr +++ b/src/test/ui/imports/issue-33464.stderr @@ -3,18 +3,24 @@ error[E0432]: unresolved import `abc` | LL | use abc::one_el; | ^^^ maybe a missing crate `abc`? + | + = help: consider adding `extern crate abc` to use the `abc` crate error[E0432]: unresolved import `abc` --> $DIR/issue-33464.rs:5:5 | LL | use abc::{a, bbb, cccccc}; | ^^^ maybe a missing crate `abc`? + | + = help: consider adding `extern crate abc` to use the `abc` crate error[E0432]: unresolved import `a_very_long_name` --> $DIR/issue-33464.rs:7:5 | LL | use a_very_long_name::{el, el2}; | ^^^^^^^^^^^^^^^^ maybe a missing crate `a_very_long_name`? + | + = help: consider adding `extern crate a_very_long_name` to use the `a_very_long_name` crate error: aborting due to 3 previous errors diff --git a/src/test/ui/imports/issue-36881.stderr b/src/test/ui/imports/issue-36881.stderr index caf9d5d6d6277..2e1b468603d47 100644 --- a/src/test/ui/imports/issue-36881.stderr +++ b/src/test/ui/imports/issue-36881.stderr @@ -3,6 +3,8 @@ error[E0432]: unresolved import `issue_36881_aux` | LL | use issue_36881_aux::Foo; | ^^^^^^^^^^^^^^^ maybe a missing crate `issue_36881_aux`? + | + = help: consider adding `extern crate issue_36881_aux` to use the `issue_36881_aux` crate error: aborting due to previous error diff --git a/src/test/ui/imports/issue-37887.stderr b/src/test/ui/imports/issue-37887.stderr index 944d544098ab4..75185cad3b764 100644 --- a/src/test/ui/imports/issue-37887.stderr +++ b/src/test/ui/imports/issue-37887.stderr @@ -3,6 +3,8 @@ error[E0432]: unresolved import `libc` | LL | use libc::*; | ^^^^ maybe a missing crate `libc`? + | + = help: consider adding `extern crate libc` to use the `libc` crate error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? --> $DIR/issue-37887.rs:2:5 diff --git a/src/test/ui/imports/issue-53269.stderr b/src/test/ui/imports/issue-53269.stderr index a0e7bf8b61f6e..29c7556dac432 100644 --- a/src/test/ui/imports/issue-53269.stderr +++ b/src/test/ui/imports/issue-53269.stderr @@ -3,6 +3,8 @@ error[E0432]: unresolved import `nonexistent_module` | LL | use nonexistent_module::mac; | ^^^^^^^^^^^^^^^^^^ maybe a missing crate `nonexistent_module`? + | + = help: consider adding `extern crate nonexistent_module` to use the `nonexistent_module` crate error[E0659]: `mac` is ambiguous --> $DIR/issue-53269.rs:8:5 diff --git a/src/test/ui/imports/issue-55457.stderr b/src/test/ui/imports/issue-55457.stderr index 07de3d95902ef..788fcc830ae9a 100644 --- a/src/test/ui/imports/issue-55457.stderr +++ b/src/test/ui/imports/issue-55457.stderr @@ -12,6 +12,8 @@ error[E0432]: unresolved import `non_existent` | LL | use non_existent::non_existent; | ^^^^^^^^^^^^ maybe a missing crate `non_existent`? + | + = help: consider adding `extern crate non_existent` to use the `non_existent` crate error: cannot determine resolution for the derive macro `NonExistent` --> $DIR/issue-55457.rs:5:10 diff --git a/src/test/ui/imports/tool-mod-child.stderr b/src/test/ui/imports/tool-mod-child.stderr index efab4f6a74f83..6caf15bc72401 100644 --- a/src/test/ui/imports/tool-mod-child.stderr +++ b/src/test/ui/imports/tool-mod-child.stderr @@ -3,24 +3,32 @@ error[E0433]: failed to resolve: maybe a missing crate `clippy`? | LL | use clippy::a::b; | ^^^^^^ maybe a missing crate `clippy`? + | + = help: consider adding `extern crate clippy` to use the `clippy` crate error[E0432]: unresolved import `clippy` --> $DIR/tool-mod-child.rs:1:5 | LL | use clippy::a; | ^^^^^^ maybe a missing crate `clippy`? + | + = help: consider adding `extern crate clippy` to use the `clippy` crate error[E0433]: failed to resolve: maybe a missing crate `rustdoc`? --> $DIR/tool-mod-child.rs:5:5 | LL | use rustdoc::a::b; | ^^^^^^^ maybe a missing crate `rustdoc`? + | + = help: consider adding `extern crate rustdoc` to use the `rustdoc` crate error[E0432]: unresolved import `rustdoc` --> $DIR/tool-mod-child.rs:4:5 | LL | use rustdoc::a; | ^^^^^^^ maybe a missing crate `rustdoc`? + | + = help: consider adding `extern crate rustdoc` to use the `rustdoc` crate error: aborting due to 4 previous errors diff --git a/src/test/ui/imports/unresolved-imports-used.stderr b/src/test/ui/imports/unresolved-imports-used.stderr index ddf3608933909..73f9d1bfb6c61 100644 --- a/src/test/ui/imports/unresolved-imports-used.stderr +++ b/src/test/ui/imports/unresolved-imports-used.stderr @@ -15,24 +15,32 @@ error[E0432]: unresolved import `foo` | LL | use foo::bar; | ^^^ maybe a missing crate `foo`? + | + = help: consider adding `extern crate foo` to use the `foo` crate error[E0432]: unresolved import `baz` --> $DIR/unresolved-imports-used.rs:12:5 | LL | use baz::*; | ^^^ maybe a missing crate `baz`? + | + = help: consider adding `extern crate baz` to use the `baz` crate error[E0432]: unresolved import `foo2` --> $DIR/unresolved-imports-used.rs:14:5 | LL | use foo2::bar2; | ^^^^ maybe a missing crate `foo2`? + | + = help: consider adding `extern crate foo2` to use the `foo2` crate error[E0432]: unresolved import `baz2` --> $DIR/unresolved-imports-used.rs:15:5 | LL | use baz2::*; | ^^^^ maybe a missing crate `baz2`? + | + = help: consider adding `extern crate baz2` to use the `baz2` crate error[E0603]: function `quz` is private --> $DIR/unresolved-imports-used.rs:9:10 diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr index 247d6b0ed7163..54ee45c28679a 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr @@ -14,6 +14,8 @@ error[E0432]: unresolved import `r#extern` | LL | use extern::foo; | ^^^^^^ maybe a missing crate `r#extern`? + | + = help: consider adding `extern crate r#extern` to use the `r#extern` crate error: aborting due to 2 previous errors diff --git a/src/test/ui/privacy/restricted/test.stderr b/src/test/ui/privacy/restricted/test.stderr index 0f6003c424767..00e00dd943dcd 100644 --- a/src/test/ui/privacy/restricted/test.stderr +++ b/src/test/ui/privacy/restricted/test.stderr @@ -3,6 +3,8 @@ error[E0433]: failed to resolve: maybe a missing crate `bad`? | LL | pub(in bad::path) mod m1 {} | ^^^ maybe a missing crate `bad`? + | + = help: consider adding `extern crate bad` to use the `bad` crate error[E0742]: visibilities can only be restricted to ancestor modules --> $DIR/test.rs:51:12 diff --git a/src/test/ui/resolve/editions-crate-root-2015.stderr b/src/test/ui/resolve/editions-crate-root-2015.stderr index f8d65fec3d12d..00cdd0c58f4ec 100644 --- a/src/test/ui/resolve/editions-crate-root-2015.stderr +++ b/src/test/ui/resolve/editions-crate-root-2015.stderr @@ -3,12 +3,16 @@ error[E0433]: failed to resolve: maybe a missing crate `nonexistant`? | LL | fn global_inner(_: ::nonexistant::Foo) { | ^^^^^^^^^^^ maybe a missing crate `nonexistant`? + | + = help: consider adding `extern crate nonexistant` to use the `nonexistant` crate error[E0433]: failed to resolve: maybe a missing crate `nonexistant`? --> $DIR/editions-crate-root-2015.rs:7:30 | LL | fn crate_inner(_: crate::nonexistant::Foo) { | ^^^^^^^^^^^ maybe a missing crate `nonexistant`? + | + = help: consider adding `extern crate nonexistant` to use the `nonexistant` crate error[E0412]: cannot find type `nonexistant` in the crate root --> $DIR/editions-crate-root-2015.rs:11:25 diff --git a/src/test/ui/resolve/extern-prelude-fail.stderr b/src/test/ui/resolve/extern-prelude-fail.stderr index a59f4c952bb08..a1591914b4d29 100644 --- a/src/test/ui/resolve/extern-prelude-fail.stderr +++ b/src/test/ui/resolve/extern-prelude-fail.stderr @@ -3,12 +3,16 @@ error[E0432]: unresolved import `extern_prelude` | LL | use extern_prelude::S; | ^^^^^^^^^^^^^^ maybe a missing crate `extern_prelude`? + | + = help: consider adding `extern crate extern_prelude` to use the `extern_prelude` crate error[E0433]: failed to resolve: maybe a missing crate `extern_prelude`? --> $DIR/extern-prelude-fail.rs:8:15 | LL | let s = ::extern_prelude::S; | ^^^^^^^^^^^^^^ maybe a missing crate `extern_prelude`? + | + = help: consider adding `extern crate extern_prelude` to use the `extern_prelude` crate error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/issue-82865.stderr b/src/test/ui/resolve/issue-82865.stderr index 7898c2a360f12..730fd6d602645 100644 --- a/src/test/ui/resolve/issue-82865.stderr +++ b/src/test/ui/resolve/issue-82865.stderr @@ -3,6 +3,8 @@ error[E0433]: failed to resolve: maybe a missing crate `x`? | LL | use x::y::z; | ^ maybe a missing crate `x`? + | + = help: consider adding `extern crate x` to use the `x` crate error[E0599]: no function or associated item named `z` found for struct `Box<_, _>` in the current scope --> $DIR/issue-82865.rs:8:10 diff --git a/src/test/ui/resolve/resolve-bad-visibility.stderr b/src/test/ui/resolve/resolve-bad-visibility.stderr index 197ecf0cb0039..2ac41b87562e1 100644 --- a/src/test/ui/resolve/resolve-bad-visibility.stderr +++ b/src/test/ui/resolve/resolve-bad-visibility.stderr @@ -21,12 +21,16 @@ error[E0433]: failed to resolve: maybe a missing crate `nonexistent`? | LL | pub(in nonexistent) struct G; | ^^^^^^^^^^^ maybe a missing crate `nonexistent`? + | + = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate error[E0433]: failed to resolve: maybe a missing crate `too_soon`? --> $DIR/resolve-bad-visibility.rs:8:8 | LL | pub(in too_soon) struct H; | ^^^^^^^^ maybe a missing crate `too_soon`? + | + = help: consider adding `extern crate too_soon` to use the `too_soon` crate error: aborting due to 5 previous errors diff --git a/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr b/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr index f568aa0429525..870f4064de49b 100644 --- a/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr +++ b/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr @@ -3,6 +3,8 @@ error[E0433]: failed to resolve: maybe a missing crate `core`? | LL | use core::simd::intrinsics; | ^^^^ maybe a missing crate `core`? + | + = help: consider adding `extern crate core` to use the `core` crate error[E0432]: unresolved import `std::simd::intrinsics` --> $DIR/portable-intrinsics-arent-exposed.rs:5:5 diff --git a/src/test/ui/unresolved/unresolved-asterisk-imports.stderr b/src/test/ui/unresolved/unresolved-asterisk-imports.stderr index a789179db65bd..8df8eab34a715 100644 --- a/src/test/ui/unresolved/unresolved-asterisk-imports.stderr +++ b/src/test/ui/unresolved/unresolved-asterisk-imports.stderr @@ -3,6 +3,8 @@ error[E0432]: unresolved import `not_existing_crate` | LL | use not_existing_crate::*; | ^^^^^^^^^^^^^^^^^^ maybe a missing crate `not_existing_crate`? + | + = help: consider adding `extern crate not_existing_crate` to use the `not_existing_crate` crate error: aborting due to previous error diff --git a/src/test/ui/unresolved/unresolved-import.rs b/src/test/ui/unresolved/unresolved-import.rs index b65c3dfb90b42..4125c593c747f 100644 --- a/src/test/ui/unresolved/unresolved-import.rs +++ b/src/test/ui/unresolved/unresolved-import.rs @@ -1,5 +1,6 @@ use foo::bar; //~ ERROR unresolved import `foo` [E0432] //~^ maybe a missing crate `foo`? + //~| HELP consider adding `extern crate foo` to use the `foo` crate use bar::Baz as x; //~ ERROR unresolved import `bar::Baz` [E0432] //~| no `Baz` in `bar` diff --git a/src/test/ui/unresolved/unresolved-import.stderr b/src/test/ui/unresolved/unresolved-import.stderr index d4bfea57809b0..0dd928c8b6ffd 100644 --- a/src/test/ui/unresolved/unresolved-import.stderr +++ b/src/test/ui/unresolved/unresolved-import.stderr @@ -3,9 +3,11 @@ error[E0432]: unresolved import `foo` | LL | use foo::bar; | ^^^ maybe a missing crate `foo`? + | + = help: consider adding `extern crate foo` to use the `foo` crate error[E0432]: unresolved import `bar::Baz` - --> $DIR/unresolved-import.rs:4:5 + --> $DIR/unresolved-import.rs:5:5 | LL | use bar::Baz as x; | ^^^^^---^^^^^ @@ -14,7 +16,7 @@ LL | use bar::Baz as x; | no `Baz` in `bar` error[E0432]: unresolved import `food::baz` - --> $DIR/unresolved-import.rs:9:5 + --> $DIR/unresolved-import.rs:10:5 | LL | use food::baz; | ^^^^^^--- @@ -23,7 +25,7 @@ LL | use food::baz; | no `baz` in `food` error[E0432]: unresolved import `food::beens` - --> $DIR/unresolved-import.rs:14:12 + --> $DIR/unresolved-import.rs:15:12 | LL | use food::{beens as Foo}; | -----^^^^^^^ @@ -32,13 +34,13 @@ LL | use food::{beens as Foo}; | help: a similar name exists in the module: `beans` error[E0432]: unresolved import `MyEnum` - --> $DIR/unresolved-import.rs:38:9 + --> $DIR/unresolved-import.rs:39:9 | LL | use MyEnum::*; | ^^^^^^ help: a similar path exists: `self::MyEnum` error[E0432]: unresolved import `Enum` - --> $DIR/unresolved-import.rs:48:9 + --> $DIR/unresolved-import.rs:49:9 | LL | use Enum::*; | ^^^^ help: a similar path exists: `self::Enum` From aeb765b299640ee0674d2c01df600c296af7d691 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 26 Apr 2022 19:59:50 -0700 Subject: [PATCH 2/6] better enum suggestions --- .../rustc_typeck/src/check/method/suggest.rs | 215 +++++++++++++----- .../ui/suggestions/enum-method-probe.fixed | 42 ++++ src/test/ui/suggestions/enum-method-probe.rs | 42 ++++ .../ui/suggestions/enum-method-probe.stderr | 67 ++++++ 4 files changed, 314 insertions(+), 52 deletions(-) create mode 100644 src/test/ui/suggestions/enum-method-probe.fixed create mode 100644 src/test/ui/suggestions/enum-method-probe.rs create mode 100644 src/test/ui/suggestions/enum-method-probe.stderr diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 4e54d554c6ec2..b5fd3eb44628c 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -978,45 +978,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - if let SelfSource::MethodCall(expr) = source - && let Some((fields, substs)) = self.get_field_candidates(span, actual) - { - let call_expr = - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - for candidate_field in fields.iter() { - if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - &|_, field_ty| { - self.lookup_probe( - span, - item_name, - field_ty, - call_expr, - ProbeScope::AllTraits, - ) - .is_ok() - }, - candidate_field, - substs, - vec![], - self.tcx.parent_module(expr.hir_id).to_def_id(), - ) { - let field_path_str = field_path - .iter() - .map(|id| id.name.to_ident_string()) - .collect::>() - .join("."); - debug!("field_path_str: {:?}", field_path_str); + self.check_for_field_method(&mut err, source, span, actual, item_name); - err.span_suggestion_verbose( - item_name.span.shrink_to_lo(), - "one of the expressions' fields has a method of the same name", - format!("{field_path_str}."), - Applicability::MaybeIncorrect, - ); - } - } - } + self.check_for_unwrap_self(&mut err, source, span, actual, item_name); bound_spans.sort(); bound_spans.dedup(); @@ -1343,6 +1307,157 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + fn check_for_field_method( + &self, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + source: SelfSource<'tcx>, + span: Span, + actual: Ty<'tcx>, + item_name: Ident, + ) { + if let SelfSource::MethodCall(expr) = source + && let Some((fields, substs)) = self.get_field_candidates(span, actual) + { + let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); + for candidate_field in fields.iter() { + if let Some(field_path) = self.check_for_nested_field_satisfying( + span, + &|_, field_ty| { + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::AllTraits, + ) + .is_ok() + }, + candidate_field, + substs, + vec![], + self.tcx.parent_module(expr.hir_id).to_def_id(), + ) { + let field_path_str = field_path + .iter() + .map(|id| id.name.to_ident_string()) + .collect::>() + .join("."); + debug!("field_path_str: {:?}", field_path_str); + + err.span_suggestion_verbose( + item_name.span.shrink_to_lo(), + "one of the expressions' fields has a method of the same name", + format!("{field_path_str}."), + Applicability::MaybeIncorrect, + ); + } + } + } + } + + fn check_for_unwrap_self( + &self, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + source: SelfSource<'tcx>, + span: Span, + actual: Ty<'tcx>, + item_name: Ident, + ) { + let tcx = self.tcx; + let SelfSource::MethodCall(expr) = source else { return; }; + let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + + let ty::Adt(kind, substs) = actual.kind() else { return; }; + if !kind.is_enum() { + return; + } + + let matching_variants: Vec<_> = kind + .variants() + .iter() + .filter_map(|variant| { + let [field] = &variant.fields[..] else { return None; }; + let field_ty = field.ty(tcx, substs); + + // Skip `_`, since that'll just lead to ambiguity. + if matches!(self.resolve_vars_if_possible(field_ty).kind(), ty::Infer(_)) { + return None; + } + + if let Ok(pick) = + self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) + { + Some((variant, field, pick)) + } else { + None + } + }) + .collect(); + + let ret_ty_matches = |diagnostic_item| { + if let Some(ret_ty) = self + .ret_coercion + .as_ref() + .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) + && let ty::Adt(kind, _) = ret_ty.kind() + && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) + { + true + } else { + false + } + }; + + match &matching_variants[..] { + [(_, field, pick)] if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) => { + let self_ty = field.ty(tcx, substs); + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{self_ty}`"), + ); + if ret_ty_matches(sym::Result) { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!("use the `?` operator to extract the `{self_ty}` value, propagating a `Result::Err` value to the caller"), + "?".to_owned(), + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!("consider using `Result::expect` to unwrap the `{self_ty}` value, panicking if the value is an `Err`"), + ".expect(\"REASON\")".to_owned(), + Applicability::HasPlaceholders, + ); + } + } + [(_, field, pick)] if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) => { + let self_ty = field.ty(tcx, substs); + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{self_ty}`"), + ); + if ret_ty_matches(sym::Option) { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!("use the `?` operator to extract the `{self_ty}` value, propagating a `None` to the caller"), + "?".to_owned(), + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!("consider using `Option::expect` to unwrap the `{self_ty}` value, panicking if the value is `None`"), + ".expect(\"REASON\")".to_owned(), + Applicability::HasPlaceholders, + ); + } + } + // FIXME(compiler-errors): Support suggestions for other matching enum variants + _ => {} + } + } + pub(crate) fn note_unmet_impls_on_type( &self, err: &mut Diagnostic, @@ -1662,13 +1777,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { - match self.lookup_probe( - span, - item_name, - *rcvr_ty, - rcvr, - crate::check::method::probe::ProbeScope::AllTraits, - ) { + match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { Ok(pick) => { // If the method is defined for the receiver we have, it likely wasn't `use`d. // We point at the method, but we just skip the rest of the check for arbitrary @@ -1700,13 +1809,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"), (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), ] { - if let Some(new_rcvr_t) = *rcvr_ty && let Ok(pick) = self.lookup_probe( - span, - item_name, - new_rcvr_t, - rcvr, - crate::check::method::probe::ProbeScope::AllTraits, - ) { + if let Some(new_rcvr_t) = *rcvr_ty + && let Ok(pick) = self.lookup_probe( + span, + item_name, + new_rcvr_t, + rcvr, + ProbeScope::AllTraits, + ) + { debug!("try_alt_rcvr: pick candidate {:?}", pick); let did = Some(pick.item.container.id()); // We don't want to suggest a container type when the missing diff --git a/src/test/ui/suggestions/enum-method-probe.fixed b/src/test/ui/suggestions/enum-method-probe.fixed new file mode 100644 index 0000000000000..990f7900f2232 --- /dev/null +++ b/src/test/ui/suggestions/enum-method-probe.fixed @@ -0,0 +1,42 @@ +// run-rustfix +#![allow(unused)] + +struct Foo; + +impl Foo { + fn get(&self) -> u8 { + 42 + } +} + +fn test_result_in_result() -> Result<(), ()> { + let res: Result<_, ()> = Ok(Foo); + res?.get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP use the `?` operator + Ok(()) +} + +fn test_result_in_plain() { + let res: Result<_, ()> = Ok(Foo); + res.expect("REASON").get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is an `Err` +} + +fn test_option_in_option() -> Option<()> { + let res: Option<_> = Some(Foo); + res?.get(); + //~^ ERROR no method named `get` found for enum `Option` in the current scope + //~| HELP use the `?` operator + Some(()) +} + +fn test_option_in_plain() { + let res: Option<_> = Some(Foo); + res.expect("REASON").get(); + //~^ ERROR no method named `get` found for enum `Option` in the current scope + //~| HELP consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is `None` +} + +fn main() {} diff --git a/src/test/ui/suggestions/enum-method-probe.rs b/src/test/ui/suggestions/enum-method-probe.rs new file mode 100644 index 0000000000000..6270fa9fea598 --- /dev/null +++ b/src/test/ui/suggestions/enum-method-probe.rs @@ -0,0 +1,42 @@ +// run-rustfix +#![allow(unused)] + +struct Foo; + +impl Foo { + fn get(&self) -> u8 { + 42 + } +} + +fn test_result_in_result() -> Result<(), ()> { + let res: Result<_, ()> = Ok(Foo); + res.get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP use the `?` operator + Ok(()) +} + +fn test_result_in_plain() { + let res: Result<_, ()> = Ok(Foo); + res.get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is an `Err` +} + +fn test_option_in_option() -> Option<()> { + let res: Option<_> = Some(Foo); + res.get(); + //~^ ERROR no method named `get` found for enum `Option` in the current scope + //~| HELP use the `?` operator + Some(()) +} + +fn test_option_in_plain() { + let res: Option<_> = Some(Foo); + res.get(); + //~^ ERROR no method named `get` found for enum `Option` in the current scope + //~| HELP consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is `None` +} + +fn main() {} diff --git a/src/test/ui/suggestions/enum-method-probe.stderr b/src/test/ui/suggestions/enum-method-probe.stderr new file mode 100644 index 0000000000000..4af1775f66bf9 --- /dev/null +++ b/src/test/ui/suggestions/enum-method-probe.stderr @@ -0,0 +1,67 @@ +error[E0599]: no method named `get` found for enum `Result` in the current scope + --> $DIR/enum-method-probe.rs:14:9 + | +LL | res.get(); + | ^^^ method not found in `Result` + | +note: the method `get` exists on the type `Foo` + --> $DIR/enum-method-probe.rs:7:5 + | +LL | fn get(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^ +help: use the `?` operator to extract the `Foo` value, propagating a `Result::Err` value to the caller + | +LL | res?.get(); + | + + +error[E0599]: no method named `get` found for enum `Result` in the current scope + --> $DIR/enum-method-probe.rs:22:9 + | +LL | res.get(); + | ^^^ method not found in `Result` + | +note: the method `get` exists on the type `Foo` + --> $DIR/enum-method-probe.rs:7:5 + | +LL | fn get(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^ +help: consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is an `Err` + | +LL | res.expect("REASON").get(); + | +++++++++++++++++ + +error[E0599]: no method named `get` found for enum `Option` in the current scope + --> $DIR/enum-method-probe.rs:29:9 + | +LL | res.get(); + | ^^^ method not found in `Option` + | +note: the method `get` exists on the type `Foo` + --> $DIR/enum-method-probe.rs:7:5 + | +LL | fn get(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^ +help: use the `?` operator to extract the `Foo` value, propagating a `None` to the caller + | +LL | res?.get(); + | + + +error[E0599]: no method named `get` found for enum `Option` in the current scope + --> $DIR/enum-method-probe.rs:37:9 + | +LL | res.get(); + | ^^^ method not found in `Option` + | +note: the method `get` exists on the type `Foo` + --> $DIR/enum-method-probe.rs:7:5 + | +LL | fn get(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^ +help: consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is `None` + | +LL | res.expect("REASON").get(); + | +++++++++++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0599`. From 2a61f0cc45c809c62dd149477cb2672c2022c3a4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 May 2022 14:18:03 -0700 Subject: [PATCH 3/6] address comments --- .../rustc_typeck/src/check/method/suggest.rs | 58 ++++++++----------- .../ui/suggestions/enum-method-probe.fixed | 25 ++++++-- src/test/ui/suggestions/enum-method-probe.rs | 25 ++++++-- .../ui/suggestions/enum-method-probe.stderr | 56 ++++++++++++++---- 4 files changed, 109 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index b5fd3eb44628c..0e198907c8d50 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -1375,22 +1375,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let matching_variants: Vec<_> = kind .variants() .iter() - .filter_map(|variant| { + .flat_map(|variant| { let [field] = &variant.fields[..] else { return None; }; let field_ty = field.ty(tcx, substs); // Skip `_`, since that'll just lead to ambiguity. - if matches!(self.resolve_vars_if_possible(field_ty).kind(), ty::Infer(_)) { + if self.resolve_vars_if_possible(field_ty).is_ty_var() { return None; } - if let Ok(pick) = - self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) - { - Some((variant, field, pick)) - } else { - None - } + self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) + .ok() + .map(|pick| (variant, field, pick)) }) .collect(); @@ -1409,45 +1405,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; match &matching_variants[..] { - [(_, field, pick)] if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) => { + [(_, field, pick)] => { let self_ty = field.ty(tcx, substs); err.span_note( tcx.def_span(pick.item.def_id), &format!("the method `{item_name}` exists on the type `{self_ty}`"), ); - if ret_ty_matches(sym::Result) { - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!("use the `?` operator to extract the `{self_ty}` value, propagating a `Result::Err` value to the caller"), - "?".to_owned(), - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!("consider using `Result::expect` to unwrap the `{self_ty}` value, panicking if the value is an `Err`"), - ".expect(\"REASON\")".to_owned(), - Applicability::HasPlaceholders, - ); - } - } - [(_, field, pick)] if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) => { - let self_ty = field.ty(tcx, substs); - err.span_note( - tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{self_ty}`"), - ); - if ret_ty_matches(sym::Option) { + let (article, kind, variant, question) = + if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) { + ("a", "Result", "Err", ret_ty_matches(sym::Result)) + } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) { + ("an", "Option", "None", ret_ty_matches(sym::Option)) + } else { + return; + }; + if question { err.span_suggestion_verbose( expr.span.shrink_to_hi(), - format!("use the `?` operator to extract the `{self_ty}` value, propagating a `None` to the caller"), + format!( + "use the `?` operator to extract the `{self_ty}` value, propagating \ + {article} `{kind}::{variant}` value to the caller" + ), "?".to_owned(), Applicability::MachineApplicable, ); } else { err.span_suggestion_verbose( expr.span.shrink_to_hi(), - format!("consider using `Option::expect` to unwrap the `{self_ty}` value, panicking if the value is `None`"), + format!( + "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ + panicking if the value is {article} `{kind}::{variant}`" + ), ".expect(\"REASON\")".to_owned(), Applicability::HasPlaceholders, ); diff --git a/src/test/ui/suggestions/enum-method-probe.fixed b/src/test/ui/suggestions/enum-method-probe.fixed index 990f7900f2232..6499c92bc6f15 100644 --- a/src/test/ui/suggestions/enum-method-probe.fixed +++ b/src/test/ui/suggestions/enum-method-probe.fixed @@ -1,4 +1,6 @@ +// compile-flags: --edition=2021 // run-rustfix + #![allow(unused)] struct Foo; @@ -17,11 +19,26 @@ fn test_result_in_result() -> Result<(), ()> { Ok(()) } -fn test_result_in_plain() { +async fn async_test_result_in_result() -> Result<(), ()> { + let res: Result<_, ()> = Ok(Foo); + res?.get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP use the `?` operator + Ok(()) +} + +fn test_result_in_unit_return() { + let res: Result<_, ()> = Ok(Foo); + res.expect("REASON").get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is a `Result::Err` +} + +async fn async_test_result_in_unit_return() { let res: Result<_, ()> = Ok(Foo); res.expect("REASON").get(); //~^ ERROR no method named `get` found for enum `Result` in the current scope - //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is an `Err` + //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is a `Result::Err` } fn test_option_in_option() -> Option<()> { @@ -32,11 +49,11 @@ fn test_option_in_option() -> Option<()> { Some(()) } -fn test_option_in_plain() { +fn test_option_in_unit_return() { let res: Option<_> = Some(Foo); res.expect("REASON").get(); //~^ ERROR no method named `get` found for enum `Option` in the current scope - //~| HELP consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is `None` + //~| HELP consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is an `Option::None` } fn main() {} diff --git a/src/test/ui/suggestions/enum-method-probe.rs b/src/test/ui/suggestions/enum-method-probe.rs index 6270fa9fea598..18ea8ed8a58ff 100644 --- a/src/test/ui/suggestions/enum-method-probe.rs +++ b/src/test/ui/suggestions/enum-method-probe.rs @@ -1,4 +1,6 @@ +// compile-flags: --edition=2021 // run-rustfix + #![allow(unused)] struct Foo; @@ -17,11 +19,26 @@ fn test_result_in_result() -> Result<(), ()> { Ok(()) } -fn test_result_in_plain() { +async fn async_test_result_in_result() -> Result<(), ()> { + let res: Result<_, ()> = Ok(Foo); + res.get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP use the `?` operator + Ok(()) +} + +fn test_result_in_unit_return() { + let res: Result<_, ()> = Ok(Foo); + res.get(); + //~^ ERROR no method named `get` found for enum `Result` in the current scope + //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is a `Result::Err` +} + +async fn async_test_result_in_unit_return() { let res: Result<_, ()> = Ok(Foo); res.get(); //~^ ERROR no method named `get` found for enum `Result` in the current scope - //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is an `Err` + //~| HELP consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is a `Result::Err` } fn test_option_in_option() -> Option<()> { @@ -32,11 +49,11 @@ fn test_option_in_option() -> Option<()> { Some(()) } -fn test_option_in_plain() { +fn test_option_in_unit_return() { let res: Option<_> = Some(Foo); res.get(); //~^ ERROR no method named `get` found for enum `Option` in the current scope - //~| HELP consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is `None` + //~| HELP consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is an `Option::None` } fn main() {} diff --git a/src/test/ui/suggestions/enum-method-probe.stderr b/src/test/ui/suggestions/enum-method-probe.stderr index 4af1775f66bf9..6ed14984f4747 100644 --- a/src/test/ui/suggestions/enum-method-probe.stderr +++ b/src/test/ui/suggestions/enum-method-probe.stderr @@ -1,11 +1,11 @@ error[E0599]: no method named `get` found for enum `Result` in the current scope - --> $DIR/enum-method-probe.rs:14:9 + --> $DIR/enum-method-probe.rs:24:9 | LL | res.get(); | ^^^ method not found in `Result` | note: the method `get` exists on the type `Foo` - --> $DIR/enum-method-probe.rs:7:5 + --> $DIR/enum-method-probe.rs:9:5 | LL | fn get(&self) -> u8 { | ^^^^^^^^^^^^^^^^^^^ @@ -15,53 +15,85 @@ LL | res?.get(); | + error[E0599]: no method named `get` found for enum `Result` in the current scope - --> $DIR/enum-method-probe.rs:22:9 + --> $DIR/enum-method-probe.rs:39:9 | LL | res.get(); | ^^^ method not found in `Result` | note: the method `get` exists on the type `Foo` - --> $DIR/enum-method-probe.rs:7:5 + --> $DIR/enum-method-probe.rs:9:5 | LL | fn get(&self) -> u8 { | ^^^^^^^^^^^^^^^^^^^ -help: consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is an `Err` +help: consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is a `Result::Err` + | +LL | res.expect("REASON").get(); + | +++++++++++++++++ + +error[E0599]: no method named `get` found for enum `Result` in the current scope + --> $DIR/enum-method-probe.rs:16:9 + | +LL | res.get(); + | ^^^ method not found in `Result` + | +note: the method `get` exists on the type `Foo` + --> $DIR/enum-method-probe.rs:9:5 + | +LL | fn get(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^ +help: use the `?` operator to extract the `Foo` value, propagating a `Result::Err` value to the caller + | +LL | res?.get(); + | + + +error[E0599]: no method named `get` found for enum `Result` in the current scope + --> $DIR/enum-method-probe.rs:32:9 + | +LL | res.get(); + | ^^^ method not found in `Result` + | +note: the method `get` exists on the type `Foo` + --> $DIR/enum-method-probe.rs:9:5 + | +LL | fn get(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^ +help: consider using `Result::expect` to unwrap the `Foo` value, panicking if the value is a `Result::Err` | LL | res.expect("REASON").get(); | +++++++++++++++++ error[E0599]: no method named `get` found for enum `Option` in the current scope - --> $DIR/enum-method-probe.rs:29:9 + --> $DIR/enum-method-probe.rs:46:9 | LL | res.get(); | ^^^ method not found in `Option` | note: the method `get` exists on the type `Foo` - --> $DIR/enum-method-probe.rs:7:5 + --> $DIR/enum-method-probe.rs:9:5 | LL | fn get(&self) -> u8 { | ^^^^^^^^^^^^^^^^^^^ -help: use the `?` operator to extract the `Foo` value, propagating a `None` to the caller +help: use the `?` operator to extract the `Foo` value, propagating an `Option::None` value to the caller | LL | res?.get(); | + error[E0599]: no method named `get` found for enum `Option` in the current scope - --> $DIR/enum-method-probe.rs:37:9 + --> $DIR/enum-method-probe.rs:54:9 | LL | res.get(); | ^^^ method not found in `Option` | note: the method `get` exists on the type `Foo` - --> $DIR/enum-method-probe.rs:7:5 + --> $DIR/enum-method-probe.rs:9:5 | LL | fn get(&self) -> u8 { | ^^^^^^^^^^^^^^^^^^^ -help: consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is `None` +help: consider using `Option::expect` to unwrap the `Foo` value, panicking if the value is an `Option::None` | LL | res.expect("REASON").get(); | +++++++++++++++++ -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0599`. From 59d35d6e90bef49719292884b8f769cf9b111467 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 31 May 2022 11:12:02 -0700 Subject: [PATCH 4/6] rustdoc: also index impl trait --- src/librustdoc/clean/types.rs | 4 ++ src/librustdoc/html/render/search_index.rs | 30 ++++++++++--- src/test/rustdoc-js/impl-trait.js | 51 ++++++++++++++++++++++ src/test/rustdoc-js/impl-trait.rs | 21 +++++++++ 4 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/test/rustdoc-js/impl-trait.js create mode 100644 src/test/rustdoc-js/impl-trait.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1e434458dcef5..4605793d0df94 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1667,6 +1667,10 @@ impl Type { matches!(self, Type::Generic(_)) } + pub(crate) fn is_impl_trait(&self) -> bool { + matches!(self, Type::ImplTrait(_)) + } + pub(crate) fn is_primitive(&self) -> bool { self.primitive_type().is_some() } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 25c70f0808c6d..5ff9cd15a0ab0 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -226,7 +226,7 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option { Some(path.segments.last().unwrap().name) } // We return an empty name because we don't care about the generic name itself. - clean::Generic(_) => Some(kw::Empty), + clean::Generic(_) | clean::ImplTrait(_) => Some(kw::Empty), clean::Primitive(ref p) => Some(p.as_sym()), clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_), clean::BareFunction(_) @@ -235,8 +235,7 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option { | clean::Array(_, _) | clean::RawPointer(_, _) | clean::QPath { .. } - | clean::Infer - | clean::ImplTrait(_) => None, + | clean::Infer => None, } } @@ -264,10 +263,12 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>( mut generics: Vec, cache: &Cache, ) { - let is_full_generic = ty.is_full_generic(); + // generics and impl trait are both identified by their generics, + // rather than a type name itself + let anonymous = ty.is_full_generic() || ty.is_impl_trait(); let generics_empty = generics.is_empty(); - if is_full_generic { + if anonymous { if generics_empty { // This is a type parameter with no trait bounds (for example: `T` in // `fn f(p: T)`, so not useful for the rustdoc search because we would end up @@ -318,7 +319,7 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>( if index_ty.name.as_ref().map(|s| s.is_empty() && generics_empty).unwrap_or(true) { return; } - if is_full_generic { + if anonymous { // We remove the name of the full generic because we have no use for it. index_ty.name = Some(String::new()); res.push(TypeWithKind::from((index_ty, ItemType::Generic))); @@ -398,6 +399,23 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>( } insert_ty(res, tcx, arg.clone(), ty_generics, cache); } + } else if let Type::ImplTrait(ref bounds) = *arg { + let mut ty_generics = Vec::new(); + for bound in bounds { + if let Some(path) = bound.get_trait_path() { + let ty = Type::Path { path }; + add_generics_and_bounds_as_types( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut ty_generics, + cache, + ); + } + } + insert_ty(res, tcx, arg.clone(), ty_generics, cache); } else { // This is not a type parameter. So for example if we have `T, U: Option`, and we're // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't. diff --git a/src/test/rustdoc-js/impl-trait.js b/src/test/rustdoc-js/impl-trait.js new file mode 100644 index 0000000000000..8d594bf8aea75 --- /dev/null +++ b/src/test/rustdoc-js/impl-trait.js @@ -0,0 +1,51 @@ +// ignore-order + +const QUERY = [ + 'Aaaaaaa -> i32', + 'Aaaaaaa -> Aaaaaaa', + 'Aaaaaaa -> usize', + '-> Aaaaaaa', + 'Aaaaaaa', +]; + +const EXPECTED = [ + { + // Aaaaaaa -> i32 + 'others': [ + { 'path': 'impl_trait::Ccccccc', 'name': 'eeeeeee' }, + ], + }, + { + // Aaaaaaa -> Aaaaaaa + 'others': [ + { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, + ], + }, + { + // Aaaaaaa -> usize + 'others': [], + }, + { + // -> Aaaaaaa + 'others': [ + { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, + { 'path': 'impl_trait::Ccccccc', 'name': 'ddddddd' }, + { 'path': 'impl_trait', 'name': 'bbbbbbb' }, + ], + }, + { + // Aaaaaaa + 'others': [ + { 'path': 'impl_trait', 'name': 'Aaaaaaa' }, + ], + 'in_args': [ + { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, + { 'path': 'impl_trait::Ccccccc', 'name': 'eeeeeee' }, + ], + 'returned': [ + { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, + { 'path': 'impl_trait::Ccccccc', 'name': 'ddddddd' }, + { 'path': 'impl_trait', 'name': 'bbbbbbb' }, + ], + }, +]; diff --git a/src/test/rustdoc-js/impl-trait.rs b/src/test/rustdoc-js/impl-trait.rs new file mode 100644 index 0000000000000..fb8869b46f3d4 --- /dev/null +++ b/src/test/rustdoc-js/impl-trait.rs @@ -0,0 +1,21 @@ +pub trait Aaaaaaa {} + +impl Aaaaaaa for () {} + +pub fn bbbbbbb() -> impl Aaaaaaa { + () +} + +pub struct Ccccccc {} + +impl Ccccccc { + pub fn ddddddd(&self) -> impl Aaaaaaa { + () + } + pub fn eeeeeee(&self, _x: impl Aaaaaaa) -> i32 { + 0 + } + pub fn fffffff(&self, x: impl Aaaaaaa) -> impl Aaaaaaa { + x + } +} From 14d8baf365280b82f79dddfbc1a65d2692258acd Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 31 May 2022 11:18:51 -0700 Subject: [PATCH 5/6] rustdoc: also index raw pointers Co-authored-by: Noah Lev --- src/librustdoc/html/render/search_index.rs | 5 +- src/test/rustdoc-js/raw-pointer.js | 55 ++++++++++++++++++++++ src/test/rustdoc-js/raw-pointer.rs | 24 ++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/test/rustdoc-js/raw-pointer.js create mode 100644 src/test/rustdoc-js/raw-pointer.rs diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 5ff9cd15a0ab0..9f302cc256659 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -228,12 +228,13 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option { // We return an empty name because we don't care about the generic name itself. clean::Generic(_) | clean::ImplTrait(_) => Some(kw::Empty), clean::Primitive(ref p) => Some(p.as_sym()), - clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_), + clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => { + get_index_type_name(type_) + } clean::BareFunction(_) | clean::Tuple(_) | clean::Slice(_) | clean::Array(_, _) - | clean::RawPointer(_, _) | clean::QPath { .. } | clean::Infer => None, } diff --git a/src/test/rustdoc-js/raw-pointer.js b/src/test/rustdoc-js/raw-pointer.js new file mode 100644 index 0000000000000..140b955ea713a --- /dev/null +++ b/src/test/rustdoc-js/raw-pointer.js @@ -0,0 +1,55 @@ +// ignore-order + +const QUERY = [ + 'Aaaaaaa -> i32', + 'Aaaaaaa -> Aaaaaaa', + 'Aaaaaaa -> usize', + '-> Aaaaaaa', + 'Aaaaaaa', +]; + +const EXPECTED = [ + { + // Aaaaaaa -> i32 + 'others': [ + { 'path': 'raw_pointer::Ccccccc', 'name': 'eeeeeee' }, + ], + }, + { + // Aaaaaaa -> Aaaaaaa + 'others': [ + { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, + { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, + ], + }, + { + // Aaaaaaa -> usize + 'others': [], + }, + { + // -> Aaaaaaa + 'others': [ + { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, + { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, + { 'path': 'raw_pointer::Ccccccc', 'name': 'ddddddd' }, + { 'path': 'raw_pointer', 'name': 'bbbbbbb' }, + ], + }, + { + // Aaaaaaa + 'others': [ + { 'path': 'raw_pointer', 'name': 'Aaaaaaa' }, + ], + 'in_args': [ + { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, + { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, + { 'path': 'raw_pointer::Ccccccc', 'name': 'eeeeeee' }, + ], + 'returned': [ + { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, + { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, + { 'path': 'raw_pointer::Ccccccc', 'name': 'ddddddd' }, + { 'path': 'raw_pointer', 'name': 'bbbbbbb' }, + ], + }, +]; diff --git a/src/test/rustdoc-js/raw-pointer.rs b/src/test/rustdoc-js/raw-pointer.rs new file mode 100644 index 0000000000000..b8ace2e0b7d77 --- /dev/null +++ b/src/test/rustdoc-js/raw-pointer.rs @@ -0,0 +1,24 @@ +use std::ptr; + +pub struct Aaaaaaa {} + +pub fn bbbbbbb() -> *const Aaaaaaa { + ptr::null() +} + +pub struct Ccccccc {} + +impl Ccccccc { + pub fn ddddddd(&self) -> *const Aaaaaaa { + ptr::null() + } + pub fn eeeeeee(&self, _x: *const Aaaaaaa) -> i32 { + 0 + } + pub fn fffffff(&self, x: *const Aaaaaaa) -> *const Aaaaaaa { + x + } + pub fn ggggggg(&self, x: *mut Aaaaaaa) -> *mut Aaaaaaa { + x + } +} From 9f2b69d344bf78426957af44a4c24c7434e1bb4d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 1 Jun 2022 09:26:26 -0400 Subject: [PATCH 6/6] update Miri --- Cargo.lock | 2 ++ src/tools/miri | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 3610d60376781..7ed327e9f4ccb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5509,6 +5509,8 @@ dependencies = [ "pretty_assertions 1.2.1", "regex", "rustc_version", + "serde", + "serde_json", ] [[package]] diff --git a/src/tools/miri b/src/tools/miri index 065ff89e33b67..749efd29565a9 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 065ff89e33b67b3527fcdd56cf8b432e593e32d4 +Subproject commit 749efd29565a9b8f47afb441aaacfcc10bc145d7