From e1eff1b0e89bf2350dcf4d7933f1984f2c37c9c4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 27 Jan 2022 16:53:17 -0800 Subject: [PATCH 01/15] Windows: Disable LLVM crash dialog boxes. --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_codegen_llvm/src/llvm_util.rs | 1 + compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 2b102188790..367c86a1dc9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -987,6 +987,7 @@ pub fn from_generic(kind: rustc_session::config::DebugInfo) -> Self { extern "C" { pub fn LLVMRustInstallFatalErrorHandler(); + pub fn LLVMRustDisableSystemDialogsOnCrash(); // Create and destroy contexts. pub fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index d49df29f453..437f9a9e4cc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -46,6 +46,7 @@ unsafe fn configure_llvm(sess: &Session) { let mut llvm_args = Vec::with_capacity(n_args + 1); llvm::LLVMRustInstallFatalErrorHandler(); + llvm::LLVMRustDisableSystemDialogsOnCrash(); fn llvm_arg_to_arg_name(full_arg: &str) -> &str { full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("") diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index dcd6327c92f..d871290744f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -76,6 +76,10 @@ extern "C" void LLVMRustInstallFatalErrorHandler() { install_fatal_error_handler(FatalErrorHandler); } +extern "C" void LLVMRustDisableSystemDialogsOnCrash() { + sys::DisableSystemDialogsOnCrash(); +} + extern "C" char *LLVMRustGetLastError(void) { char *Ret = LastError; LastError = nullptr; From 71249a9ef7a47226bbfc6d652bfb731124822c68 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Mon, 31 Jan 2022 10:38:15 -0500 Subject: [PATCH 02/15] Add rustdoc info to jsondocck output --- src/tools/compiletest/src/runtest.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index f039ba59d23..807092eede2 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2412,7 +2412,10 @@ fn run_rustdoc_json_test(&self) { ); if !res.status.success() { - self.fatal_proc_rec("jsondocck failed!", &res) + self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| { + println!("Rustdoc Output:"); + proc_res.print_info(); + }) } let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); @@ -3759,10 +3762,7 @@ pub struct ProcRes { } impl ProcRes { - pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! { - if let Some(e) = err { - println!("\nerror: {}", e); - } + pub fn print_info(&self) { print!( "\ status: {}\n\ @@ -3781,6 +3781,13 @@ pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! { json::extract_rendered(&self.stdout), json::extract_rendered(&self.stderr), ); + } + + pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! { + if let Some(e) = err { + println!("\nerror: {}", e); + } + self.print_info(); on_failure(); // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from // compiletest, which is unnecessary noise. From ed88e615c49928ba338c82d344e28557c26650e1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 1 Feb 2022 17:14:59 +0100 Subject: [PATCH 03/15] Add package.json in gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 87437a16fb3..ec6cb6ed2e4 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ __pycache__/ ## Node node_modules package-lock.json +package.json ## Rustdoc GUI tests src/test/rustdoc-gui/src/**.lock From 1bc8f0b49f34927c08d1f31d2bb32ab7404ec948 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 1 Feb 2022 17:47:19 +0000 Subject: [PATCH 04/15] Link `try_exists` docs to `Path::exists` --- library/std/src/fs.rs | 4 +++- library/std/src/path.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9d6b2fe8c25..7ca64c38e5d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2288,7 +2288,7 @@ fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `Ok(false)`. /// -/// As opposed to the `exists()` method, this one doesn't silently ignore errors +/// As opposed to the [`Path::exists`] method, this one doesn't silently ignore errors /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission /// denied on some of the parent directories.) /// @@ -2301,6 +2301,8 @@ fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { /// assert!(!fs::try_exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt")); /// assert!(fs::try_exists("/root/secret_file.txt").is_err()); /// ``` +/// +/// [`Path::exists`]: crate::path::Path::exists // FIXME: stabilization should modify documentation of `exists()` to recommend this method // instead. #[unstable(feature = "path_try_exists", issue = "83186")] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index e540f860160..85cad65da6a 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2730,7 +2730,7 @@ pub fn exists(&self) -> bool { /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `Ok(false)`. /// - /// As opposed to the `exists()` method, this one doesn't silently ignore errors + /// As opposed to the [`exists()`] method, this one doesn't silently ignore errors /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission /// denied on some of the parent directories.) /// @@ -2743,6 +2743,8 @@ pub fn exists(&self) -> bool { /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt")); /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err()); /// ``` + /// + /// [`exists()`]: Self::exists // FIXME: stabilization should modify documentation of `exists()` to recommend this method // instead. #[unstable(feature = "path_try_exists", issue = "83186")] From 83242897fb356f51762e0e466dfd53186725028a Mon Sep 17 00:00:00 2001 From: tamaron Date: Wed, 2 Feb 2022 23:07:02 +0900 Subject: [PATCH 05/15] add tests --- library/core/tests/future.rs | 8 ++++++++ library/core/tests/hash/mod.rs | 8 ++++++++ library/core/tests/iter/traits/iterator.rs | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/library/core/tests/future.rs b/library/core/tests/future.rs index 0ed8c52c212..74b6f74e401 100644 --- a/library/core/tests/future.rs +++ b/library/core/tests/future.rs @@ -118,3 +118,11 @@ fn wake(self: Arc) { } } } + +// just tests by whether or not this compiles +fn _pending_impl_all_auto_traits() { + use std::panic::{RefUnwindSafe, UnwindSafe}; + fn all_auto_traits() {} + + all_auto_traits::>(); +} diff --git a/library/core/tests/hash/mod.rs b/library/core/tests/hash/mod.rs index 72ccdd4848a..a173e461c60 100644 --- a/library/core/tests/hash/mod.rs +++ b/library/core/tests/hash/mod.rs @@ -146,3 +146,11 @@ fn test_build_hasher_object_safe() { let _: &dyn BuildHasher = &RandomState::new(); } + +// just tests by whether or not this compiles +fn _build_hasher_default_impl_all_auto_traits() { + use std::panic::{RefUnwindSafe, UnwindSafe}; + fn all_auto_traits() {} + + all_auto_traits::>(); +} diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index bb4da831412..972d61ba909 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -496,3 +496,11 @@ fn test_collect() { let b: Vec = a.iter().cloned().collect(); assert!(a == b); } + +// just tests by whether or not this compiles +fn _empty_impl_all_auto_traits() { + use std::panic::{RefUnwindSafe, UnwindSafe}; + fn all_auto_traits() {} + + all_auto_traits::>(); +} From 7efba33582ab226e64a8cbee73041d44e7789528 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jan 2022 14:50:16 +0100 Subject: [PATCH 06/15] Unify storage getter and setter functions --- src/librustdoc/html/static/js/search.js | 2 +- src/librustdoc/html/static/js/settings.js | 2 +- src/librustdoc/html/static/js/source-script.js | 8 ++++---- src/librustdoc/html/static/js/storage.js | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 104464b3881..7052d2138b6 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1619,7 +1619,7 @@ window.initSearch = function(rawSearchIndex) { } function updateCrate(ev) { - updateLocalStorage("rustdoc-saved-filter-crate", ev.target.value); + updateLocalStorage("saved-filter-crate", ev.target.value); // In case you "cut" the entry from the search input, then change the crate filter // before paste back the previous search, you get the old search results without // the filter. To prevent this, we need to remove the previous results. diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 47a8fcdfd5e..139fa5c9a11 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -4,7 +4,7 @@ (function () { function changeSetting(settingName, value) { - updateLocalStorage("rustdoc-" + settingName, value); + updateLocalStorage(settingName, value); switch (settingName) { case "theme": diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 498f60e9f25..90490acccfd 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -82,11 +82,11 @@ function toggleSidebar() { if (child.innerText === ">") { sidebar.classList.add("expanded"); child.innerText = "<"; - updateLocalStorage("rustdoc-source-sidebar-show", "true"); + updateLocalStorage("source-sidebar-show", "true"); } else { sidebar.classList.remove("expanded"); child.innerText = ">"; - updateLocalStorage("rustdoc-source-sidebar-show", "false"); + updateLocalStorage("source-sidebar-show", "false"); } } @@ -97,7 +97,7 @@ function createSidebarToggle() { var inner = document.createElement("div"); - if (getCurrentValue("rustdoc-source-sidebar-show") === "true") { + if (getCurrentValue("source-sidebar-show") === "true") { inner.innerText = "<"; } else { inner.innerText = ">"; @@ -120,7 +120,7 @@ function createSourceSidebar() { var sidebar = document.createElement("div"); sidebar.id = "source-sidebar"; - if (getCurrentValue("rustdoc-source-sidebar-show") !== "true") { + if (getCurrentValue("source-sidebar-show") !== "true") { container.classList.remove("expanded"); } else { container.classList.add("expanded"); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 06bb34eb115..ccf3d0a581a 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -15,7 +15,7 @@ var settingsDataset = (function () { })(); function getSettingValue(settingName) { - var current = getCurrentValue('rustdoc-' + settingName); + var current = getCurrentValue(settingName); if (current !== null) { return current; } @@ -106,7 +106,7 @@ function hasOwnPropertyRustdoc(obj, property) { function updateLocalStorage(name, value) { try { - window.localStorage.setItem(name, value); + window.localStorage.setItem("rustdoc-" + name, value); } catch(e) { // localStorage is not accessible, do nothing } @@ -114,7 +114,7 @@ function updateLocalStorage(name, value) { function getCurrentValue(name) { try { - return window.localStorage.getItem(name); + return window.localStorage.getItem("rustdoc-" + name); } catch(e) { return null; } @@ -127,7 +127,7 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) { // If this new value comes from a system setting or from the previously // saved theme, no need to save it. if (saveTheme) { - updateLocalStorage("rustdoc-theme", newTheme); + updateLocalStorage("theme", newTheme); } if (styleElem.href === newHref) { @@ -158,7 +158,7 @@ function useSystemTheme(value) { value = true; } - updateLocalStorage("rustdoc-use-system-theme", value); + updateLocalStorage("use-system-theme", value); // update the toggle if we're on the settings page var toggle = document.getElementById("use-system-theme"); @@ -231,7 +231,7 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { if (getSettingValue("use-system-theme") === null && getSettingValue("preferred-dark-theme") === null && darkThemes.indexOf(localStoredTheme) >= 0) { - updateLocalStorage("rustdoc-preferred-dark-theme", localStoredTheme); + updateLocalStorage("preferred-dark-theme", localStoredTheme); } // call the function to initialize the theme at least once! From c64d6bf5af368066af7ecc2f69555b1721cfd0b5 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 3 Feb 2022 07:03:44 -0800 Subject: [PATCH 07/15] Only disable dialogs on CI. The "CI" environment var isn't universal (for example, I think Azure uses TF_BUILD). However, we are mostly concerned with rust-lang/rust's own CI which currently is GitHub Actions which does set "CI". And I think most other providers use "CI" as well. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 437f9a9e4cc..f91fad2d9c9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -46,7 +46,12 @@ unsafe fn configure_llvm(sess: &Session) { let mut llvm_args = Vec::with_capacity(n_args + 1); llvm::LLVMRustInstallFatalErrorHandler(); - llvm::LLVMRustDisableSystemDialogsOnCrash(); + // On Windows, an LLVM assertion will open an Abort/Retry/Ignore dialog + // box for the purpose of launching a debugger. However, on CI this will + // cause it to hang until it times out, which can take several hours. + if std::env::var_os("CI").is_some() { + llvm::LLVMRustDisableSystemDialogsOnCrash(); + } fn llvm_arg_to_arg_name(full_arg: &str) -> &str { full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("") From 2dfd77d67555ab9a5a76002bb47749a795f3a5bf Mon Sep 17 00:00:00 2001 From: kadmin Date: Wed, 2 Feb 2022 16:42:37 +0000 Subject: [PATCH 08/15] Fix ret > 1 bound if shadowed by const Prior to a change, it would only look at types in bounds. When it started looking for consts, shadowing type variables with a const would cause an ICE, so now defer looking at consts only if there are no types present. --- compiler/rustc_middle/src/ty/assoc.rs | 5 +- compiler/rustc_typeck/src/astconv/mod.rs | 57 ++++++++++--------- .../ui/associated-consts/shadowed-const.rs | 23 ++++++++ .../associated-consts/shadowed-const.stderr | 8 +++ 4 files changed, 62 insertions(+), 31 deletions(-) create mode 100644 src/test/ui/associated-consts/shadowed-const.rs create mode 100644 src/test/ui/associated-consts/shadowed-const.stderr diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index c23d4eae1a4..49f846562a3 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -160,12 +160,11 @@ pub fn find_by_name_and_kinds( &self, tcx: TyCtxt<'_>, ident: Ident, + // Sorted in order of what kinds to look at kinds: &[AssocKind], parent_def_id: DefId, ) -> Option<&ty::AssocItem> { - self.filter_by_name_unhygienic(ident.name) - .filter(|item| kinds.contains(&item.kind)) - .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id)) + kinds.iter().find_map(|kind| self.find_by_name_and_kind(tcx, ident, *kind, parent_def_id)) } /// Returns the associated item with the given name in the given `Namespace`, if one exists. diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 3e2d7fc3820..0ad2242f667 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -887,15 +887,10 @@ fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: I .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id) .is_some() } - fn trait_defines_associated_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { + fn trait_defines_associated_const_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool { self.tcx() .associated_items(trait_def_id) - .find_by_name_and_kinds( - self.tcx(), - assoc_name, - &[ty::AssocKind::Type, ty::AssocKind::Const], - trait_def_id, - ) + .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Const, trait_def_id) .is_some() } @@ -1145,13 +1140,13 @@ fn add_predicates_for_ast_type_binding( // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. - let assoc_item = tcx - .associated_items(candidate.def_id()) - .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| { - (i.kind == ty::AssocKind::Type || i.kind == ty::AssocKind::Const) - && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident - }) + let find_item_of_kind = |kind| { + tcx.associated_items(candidate.def_id()) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) + }; + let assoc_item = find_item_of_kind(ty::AssocKind::Type) + .or_else(|| find_item_of_kind(ty::AssocKind::Const)) .expect("missing associated type"); if !assoc_item.vis.is_accessible_from(def_scope, tcx) { @@ -1657,11 +1652,14 @@ fn one_bound_for_assoc_type( I: Iterator>, { let mut matching_candidates = all_candidates() - .filter(|r| self.trait_defines_associated_named(r.def_id(), assoc_name)); + .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name)); + let mut const_candidates = all_candidates() + .filter(|r| self.trait_defines_associated_const_named(r.def_id(), assoc_name)); - let bound = match matching_candidates.next() { - Some(bound) => bound, - None => { + let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) { + (Some(bound), _) => (bound, matching_candidates.next()), + (None, Some(bound)) => (bound, const_candidates.next()), + (None, None) => { self.complain_about_assoc_type_not_found( all_candidates, &ty_param_name(), @@ -1671,10 +1669,9 @@ fn one_bound_for_assoc_type( return Err(ErrorReported); } }; - debug!("one_bound_for_assoc_type: bound = {:?}", bound); - if let Some(bound2) = matching_candidates.next() { + if let Some(bound2) = next_cand { debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); let is_equality = is_equality(); @@ -1759,6 +1756,7 @@ fn one_bound_for_assoc_type( return Err(ErrorReported); } } + Ok(bound) } @@ -1893,14 +1891,17 @@ pub fn associated_path_to_ty( // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. - let item = tcx - .associated_items(trait_did) - .in_definition_order() - .find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident - }) - .expect("missing associated type"); + let item = tcx.associated_items(trait_did).in_definition_order().find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident + }); + // Assume that if it's not matched, there must be a const defined with the same name + // but it was used in a type position. + let Some(item) = item else { + let msg = format!("found associated const `{assoc_ident}` when type was expected"); + tcx.sess.struct_span_err(span, &msg).emit(); + return Err(ErrorReported); + }; let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); let ty = self.normalize_ty(span, ty); diff --git a/src/test/ui/associated-consts/shadowed-const.rs b/src/test/ui/associated-consts/shadowed-const.rs new file mode 100644 index 00000000000..cfdb391d39d --- /dev/null +++ b/src/test/ui/associated-consts/shadowed-const.rs @@ -0,0 +1,23 @@ +// Checking that none of these ICE, which was introduced in +// https://github.com/rust-lang/rust/issues/93553 +trait Foo { + type Bar; +} + +trait Baz: Foo { + const Bar: Self::Bar; +} + +trait Baz2: Foo { + const Bar: u32; + + fn foo() -> Self::Bar; +} + +trait Baz3 { + const BAR: usize; + const QUX: Self::BAR; + //~^ ERROR found associated const +} + +fn main() {} diff --git a/src/test/ui/associated-consts/shadowed-const.stderr b/src/test/ui/associated-consts/shadowed-const.stderr new file mode 100644 index 00000000000..fe21d2aec00 --- /dev/null +++ b/src/test/ui/associated-consts/shadowed-const.stderr @@ -0,0 +1,8 @@ +error: found associated const `BAR` when type was expected + --> $DIR/shadowed-const.rs:19:14 + | +LL | const QUX: Self::BAR; + | ^^^^^^^^^ + +error: aborting due to previous error + From de2abc29e9f890433bef39eac46a84bdb9eaecf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 3 Feb 2022 21:44:47 +0100 Subject: [PATCH 09/15] clippy::perf fixes single_char_pattern and to_string_in_format_args --- compiler/rustc_codegen_llvm/src/back/archive.rs | 2 +- compiler/rustc_interface/src/interface.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 6 +++--- compiler/rustc_typeck/src/check/expr.rs | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 5703a72c686..8a1dea4d99b 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -219,7 +219,7 @@ fn inject_dll_import_lib( match result { Err(e) => { - self.config.sess.fatal(&format!("Error calling dlltool: {}", e.to_string())); + self.config.sess.fatal(&format!("Error calling dlltool: {}", e)); } Ok(output) if !output.status.success() => self.config.sess.fatal(&format!( "Dlltool could not create import library: {}\n{}", diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 26343561959..237aef1cf23 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -126,7 +126,7 @@ macro_rules! error { // If the user tried to use a key="value" flag, but is missing the quotes, provide // a hint about how to resolve this. - if s.contains("=") && !s.contains("=\"") && !s.ends_with("\"") { + if s.contains('=') && !s.contains("=\"") && !s.ends_with('"') { error!(concat!( r#"expected `key` or `key="value"`, ensure escaping is appropriate"#, r#" for your shell, try 'key="value"' or key=\"value\""# diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0115d498a7f..4898a4844b9 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1702,11 +1702,11 @@ fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { // Try to lowercase the prefix if it's a valid base prefix. fn fix_base_capitalisation(s: &str) -> Option { - if let Some(stripped) = s.strip_prefix("B") { + if let Some(stripped) = s.strip_prefix('B') { Some(format!("0b{stripped}")) - } else if let Some(stripped) = s.strip_prefix("O") { + } else if let Some(stripped) = s.strip_prefix('O') { Some(format!("0o{stripped}")) - } else if let Some(stripped) = s.strip_prefix("X") { + } else if let Some(stripped) = s.strip_prefix('X') { Some(format!("0x{stripped}")) } else { None diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 82cda5a2f2e..0347b6a4ab8 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1587,10 +1587,10 @@ fn report_missing_fields( ) { let len = remaining_fields.len(); - let mut displayable_field_names = - remaining_fields.keys().map(|ident| ident.as_str()).collect::>(); - - displayable_field_names.sort(); + let mut displayable_field_names: Vec<&str> = + remaining_fields.keys().map(|ident| ident.as_str()).collect(); + // sorting &str primitives here, sort_unstable is ok + displayable_field_names.sort_unstable(); let mut truncated_fields_error = String::new(); let remaining_fields_names = match &displayable_field_names[..] { From a2a4cababeafcc5d5de0b26ba6e0d630480220dc Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 3 Feb 2022 13:51:37 -0700 Subject: [PATCH 10/15] rustc_mir_dataflow: use iter::once instead of Some().into_iter --- compiler/rustc_mir_dataflow/src/elaborate_drops.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 501bc96401a..a3294672f54 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; -use std::fmt; +use std::{fmt, iter}; /// The value of an inserted drop flag. #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -329,8 +329,7 @@ fn drop_halfladder( mut succ: BasicBlock, fields: &[(Place<'tcx>, Option)], ) -> Vec { - Some(succ) - .into_iter() + iter::once(succ) .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { succ = self.drop_subpath(place, path, succ, unwind_succ); succ From cfa677666cd7982cba107736be9fcc730d5bf6bf Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jan 2022 14:57:43 +0100 Subject: [PATCH 11/15] Add filter-crate URL parameter --- src/librustdoc/html/static/js/main.js | 23 +---- src/librustdoc/html/static/js/search.js | 121 ++++++++++++++++++------ 2 files changed, 97 insertions(+), 47 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 161b95d9993..4d13956c375 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -54,7 +54,6 @@ function resourcePath(basename, extension) { return getVar("root-path") + basename + getVar("resource-suffix") + extension; } - (function () { window.rootPath = getVar("root-path"); window.currentCrate = getVar("current-crate"); @@ -232,7 +231,7 @@ function hideThemeButtonState() { document.title = searchState.titleBeforeSearch; // We also remove the query parameter from the URL. if (searchState.browserSupportsHistoryApi()) { - history.replaceState("", window.currentCrate + " - Rust", + history.replaceState(null, window.currentCrate + " - Rust", getNakedUrl() + window.location.hash); } }, @@ -246,18 +245,6 @@ function hideThemeButtonState() { }); return params; }, - putBackSearch: function(search_input) { - var search = searchState.outputElement(); - if (search_input.value !== "" && hasClass(search, "hidden")) { - searchState.showResults(search); - if (searchState.browserSupportsHistoryApi()) { - var extra = "?search=" + encodeURIComponent(search_input.value); - history.replaceState(search_input.value, "", - getNakedUrl() + extra + window.location.hash); - } - document.title = searchState.title; - } - }, browserSupportsHistoryApi: function() { return window.history && typeof window.history.pushState === "function"; }, @@ -282,14 +269,10 @@ function hideThemeButtonState() { } search_input.addEventListener("focus", function() { - searchState.putBackSearch(this); - search_input.origPlaceholder = searchState.input.placeholder; + search_input.origPlaceholder = search_input.placeholder; search_input.placeholder = "Type your search here."; loadSearch(); }); - search_input.addEventListener("blur", function() { - search_input.placeholder = searchState.input.origPlaceholder; - }); if (search_input.value != '') { loadSearch(); @@ -330,7 +313,7 @@ function hideThemeButtonState() { var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); if (searchState.browserSupportsHistoryApi()) { // `window.location.search`` contains all the query parameters, not just `search`. - history.replaceState(hash, "", + history.replaceState(null, "", getNakedUrl() + window.location.search + "#" + hash); } elem = document.getElementById(hash); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 7052d2138b6..8c832a222b7 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1,5 +1,5 @@ /* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */ -/* global onEachLazy, removeClass, searchState, updateLocalStorage */ +/* global onEachLazy, removeClass, searchState, hasClass */ (function() { // This mapping table should match the discriminants of @@ -133,6 +133,39 @@ window.initSearch = function(rawSearchIndex) { searchState.input.value = params.search || ""; } + /** + * Build an URL with search parameters. + * + * @param {string} search - The current search being performed. + * @param {string|null} filterCrates - The current filtering crate (if any). + * @return {string} + */ + function buildUrl(search, filterCrates) { + var extra = "?search=" + encodeURIComponent(search); + + if (filterCrates !== null) { + extra += "&filter-crate=" + encodeURIComponent(filterCrates); + } + return getNakedUrl() + extra + window.location.hash; + } + + /** + * Return the filtering crate or `null` if there is none. + * + * @return {string|null} + */ + function getFilterCrates() { + var elem = document.getElementById("crate-search"); + + if (elem && + elem.value !== "All crates" && + hasOwnPropertyRustdoc(rawSearchIndex, elem.value)) + { + return elem.value; + } + return null; + } + /** * Executes the query and returns a list of results for each results tab. * @param {Object} query - The user query @@ -595,7 +628,7 @@ window.initSearch = function(rawSearchIndex) { // aliases to be before the others in the displayed results. var aliases = []; var crateAliases = []; - if (filterCrates !== undefined) { + if (filterCrates !== null) { if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { var query_aliases = ALIASES[filterCrates][query.search]; var len = query_aliases.length; @@ -694,7 +727,7 @@ window.initSearch = function(rawSearchIndex) { { val = extractGenerics(val.substr(1, val.length - 2)); for (i = 0; i < nSearchWords; ++i) { - if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { + if (filterCrates !== null && searchIndex[i].crate !== filterCrates) { continue; } in_args = findArg(searchIndex[i], val, true, typeFilter); @@ -725,7 +758,7 @@ window.initSearch = function(rawSearchIndex) { var output = extractGenerics(parts[1]); for (i = 0; i < nSearchWords; ++i) { - if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { + if (filterCrates !== null && searchIndex[i].crate !== filterCrates) { continue; } var type = searchIndex[i].type; @@ -781,7 +814,7 @@ window.initSearch = function(rawSearchIndex) { var lev, j; for (j = 0; j < nSearchWords; ++j) { ty = searchIndex[j]; - if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) { + if (!ty || (filterCrates !== null && ty.crate !== filterCrates)) { continue; } var lev_add = 0; @@ -1279,17 +1312,6 @@ window.initSearch = function(rawSearchIndex) { }; } - function getFilterCrates() { - var elem = document.getElementById("crate-search"); - - if (elem && elem.value !== "All crates" && - hasOwnPropertyRustdoc(rawSearchIndex, elem.value)) - { - return elem.value; - } - return undefined; - } - /** * Perform a search based on the current state of the search input element * and display the results. @@ -1309,27 +1331,34 @@ window.initSearch = function(rawSearchIndex) { } if (!forced && query.id === currentResults) { if (query.query.length > 0) { - searchState.putBackSearch(searchState.input); + putBackSearch(); } return; } + var filterCrates = getFilterCrates(); + + // In case we have no information about the saved crate and there is a URL query parameter, + // we override it with the URL query parameter. + if (filterCrates === null && params["filter-crate"] !== undefined) { + filterCrates = params["filter-crate"]; + } + // Update document title to maintain a meaningful browser history searchState.title = "Results for " + query.query + " - Rust"; // Because searching is incremental by character, only the most // recent search query is added to the browser history. if (searchState.browserSupportsHistoryApi()) { - var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) + - window.location.hash; + var newURL = buildUrl(query.raw, filterCrates); + if (!history.state && !params.search) { - history.pushState(query, "", newURL); + history.pushState(null, "", newURL); } else { - history.replaceState(query, "", newURL); + history.replaceState(null, "", newURL); } } - var filterCrates = getFilterCrates(); showResults(execSearch(query, searchWords, filterCrates), params["go_to_first"], filterCrates); } @@ -1495,12 +1524,28 @@ window.initSearch = function(rawSearchIndex) { search(); } + function putBackSearch() { + var search_input = searchState.input; + if (!searchState.input) { + return; + } + var search = searchState.outputElement(); + if (search_input.value !== "" && hasClass(search, "hidden")) { + searchState.showResults(search); + if (searchState.browserSupportsHistoryApi()) { + history.replaceState(null, "", + buildUrl(search_input.value, getFilterCrates())); + } + document.title = searchState.title; + } + } + function registerSearchEvents() { var searchAfter500ms = function() { searchState.clearInputTimeout(); if (searchState.input.value.length === 0) { if (searchState.browserSupportsHistoryApi()) { - history.replaceState("", window.currentCrate + " - Rust", + history.replaceState(null, window.currentCrate + " - Rust", getNakedUrl() + window.location.hash); } searchState.hideResults(); @@ -1567,6 +1612,14 @@ window.initSearch = function(rawSearchIndex) { } }); + searchState.input.addEventListener("focus", function() { + putBackSearch(); + }); + + searchState.input.addEventListener("blur", function() { + searchState.input.placeholder = searchState.input.origPlaceholder; + }); + // Push and pop states are used to add search results to the browser // history. if (searchState.browserSupportsHistoryApi()) { @@ -1619,7 +1672,16 @@ window.initSearch = function(rawSearchIndex) { } function updateCrate(ev) { - updateLocalStorage("saved-filter-crate", ev.target.value); + if (ev.target.value === "All crates") { + // If we don't remove it from the URL, it'll be picked up again by the search. + var params = searchState.getQueryStringParams(); + var query = searchState.input.value.trim(); + if (!history.state && !params.search) { + history.pushState(null, "", buildUrl(query, null)); + } else { + history.replaceState(null, "", buildUrl(query, null)); + } + } // In case you "cut" the entry from the search input, then change the crate filter // before paste back the previous search, you get the old search results without // the filter. To prevent this, we need to remove the previous results. @@ -1629,10 +1691,15 @@ window.initSearch = function(rawSearchIndex) { searchWords = buildIndex(rawSearchIndex); registerSearchEvents(); - // If there's a search term in the URL, execute the search now. - if (searchState.getQueryStringParams().search) { - search(); + + function runSearchIfNeeded() { + // If there's a search term in the URL, execute the search now. + if (searchState.getQueryStringParams().search) { + search(); + } } + + runSearchIfNeeded(); }; if (window.searchIndex !== undefined) { From 17fe3068d22a33aa13392379bf0ffc3a0ba545c1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 3 Feb 2022 22:43:02 +0100 Subject: [PATCH 12/15] Update tester to have FILTER_CRATE set to null if undefined. --- src/tools/rustdoc-js/tester.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 4f73a7f6340..dbf5cf9650c 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -357,6 +357,8 @@ function runChecks(testFile, loaded, index) { var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;'; if (testFileContent.indexOf("FILTER_CRATE") !== -1) { testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;"; + } else { + testFileContent += "exports.FILTER_CRATE = null;"; } var loadedFile = loadContent(testFileContent); From 829a0471d01d728585c4a8b433b815a82bf72cb3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jan 2022 17:48:07 +0100 Subject: [PATCH 13/15] Add GUI test for crate filter URL parameter --- src/test/rustdoc-gui/search-filter.goml | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/rustdoc-gui/search-filter.goml b/src/test/rustdoc-gui/search-filter.goml index 98ca40512ee..73d310fc5c9 100644 --- a/src/test/rustdoc-gui/search-filter.goml +++ b/src/test/rustdoc-gui/search-filter.goml @@ -11,8 +11,38 @@ wait-for: "#crate-search" click: "#crate-search" // We select "lib2" option then press enter to change the filter. press-key: "ArrowDown" +press-key: "ArrowDown" press-key: "Enter" // Waiting for the search results to appear... wait-for: "#titles" // We check that there is no more "test_docs" appearing. assert-false: "#results .externcrate" +// We also check that "lib2" is the filter crate. +assert-property: ("#crate-search", {"value": "lib2"}) + +// Now we check that leaving the search results and putting them back keeps the +// crate filtering. +press-key: "Escape" +wait-for: 100 +assert-css: ("#main-content", {"display": "block"}) +focus: ".search-input" +wait-for: 100 +assert-css: ("#main-content", {"display": "none"}) +// We check that there is no more "test_docs" appearing. +assert-false: "#results .externcrate" +assert-property: ("#crate-search", {"value": "lib2"}) + +// Selecting back "All crates" +click: "#crate-search" +press-key: "ArrowUp" +press-key: "ArrowUp" +press-key: "Enter" +// Waiting for the search results to appear... +wait-for: "#titles" +assert-property: ("#crate-search", {"value": "All crates"}) + +// Checking that the URL parameter is taken into account for crate filtering. +goto: file://|DOC_PATH|/test_docs/index.html?search=test&filter-crate=lib2 +wait-for: "#crate-search" +assert-property: ("#crate-search", {"value": "lib2"}) +assert-false: "#results .externcrate" From f1a399abaae16df04551f861afcadab4d49ac841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Thu, 3 Feb 2022 22:28:19 +0100 Subject: [PATCH 14/15] rustdoc: clippy::complexity fixes clippy::map_flatten clippy::clone_on_copy clippy::useless_conversion clippy::needless_arbitrary_self_type --- src/librustdoc/clean/mod.rs | 4 +--- src/librustdoc/clean/types.rs | 3 +-- src/librustdoc/config.rs | 3 +-- src/librustdoc/html/format.rs | 2 +- src/librustdoc/html/highlight.rs | 3 +-- src/librustdoc/html/render/search_index.rs | 2 +- src/librustdoc/scrape_examples.rs | 3 +-- 7 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 95404b33822..cfdd119377f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1533,9 +1533,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Type { for pb in obj.projection_bounds() { bindings.push(TypeBinding { name: cx.tcx.associated_item(pb.item_def_id()).name, - kind: TypeBindingKind::Equality { - term: pb.skip_binder().term.clean(cx).into(), - }, + kind: TypeBindingKind::Equality { term: pb.skip_binder().term.clean(cx) }, }); } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 02633698273..14c93ce2bdc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1036,8 +1036,7 @@ impl Attributes { // Additional documentation should be shown before the original documentation let other_attrs = additional_attrs .into_iter() - .map(|(attrs, id)| attrs.iter().map(move |attr| (attr, Some(id)))) - .flatten() + .flat_map(|(attrs, id)| attrs.iter().map(move |attr| (attr, Some(id)))) .chain(attrs.iter().map(|attr| (attr, None))) .filter_map(clean_attr) .collect(); diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 959f83a0211..6e483d27f33 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -439,13 +439,12 @@ fn println_condition(condition: Condition) { matches .opt_str("default-theme") .iter() - .map(|theme| { + .flat_map(|theme| { vec![ ("use-system-theme".to_string(), "false".to_string()), ("theme".to_string(), theme.to_string()), ] }) - .flatten() .collect(), matches .opt_strs("default-setting") diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f4df9ef4a8c..c0115bfc6d4 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -76,7 +76,7 @@ fn write_char(&mut self, c: char) -> fmt::Result { } #[inline] - fn write_fmt(self: &mut Self, args: fmt::Arguments<'_>) -> fmt::Result { + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { self.buffer.write_fmt(args) } } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 39f58cdd821..06d60b6d06c 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -274,8 +274,7 @@ fn new(info: DecorationInfo) -> Self { let (mut starts, mut ends): (Vec<_>, Vec<_>) = info .0 .into_iter() - .map(|(kind, ranges)| ranges.into_iter().map(move |(lo, hi)| ((lo, kind), hi))) - .flatten() + .flat_map(|(kind, ranges)| ranges.into_iter().map(move |(lo, hi)| ((lo, kind), hi))) .unzip(); // Sort the sequences in document order. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 0ee67467c38..e1309c03b5c 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -81,7 +81,7 @@ lastpathid += 1; if let Some(&(ref fqp, short)) = paths.get(&defid) { - crate_paths.push((short, fqp.last().unwrap().clone())); + crate_paths.push((short, *fqp.last().unwrap())); Some(pathid) } else { None diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 899c9e4c629..93292efdcb6 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -247,8 +247,7 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { let target_crates = options .target_crates .into_iter() - .map(|target| all_crates.iter().filter(move |(_, name)| name.as_str() == target)) - .flatten() + .flat_map(|target| all_crates.iter().filter(move |(_, name)| name.as_str() == target)) .map(|(crate_num, _)| **crate_num) .collect::>(); From cdd22547f0a183920bd730af6c6c18caac49b456 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 3 Feb 2022 17:01:32 -0700 Subject: [PATCH 15/15] rustdoc: remove unused Hash impl --- src/librustdoc/clean/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 02633698273..6c0d2380aa2 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -953,7 +953,7 @@ fn add_doc_fragment(out: &mut String, frag: &DocFragment) { /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq)] crate struct ItemLink { /// The original link written in the markdown crate link: String,