From 3a20cbf9fc1e3049da8dcc64e572dfb4ff32dd8e Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 30 Jan 2023 15:55:47 -0700 Subject: [PATCH 01/11] rustdoc: remove inconsistently-present sidebar tooltips Discussed in https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/Inconsistent.20sidebar.20tooltips/near/323565625 --- src/librustdoc/html/render/context.rs | 9 +++------ src/librustdoc/html/render/mod.rs | 3 --- src/librustdoc/html/static/js/main.js | 6 +----- tests/rustdoc/markdown-summaries.rs | 27 --------------------------- 4 files changed, 4 insertions(+), 41 deletions(-) delete mode 100644 tests/rustdoc/markdown-summaries.rs diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index b59645ec2e2d5..1216a8d71c8cf 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -18,7 +18,7 @@ use super::search_index::build_index; use super::write_shared::write_shared; use super::{ collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes, - LinkFromSrc, NameDoc, StylePath, + LinkFromSrc, StylePath, }; use crate::clean::{self, types::ExternalLocation, ExternalCrate}; @@ -256,7 +256,7 @@ impl<'tcx> Context<'tcx> { } /// Construct a map of items shown in the sidebar to a plain-text summary of their docs. - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { + fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { // BTreeMap instead of HashMap to get a sorted output let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); let mut inserted: FxHashMap> = FxHashMap::default(); @@ -274,10 +274,7 @@ impl<'tcx> Context<'tcx> { if inserted.entry(short).or_default().insert(myname) { let short = short.to_string(); let myname = myname.to_string(); - map.entry(short).or_default().push(( - myname, - Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))), - )); + map.entry(short).or_default().push(myname); } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 816a8f4e274ce..deebab1f0e80e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -83,9 +83,6 @@ use crate::scrape_examples::{CallData, CallLocation}; use crate::try_none; use crate::DOC_RUST_LANG_ORG_CHANNEL; -/// A pair of name and its optional document. -pub(crate) type NameDoc = (String, Option); - pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { crate::html::format::display_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index b9ad8ef70e917..b5cbc484601ca 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -456,10 +456,7 @@ function loadCss(cssUrl) { const ul = document.createElement("ul"); ul.className = "block " + shortty; - for (const item of filtered) { - const name = item[0]; - const desc = item[1]; // can be null - + for (const name of filtered) { let path; if (shortty === "mod") { path = name + "/index.html"; @@ -469,7 +466,6 @@ function loadCss(cssUrl) { const current_page = document.location.href.split("/").pop(); const link = document.createElement("a"); link.href = path; - link.title = desc; if (path === current_page) { link.className = "current"; } diff --git a/tests/rustdoc/markdown-summaries.rs b/tests/rustdoc/markdown-summaries.rs deleted file mode 100644 index 31e7072b5ce9b..0000000000000 --- a/tests/rustdoc/markdown-summaries.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![crate_type = "lib"] -#![crate_name = "summaries"] - -//! This *summary* has a [link] and `code`. -//! -//! This is the second paragraph. -//! -//! [link]: https://example.com - -// @hasraw search-index.js 'This summary has a link and code.' -// @!hasraw - 'second paragraph' - -/// This `code` will be rendered in a code tag. -/// -/// This text should not be rendered. -pub struct Sidebar; - -// @hasraw search-index.js 'This code will be rendered in a code tag.' -// @hasraw summaries/sidebar-items.js 'This `code` will be rendered in a code tag.' -// @!hasraw - 'text should not be rendered' - -/// ```text -/// this block should not be rendered -/// ``` -pub struct Sidebar2; - -// @!hasraw summaries/sidebar-items.js 'block should not be rendered' From e79b3c2bed20d1f819d109527e1db30eb464a7f4 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 6 Feb 2023 15:01:40 -0500 Subject: [PATCH 02/11] Rename atomic 'as_mut_ptr' to 'as_ptr' to match Cell (ref #66893) --- library/core/src/sync/atomic.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 1d14efc7523b4..00bcaf3e18c3d 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -922,13 +922,13 @@ impl AtomicBool { /// /// let mut atomic = AtomicBool::new(true); /// unsafe { - /// my_atomic_op(atomic.as_mut_ptr()); + /// my_atomic_op(atomic.as_ptr()); /// } /// # } /// ``` #[inline] #[unstable(feature = "atomic_mut_ptr", reason = "recently added", issue = "66893")] - pub const fn as_mut_ptr(&self) -> *mut bool { + pub const fn as_ptr(&self) -> *mut bool { self.v.get().cast() } @@ -1814,12 +1814,12 @@ impl AtomicPtr { /// /// // SAFETY: Safe as long as `my_atomic_op` is atomic. /// unsafe { - /// my_atomic_op(atomic.as_mut_ptr()); + /// my_atomic_op(atomic.as_ptr()); /// } /// ``` #[inline] #[unstable(feature = "atomic_mut_ptr", reason = "recently added", issue = "66893")] - pub const fn as_mut_ptr(&self) -> *mut *mut T { + pub const fn as_ptr(&self) -> *mut *mut T { self.p.get() } } @@ -2719,7 +2719,7 @@ macro_rules! atomic_int { /// /// // SAFETY: Safe as long as `my_atomic_op` is atomic. /// unsafe { - /// my_atomic_op(atomic.as_mut_ptr()); + /// my_atomic_op(atomic.as_ptr()); /// } /// # } /// ``` @@ -2727,7 +2727,7 @@ macro_rules! atomic_int { #[unstable(feature = "atomic_mut_ptr", reason = "recently added", issue = "66893")] - pub const fn as_mut_ptr(&self) -> *mut $int_type { + pub const fn as_ptr(&self) -> *mut $int_type { self.v.get() } } From 1af9b4f347c1ca725e43444e0068b5e520dde7e2 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Thu, 9 Feb 2023 23:56:20 -0800 Subject: [PATCH 03/11] Clarify `new_size` for realloc means bytes --- library/core/src/alloc/global.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index 1d80b8bf9ec76..18da70451f299 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -203,7 +203,7 @@ pub unsafe trait GlobalAlloc { ptr } - /// Shrink or grow a block of memory to the given `new_size`. + /// Shrink or grow a block of memory to the given `new_size` in bytes. /// The block is described by the given `ptr` pointer and `layout`. /// /// If this returns a non-null pointer, then ownership of the memory block @@ -211,10 +211,11 @@ pub unsafe trait GlobalAlloc { /// Any access to the old `ptr` is Undefined Behavior, even if the /// allocation remained in-place. The newly returned pointer is the only valid pointer /// for accessing this memory now. + /// /// The new memory block is allocated with `layout`, - /// but with the `size` updated to `new_size`. This new layout must be - /// used when deallocating the new memory block with `dealloc`. The range - /// `0..min(layout.size(), new_size)` of the new memory block is + /// but with the `size` updated to `new_size` in bytes. + /// This new layout must be used when deallocating the new memory block with `dealloc`. + /// The range `0..min(layout.size(), new_size)` of the new memory block is /// guaranteed to have the same values as the original block. /// /// If this method returns null, then ownership of the memory From a03da2bdeb7c7b466b7b4a9b361d1d19c171a589 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 9 Feb 2023 20:45:01 +0100 Subject: [PATCH 04/11] Add test for implementation on projection --- .../ui/const-generics/wrong-normalization.rs | 19 +++++++++++++++++++ .../const-generics/wrong-normalization.stderr | 11 +++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/ui/const-generics/wrong-normalization.rs create mode 100644 tests/ui/const-generics/wrong-normalization.stderr diff --git a/tests/ui/const-generics/wrong-normalization.rs b/tests/ui/const-generics/wrong-normalization.rs new file mode 100644 index 0000000000000..f1ce317b3f78b --- /dev/null +++ b/tests/ui/const-generics/wrong-normalization.rs @@ -0,0 +1,19 @@ +// This test ensures that if implementation on projections is supported, +// it doesn't end in very weird cycle error. + +#![crate_type = "lib"] + +pub trait Identity { + type Identity: ?Sized; +} + +impl Identity for T { + type Identity = Self; +} + +pub struct I8; + +impl as Identity>::Identity { +//~^ ERROR no nominal type found for inherent implementation + pub fn foo(&self) {} +} diff --git a/tests/ui/const-generics/wrong-normalization.stderr b/tests/ui/const-generics/wrong-normalization.stderr new file mode 100644 index 0000000000000..fb806bdb1e747 --- /dev/null +++ b/tests/ui/const-generics/wrong-normalization.stderr @@ -0,0 +1,11 @@ +error[E0118]: no nominal type found for inherent implementation + --> $DIR/wrong-normalization.rs:16:6 + | +LL | impl as Identity>::Identity { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl requires a nominal type + | + = note: either implement a trait on it or create a newtype to wrap it instead + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0118`. From 3a72238aa6c78d071f4b10d60c76550ccea6493f Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 10 Feb 2023 14:58:49 +0100 Subject: [PATCH 05/11] revert #107074 --- compiler/rustc_const_eval/src/transform/validate.rs | 11 ++++++++++- tests/ui/impl-trait/nested-return-type2.rs | 3 +++ tests/ui/impl-trait/nested-return-type2.stderr | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 76b316cdf0c3f..56c60d59d2858 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::{ RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, }; -use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; @@ -231,6 +231,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return true; } + // We sometimes have to use `defining_opaque_types` for subtyping + // to succeed here and figuring out how exactly that should work + // is annoying. It is harmless enough to just not validate anything + // in that case. We still check this after analysis as all opque + // types have been revealed at this point. + if (src, dest).has_opaque_types() { + return true; + } + crate::util::is_subtype(self.tcx, self.param_env, src, dest) } } diff --git a/tests/ui/impl-trait/nested-return-type2.rs b/tests/ui/impl-trait/nested-return-type2.rs index cc1f1f4ec44c8..fe883ce6fc8ed 100644 --- a/tests/ui/impl-trait/nested-return-type2.rs +++ b/tests/ui/impl-trait/nested-return-type2.rs @@ -1,4 +1,7 @@ // check-pass +// compile-flags: -Zvalidate-mir + +// Using -Zvalidate-mir as a regression test for #107346. trait Duh {} diff --git a/tests/ui/impl-trait/nested-return-type2.stderr b/tests/ui/impl-trait/nested-return-type2.stderr index 3aed05ca13298..09ad3bd05c1b3 100644 --- a/tests/ui/impl-trait/nested-return-type2.stderr +++ b/tests/ui/impl-trait/nested-return-type2.stderr @@ -1,5 +1,5 @@ warning: opaque type `impl Trait` does not satisfy its associated type bounds - --> $DIR/nested-return-type2.rs:25:24 + --> $DIR/nested-return-type2.rs:28:24 | LL | type Assoc: Duh; | --- this associated type bound is unsatisfied for `impl Send` From 5201bb53bbdc24a35cb9aac5797187f677decd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Fri, 10 Feb 2023 18:08:25 +0100 Subject: [PATCH 06/11] remove redundant clones --- compiler/rustc_codegen_ssa/src/back/archive.rs | 2 +- compiler/rustc_log/src/lib.rs | 2 +- compiler/rustc_parse_format/src/lib.rs | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index d3cd085cfb668..66ec8f5f57d21 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -203,7 +203,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { } } - self.src_archives.push((archive_path.to_owned(), archive_map)); + self.src_archives.push((archive_path, archive_map)); Ok(()) } diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 019fdc30dcec5..12d6281daad15 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -98,7 +98,7 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> { let fmt_layer = tracing_subscriber::fmt::layer() .with_writer(io::stderr) .without_time() - .event_format(BacktraceFormatter { backtrace_target: str.to_string() }); + .event_format(BacktraceFormatter { backtrace_target: str }); let subscriber = subscriber.with(fmt_layer); tracing::subscriber::set_global_default(subscriber).unwrap(); } diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 088a87ca57104..34a4fd02ea691 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -847,9 +847,7 @@ impl<'a> Parser<'a> { 0, ParseError { description: "expected format parameter to occur after `:`".to_owned(), - note: Some( - format!("`?` comes after `:`, try `{}:{}` instead", word, "?").to_owned(), - ), + note: Some(format!("`?` comes after `:`, try `{}:{}` instead", word, "?")), label: "expected `?` to occur after `:`".to_owned(), span: pos.to(pos), secondary_label: None, From c003c01a03e972d43444862c275836ac12f20a98 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Feb 2023 18:32:18 +0100 Subject: [PATCH 07/11] Correctly handle reexports for macros --- src/librustdoc/visit_ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 088cb3f339492..9c1e5f4a3cddb 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -378,7 +378,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let nonexported = !tcx.has_attr(def_id, sym::macro_export); if is_macro_2_0 || nonexported || self.inlining { - self.add_to_current_mod(item, renamed, None); + self.add_to_current_mod(item, renamed, import_id); } } hir::ItemKind::Mod(ref m) => { From ddb31de281d2c1edf2d09b8eb0b6f8e53a13b846 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Feb 2023 18:32:33 +0100 Subject: [PATCH 08/11] Also get current import attributes --- src/librustdoc/clean/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 80493b100bb45..02e6b0edc720c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2206,10 +2206,12 @@ fn clean_maybe_renamed_item<'tcx>( }; let mut extra_attrs = Vec::new(); - if let Some(hir::Node::Item(use_node)) = - import_id.and_then(|def_id| cx.tcx.hir().find_by_def_id(def_id)) + if let Some(import_id) = import_id && + let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id) { - // We get all the various imports' attributes. + // First, we add the attributes from the current import. + extra_attrs.extend_from_slice(inline::load_attrs(cx, import_id.to_def_id())); + // Then we get all the various imports' attributes. get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs); } From 295fd0d8352ca4cff048b29064cfafbf5f29592c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Feb 2023 18:37:32 +0100 Subject: [PATCH 09/11] Add regression test for reexported macros docs --- tests/rustdoc/reexport-macro.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/rustdoc/reexport-macro.rs diff --git a/tests/rustdoc/reexport-macro.rs b/tests/rustdoc/reexport-macro.rs new file mode 100644 index 0000000000000..c4dec703aed3b --- /dev/null +++ b/tests/rustdoc/reexport-macro.rs @@ -0,0 +1,23 @@ +// Ensure that macros are correctly reexported and that they get both the comment from the +// `pub use` and from the macro. + +#![crate_name = "foo"] + +// @has 'foo/macro.foo.html' +// @!has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'x y' +// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'y' +#[macro_use] +mod my_module { + /// y + #[macro_export] + macro_rules! foo { + () => (); + } +} + +// @has 'foo/another_mod/macro.bar.html' +// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'x y' +pub mod another_mod { + /// x + pub use crate::foo as bar; +} From 9790d6fbdd07d1efbec8088c88dc2b0d1f9ba283 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 Feb 2023 18:57:02 +0000 Subject: [PATCH 10/11] Add a couple random projection tests --- .../param-candidate-doesnt-shadow-project.rs | 25 ++++++++++++++++ ...ojection-param-candidates-are-ambiguous.rs | 30 +++++++++++++++++++ ...tion-param-candidates-are-ambiguous.stderr | 16 ++++++++++ 3 files changed, 71 insertions(+) create mode 100644 tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs create mode 100644 tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs create mode 100644 tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr diff --git a/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs new file mode 100644 index 0000000000000..bdf999ec5dd00 --- /dev/null +++ b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs @@ -0,0 +1,25 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Foo { + type Assoc; +} + +trait Bar {} + +impl Foo for T { + type Assoc = i32; +} + +impl Bar for T where T: Foo {} + +fn require_bar() {} + +fn foo() { + // Unlike the classic solver, `::Assoc = _` will still project + // down to `i32` even though there's a param-env candidate here, since we + // don't assemble any param-env projection candidates for `T: Foo` alone. + require_bar::(); +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs new file mode 100644 index 0000000000000..cde2059ca9b91 --- /dev/null +++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs @@ -0,0 +1,30 @@ +// compile-flags: -Ztrait-solver=next + +// When we're solving `::Assoc = i32`, we actually first solve +// `::Assoc = _#1t`, then unify `_#1t` with `i32`. That goal +// with the inference variable is ambiguous when there are >1 param-env +// candidates. + +// We don't unify the RHS of a projection goal eagerly when solving, both +// for caching reasons and partly to make sure that we don't make the new +// trait solver smarter than it should be. + +// This is (as far as I can tell) a forwards-compatible decision, but if you +// make this test go from fail to pass, be sure you understand the implications! + +trait Foo { + type Assoc; +} + +trait Bar {} + +impl Bar for T where T: Foo {} + +fn needs_bar() {} + +fn foo + Foo>() { + needs_bar::(); + //~^ ERROR type annotations needed: cannot satisfy `T: Bar` +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr new file mode 100644 index 0000000000000..fa5e780ee5e8b --- /dev/null +++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr @@ -0,0 +1,16 @@ +error[E0283]: type annotations needed: cannot satisfy `T: Bar` + --> $DIR/two-projection-param-candidates-are-ambiguous.rs:26:5 + | +LL | needs_bar::(); + | ^^^^^^^^^^^^^^ + | + = note: cannot satisfy `T: Bar` +note: required by a bound in `needs_bar` + --> $DIR/two-projection-param-candidates-are-ambiguous.rs:23:17 + | +LL | fn needs_bar() {} + | ^^^ required by this bound in `needs_bar` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. From 0eba2f3c16b4e2792b75762c7643f3a09c9f1a3d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 19 Jan 2023 21:13:01 +0000 Subject: [PATCH 11/11] Suggest fn call on pattern type mismatch --- compiler/rustc_hir_typeck/src/_match.rs | 2 +- compiler/rustc_hir_typeck/src/check.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 6 ++-- compiler/rustc_hir_typeck/src/pat.rs | 19 ++++++++--- .../suggest-call-on-pat-mismatch.rs | 16 +++++++++ .../suggest-call-on-pat-mismatch.stderr | 33 +++++++++++++++++++ 6 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 tests/ui/suggestions/suggest-call-on-pat-mismatch.rs create mode 100644 tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 88fb265358686..e19ef2ff3bf48 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #55810: Type check patterns first so we get types for all bindings. let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); for arm in arms { - self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true); + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut)); } // Now typecheck the blocks. diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 1c70c1b71e763..cc515e6c85313 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>( for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { // Check the pattern. let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); + fcx.check_pat_top(¶m.pat, param_ty, ty_span, None); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2a1265600de8b..9c7a84ce198e8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1330,11 +1330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Does the expected pattern type originate from an expression and what is the span? let (origin_expr, ty_span) = match (decl.ty, decl.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type. (_, Some(init)) => { - (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) } // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained. }; // Type check the pattern. Override if necessary to avoid knock-on errors. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 46799245222dc..52236ae56eeaa 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -46,7 +46,7 @@ struct TopInfo<'tcx> { /// Was the origin of the `span` from a scrutinee expression? /// /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, /// The span giving rise to the `expected` type, if one could be provided. /// /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: @@ -74,7 +74,8 @@ struct TopInfo<'tcx> { impl<'tcx> FnCtxt<'_, 'tcx> { fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { - let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; + let code = + Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() }; self.cause(cause_span, code) } @@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { actual: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Option> { - self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) + let mut diag = + self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?; + if let Some(expr) = ti.origin_expr { + self.suggest_fn_call(&mut diag, expr, expected, |output| { + self.can_eq(self.param_env, output, actual).is_ok() + }); + } + Some(diag) } fn demand_eqtype_pat( @@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, span: Option, - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, ) { let info = TopInfo { expected, origin_expr, span }; self.check_pat(pat, expected, INITIAL_BM, info); @@ -2146,7 +2154,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("the semantics of slice patterns changed recently; see issue #62254"); } else if self.autoderef(span, expected_ty) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) - && let (Some(span), true) = (ti.span, ti.origin_expr) + && let Some(span) = ti.span + && let Some(_) = ti.origin_expr && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let ty = self.resolve_vars_if_possible(ti.expected); diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs new file mode 100644 index 0000000000000..657dd9c22c21b --- /dev/null +++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs @@ -0,0 +1,16 @@ +enum E { + One(i32, i32), +} + +fn main() { + let var = E::One; + if let E::One(var1, var2) = var { + //~^ ERROR mismatched types + //~| HELP use parentheses to construct this tuple variant + println!("{var1} {var2}"); + } + + let Some(x) = Some; + //~^ ERROR mismatched types + //~| HELP use parentheses to construct this tuple variant +} diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr new file mode 100644 index 0000000000000..7338312bab651 --- /dev/null +++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/suggest-call-on-pat-mismatch.rs:7:12 + | +LL | if let E::One(var1, var2) = var { + | ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}` + | | + | expected enum constructor, found `E` + | + = note: expected enum constructor `fn(i32, i32) -> E {E::One}` + found enum `E` +help: use parentheses to construct this tuple variant + | +LL | if let E::One(var1, var2) = var(/* i32 */, /* i32 */) { + | ++++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/suggest-call-on-pat-mismatch.rs:13:9 + | +LL | let Some(x) = Some; + | ^^^^^^^ ---- this expression has type `fn(_) -> Option<_> {Option::<_>::Some}` + | | + | expected enum constructor, found `Option<_>` + | + = note: expected enum constructor `fn(_) -> Option<_> {Option::<_>::Some}` + found enum `Option<_>` +help: use parentheses to construct this tuple variant + | +LL | let Some(x) = Some(/* value */); + | +++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`.