From 83925dd453ca67c611e45cbd6c17d7a4d3041bde Mon Sep 17 00:00:00 2001 From: Michael Spector Date: Sat, 11 Sep 2021 18:40:04 +0300 Subject: [PATCH 1/8] Allow reverse iteration of lowercase'd/uppercase'd chars --- library/alloc/tests/str.rs | 31 +++++++++++++++++++++++++++++++ library/core/src/char/mod.rs | 34 ++++++++++++++++++++++++++++++++++ library/core/tests/char.rs | 6 ++++++ 3 files changed, 71 insertions(+) diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index d3a87c056cfb1..9ec57d7e64476 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1125,6 +1125,37 @@ fn test_rev_iterator() { assert_eq!(pos, v.len()); } +#[test] +fn test_to_lowercase_rev_iterator() { + let s = "AÖßÜ💩ΣΤΙΓΜΑΣDžfiİ"; + let v = ['\u{307}', 'i', 'fi', 'dž', 'σ', 'α', 'μ', 'γ', 'ι', 'τ', 'σ', '💩', 'ü', 'ß', 'ö', 'a']; + + let mut pos = 0; + let it = s.chars().flat_map(|c| c.to_lowercase()).rev(); + + for c in it { + assert_eq!(c, v[pos]); + pos += 1; + } + assert_eq!(pos, v.len()); +} + +#[test] +fn test_to_uppercase_rev_iterator() { + let s = "aößü💩στιγμαςDžfiᾀ"; + let v = + ['Ι', 'Ἀ', 'I', 'F', 'DŽ', 'Σ', 'Α', 'Μ', 'Γ', 'Ι', 'Τ', 'Σ', '💩', 'Ü', 'S', 'S', 'Ö', 'A']; + + let mut pos = 0; + let it = s.chars().flat_map(|c| c.to_uppercase()).rev(); + + for c in it { + assert_eq!(c, v[pos]); + pos += 1; + } + assert_eq!(pos, v.len()); +} + #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_chars_decoding() { diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 0728523d0a413..a9e7144eb64cd 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -393,6 +393,13 @@ impl Iterator for ToLowercase { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for ToLowercase { + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ToLowercase {} @@ -420,6 +427,13 @@ impl Iterator for ToUppercase { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for ToUppercase { + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ToUppercase {} @@ -479,6 +493,26 @@ impl Iterator for CaseMappingIter { } } +impl DoubleEndedIterator for CaseMappingIter { + fn next_back(&mut self) -> Option { + match *self { + CaseMappingIter::Three(a, b, c) => { + *self = CaseMappingIter::Two(a, b); + Some(c) + } + CaseMappingIter::Two(b, c) => { + *self = CaseMappingIter::One(b); + Some(c) + } + CaseMappingIter::One(c) => { + *self = CaseMappingIter::Zero; + Some(c) + } + CaseMappingIter::Zero => None, + } + } +} + impl fmt::Display for CaseMappingIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { diff --git a/library/core/tests/char.rs b/library/core/tests/char.rs index 51eca1e05d343..ee196db5363be 100644 --- a/library/core/tests/char.rs +++ b/library/core/tests/char.rs @@ -91,6 +91,9 @@ fn test_to_lowercase() { let iter: String = c.to_lowercase().collect(); let disp: String = c.to_lowercase().to_string(); assert_eq!(iter, disp); + let iter_rev: String = c.to_lowercase().rev().collect(); + let disp_rev: String = disp.chars().rev().collect(); + assert_eq!(iter_rev, disp_rev); iter } assert_eq!(lower('A'), "a"); @@ -118,6 +121,9 @@ fn test_to_uppercase() { let iter: String = c.to_uppercase().collect(); let disp: String = c.to_uppercase().to_string(); assert_eq!(iter, disp); + let iter_rev: String = c.to_uppercase().rev().collect(); + let disp_rev: String = disp.chars().rev().collect(); + assert_eq!(iter_rev, disp_rev); iter } assert_eq!(upper('a'), "A"); From 0198ea47d8f0a9d99820f9a3c168ab3d26fd4f2a Mon Sep 17 00:00:00 2001 From: Lucas Kent Date: Sat, 4 Dec 2021 23:15:45 +1100 Subject: [PATCH 2/8] Fix duplicate derive clone suggestion --- .../rustc_typeck/src/check/method/suggest.rs | 33 ++++++------ src/test/ui/derives/issue-91492.rs | 25 +++++++++ src/test/ui/derives/issue-91492.stderr | 54 +++++++++++++++++++ 3 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 src/test/ui/derives/issue-91492.rs create mode 100644 src/test/ui/derives/issue-91492.stderr diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index ca174ed5e8497..f0cc9d774645b 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -1175,11 +1175,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_derive( &self, err: &mut DiagnosticBuilder<'_>, - unsatisfied_predicates: &Vec<( + unsatisfied_predicates: &[( ty::Predicate<'tcx>, Option>, Option>, - )>, + )], ) { let mut derives = Vec::<(String, Span, String)>::new(); let mut traits = Vec::::new(); @@ -1216,23 +1216,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { traits.push(self.tcx.def_span(trait_pred.def_id())); } } - derives.sort(); - let derives_grouped = derives.into_iter().fold( - Vec::<(String, Span, String)>::new(), - |mut acc, (self_name, self_span, trait_name)| { - if let Some((acc_self_name, _, ref mut traits)) = acc.last_mut() { - if acc_self_name == &self_name { - traits.push_str(format!(", {}", trait_name).as_str()); - return acc; - } - } - acc.push((self_name, self_span, trait_name)); - acc - }, - ); traits.sort(); traits.dedup(); + derives.sort(); + derives.dedup(); + + let mut derives_grouped = Vec::<(String, Span, String)>::new(); + for (self_name, self_span, trait_name) in derives.into_iter() { + if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut() + { + if last_self_name == &self_name { + last_trait_names.push_str(format!(", {}", trait_name).as_str()); + continue; + } + } + derives_grouped.push((self_name, self_span, trait_name)); + } + let len = traits.len(); if len > 0 { let span: MultiSpan = traits.into(); diff --git a/src/test/ui/derives/issue-91492.rs b/src/test/ui/derives/issue-91492.rs new file mode 100644 index 0000000000000..df792f118ab76 --- /dev/null +++ b/src/test/ui/derives/issue-91492.rs @@ -0,0 +1,25 @@ +// Reproduce the issue with vec +pub struct NoDerives; +fn fun1(foo: &mut Vec, bar: &[NoDerives]) { + foo.extend_from_slice(bar); //~ ERROR +} + +// Reproduce the issue with vec +// and demonstrate that other derives are ignored in the suggested output +#[derive(Default, PartialEq)] +pub struct SomeDerives; +fn fun2(foo: &mut Vec, bar: &[SomeDerives]) { + foo.extend_from_slice(bar); //~ ERROR +} + +// Try and fail to reproduce the issue without vec. +// No idea why it doesnt reproduce the issue but its still a useful test case. +struct Object(T, A); +impl Object { + fn use_clone(&self) {} +} +fn fun3(foo: Object) { + foo.use_clone(); //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/derives/issue-91492.stderr b/src/test/ui/derives/issue-91492.stderr new file mode 100644 index 0000000000000..73c91154a7bda --- /dev/null +++ b/src/test/ui/derives/issue-91492.stderr @@ -0,0 +1,54 @@ +error[E0599]: the method `extend_from_slice` exists for mutable reference `&mut Vec`, but its trait bounds were not satisfied + --> $DIR/issue-91492.rs:4:9 + | +LL | pub struct NoDerives; + | --------------------- doesn't satisfy `NoDerives: Clone` +LL | fn fun1(foo: &mut Vec, bar: &[NoDerives]) { +LL | foo.extend_from_slice(bar); + | ^^^^^^^^^^^^^^^^^ + | + = note: the following trait bounds were not satisfied: + `NoDerives: Clone` +help: consider annotating `NoDerives` with `#[derive(Clone)]` + | +LL | #[derive(Clone)] + | + +error[E0599]: the method `extend_from_slice` exists for mutable reference `&mut Vec`, but its trait bounds were not satisfied + --> $DIR/issue-91492.rs:12:9 + | +LL | pub struct SomeDerives; + | ----------------------- doesn't satisfy `SomeDerives: Clone` +LL | fn fun2(foo: &mut Vec, bar: &[SomeDerives]) { +LL | foo.extend_from_slice(bar); + | ^^^^^^^^^^^^^^^^^ + | + = note: the following trait bounds were not satisfied: + `SomeDerives: Clone` +help: consider annotating `SomeDerives` with `#[derive(Clone)]` + | +LL | #[derive(Clone)] + | + +error[E0599]: the method `use_clone` exists for struct `Object`, but its trait bounds were not satisfied + --> $DIR/issue-91492.rs:22:9 + | +LL | pub struct NoDerives; + | --------------------- doesn't satisfy `NoDerives: Clone` +... +LL | struct Object(T, A); + | -------------------------- method `use_clone` not found for this +... +LL | foo.use_clone(); + | ^^^^^^^^^ method cannot be called on `Object` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `NoDerives: Clone` +help: consider annotating `NoDerives` with `#[derive(Clone)]` + | +LL | #[derive(Clone)] + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0599`. From b7de7973b221acb2ce900a04c11320a16fc884fb Mon Sep 17 00:00:00 2001 From: Will Crichton Date: Sat, 11 Dec 2021 10:13:16 -0800 Subject: [PATCH 3/8] Don't emit shared files when scraping dependencies --- src/librustdoc/config.rs | 4 ++++ src/librustdoc/html/render/context.rs | 16 ++++++++++------ src/librustdoc/scrape_examples.rs | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index ee19567be102f..155bd324c3994 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -282,7 +282,10 @@ crate struct RenderOptions { crate emit: Vec, /// If `true`, HTML source pages will generate links for items to their definition. crate generate_link_to_definition: bool, + /// Set of function-call locations to include as examples crate call_locations: AllCallLocations, + /// If `true`, Context::init will not emit shared files. + crate no_emit_shared: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -753,6 +756,7 @@ impl Options { emit, generate_link_to_definition, call_locations, + no_emit_shared: false, }, crate_name, output_format, diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 365d959ad9f3b..bae02bb7457ed 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -405,6 +405,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { show_type_layout, generate_link_to_definition, call_locations, + no_emit_shared, .. } = options; @@ -525,13 +526,16 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { sources::render(&mut cx, &krate)?; } - // Build our search index - let index = build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx); + if !no_emit_shared { + // Build our search index + let index = build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx); + + // Write shared runs within a flock; disable thread dispatching of IO temporarily. + Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); + write_shared(&cx, &krate, index, &md_opts)?; + Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); + } - // Write shared runs within a flock; disable thread dispatching of IO temporarily. - Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); - write_shared(&cx, &krate, index, &md_opts)?; - Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); Ok((cx, krate)) } diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 10b6fdf87f419..6809551fcfd9a 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -223,13 +223,14 @@ where crate fn run( krate: clean::Crate, - renderopts: config::RenderOptions, + mut renderopts: config::RenderOptions, cache: formats::cache::Cache, tcx: TyCtxt<'_>, options: ScrapeExamplesOptions, ) -> interface::Result<()> { let inner = move || -> Result<(), String> { // Generates source files for examples + renderopts.no_emit_shared = true; let (cx, _) = Context::init(krate, renderopts, cache, tcx).map_err(|e| e.to_string())?; // Collect CrateIds corresponding to provided target crates From de764a7ccbbcaf90db88569ce7a8b5e2214cfcd8 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 16 Dec 2021 17:21:34 +0000 Subject: [PATCH 4/8] Quote bat script command line --- library/std/src/process/tests.rs | 19 +++++++++++++++++++ library/std/src/sys/windows/process.rs | 16 ++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 67b747e410732..630a8a4c47ad0 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -416,3 +416,22 @@ fn env_empty() { let p = Command::new("cmd").args(&["/C", "exit 0"]).env_clear().spawn(); assert!(p.is_ok()); } + +// See issue #91991 +#[test] +#[cfg(windows)] +fn run_bat_script() { + let tempdir = crate::sys_common::io::test::tmpdir(); + let script_path = tempdir.join("hello.cmd"); + + crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); + let output = Command::new(&script_path) + .arg("fellow Rustaceans") + .stdout(crate::process::Stdio::piped()) + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); + assert!(output.status.success()); + assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!"); +} diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 66b210ce1bfb3..e84dfbce4a754 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -704,6 +704,19 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu // Encode the command and arguments in a command line string such // that the spawned process may recover them using CommandLineToArgvW. let mut cmd: Vec = Vec::new(); + + // CreateFileW has special handling for .bat and .cmd files, which means we + // need to add an extra pair of quotes surrounding the whole command line + // so they are properly passed on to the script. + // See issue #91991. + let is_batch_file = Path::new(prog) + .extension() + .map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat")) + .unwrap_or(false); + if is_batch_file { + cmd.push(b'"' as u16); + } + // Always quote the program name so CreateProcess doesn't interpret args as // part of the name if the binary wasn't found first time. append_arg(&mut cmd, prog, Quote::Always)?; @@ -715,6 +728,9 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu }; append_arg(&mut cmd, arg, quote)?; } + if is_batch_file { + cmd.push(b'"' as u16); + } return Ok(cmd); fn append_arg(cmd: &mut Vec, arg: &OsStr, quote: Quote) -> io::Result<()> { From 984b10da169bff9bcc1fcf75984467f0f218108a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 20 Dec 2021 12:34:10 -0800 Subject: [PATCH 5/8] Change Backtrace::enabled atomic from SeqCst to Relaxed --- library/std/src/backtrace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 0b86b4f30b959..94e6070c0f794 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -99,7 +99,7 @@ use crate::cell::UnsafeCell; use crate::env; use crate::ffi::c_void; use crate::fmt; -use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use crate::sync::Once; use crate::sys_common::backtrace::{lock, output_filename}; use crate::vec::Vec; @@ -256,7 +256,7 @@ impl Backtrace { // backtrace captures speedy, because otherwise reading environment // variables every time can be somewhat slow. static ENABLED: AtomicUsize = AtomicUsize::new(0); - match ENABLED.load(SeqCst) { + match ENABLED.load(Relaxed) { 0 => {} 1 => return false, _ => return true, @@ -268,7 +268,7 @@ impl Backtrace { Err(_) => false, }, }; - ENABLED.store(enabled as usize + 1, SeqCst); + ENABLED.store(enabled as usize + 1, Relaxed); enabled } From 874514c7b4f58256eb1c9fe4c9903cdb9e09cada Mon Sep 17 00:00:00 2001 From: Tomoaki Kawada Date: Mon, 20 Dec 2021 14:20:48 +0900 Subject: [PATCH 6/8] kmc-solid: Add `std::sys::solid::fs::File::read_buf` Catching up with commit 3b263ceb5cb89b6d53b5a03b47ec447c3a7f7765 --- library/std/src/sys/solid/fs.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/solid/fs.rs b/library/std/src/sys/solid/fs.rs index abc60b56fbbe5..8a0eeff0c4d75 100644 --- a/library/std/src/sys/solid/fs.rs +++ b/library/std/src/sys/solid/fs.rs @@ -2,7 +2,7 @@ use super::{abi, error}; use crate::{ ffi::{CStr, CString, OsStr, OsString}, fmt, - io::{self, IoSlice, IoSliceMut, SeekFrom}, + io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom}, mem::MaybeUninit, os::raw::{c_int, c_short}, os::solid::ffi::OsStrExt, @@ -339,6 +339,32 @@ impl File { } } + pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { + unsafe { + let len = buf.remaining(); + let mut out_num_bytes = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Read( + self.fd.raw(), + buf.unfilled_mut().as_mut_ptr() as *mut u8, + len, + out_num_bytes.as_mut_ptr(), + )) + .map_err(|e| e.as_io_error())?; + + // Safety: `out_num_bytes` is filled by the successful call to + // `SOLID_FS_Read` + let num_bytes_read = out_num_bytes.assume_init(); + + // Safety: `num_bytes_read` bytes were written to the unfilled + // portion of the buffer + buf.assume_init(num_bytes_read); + + buf.add_filled(num_bytes_read); + + Ok(()) + } + } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { crate::io::default_read_vectored(|buf| self.read(buf), bufs) } From 417b6f354e98a3198f456223c97b162c30ab3a5e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 22 Dec 2021 10:49:51 -0800 Subject: [PATCH 7/8] Update stability attribute for double ended case mapping iterators --- library/core/src/char/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index a9e7144eb64cd..5f30d5790a04f 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -393,7 +393,7 @@ impl Iterator for ToLowercase { } } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "case_mapping_double_ended", since = "1.59.0")] impl DoubleEndedIterator for ToLowercase { fn next_back(&mut self) -> Option { self.0.next_back() @@ -427,7 +427,7 @@ impl Iterator for ToUppercase { } } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "case_mapping_double_ended", since = "1.59.0")] impl DoubleEndedIterator for ToUppercase { fn next_back(&mut self) -> Option { self.0.next_back() From 7ba086c6db1617b17bce2de13eaa34acc366226b Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Thu, 16 Dec 2021 21:17:22 -0800 Subject: [PATCH 8/8] Add some JSDoc comments to rustdoc JS This follows the Closure Compiler dialect of JSDoc, so we can use it to do some basic type checking. We don't plan to compile with Closure Compiler, just use it to check types. See https://github.com/google/closure-compiler/wiki/ for details. --- src/librustdoc/html/static/js/README.md | 15 +++ src/librustdoc/html/static/js/externs.js | 32 ++++++ src/librustdoc/html/static/js/main.js | 7 ++ src/librustdoc/html/static/js/search.js | 130 +++++++++++++++++------ src/librustdoc/html/static/js/storage.js | 16 +++ 5 files changed, 170 insertions(+), 30 deletions(-) create mode 100644 src/librustdoc/html/static/js/README.md create mode 100644 src/librustdoc/html/static/js/externs.js diff --git a/src/librustdoc/html/static/js/README.md b/src/librustdoc/html/static/js/README.md new file mode 100644 index 0000000000000..1fd859ad7cf49 --- /dev/null +++ b/src/librustdoc/html/static/js/README.md @@ -0,0 +1,15 @@ +# Rustdoc JS + +These JavaScript files are incorporated into the rustdoc binary at build time, +and are minified and written to the filesystem as part of the doc build process. + +We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler) +dialect of JSDoc to comment our code and annotate params and return types. +To run a check: + + ./x.py doc library/std + npm i -g google-closure-compiler + google-closure-compiler -W VERBOSE \ + build//doc/{search-index*.js,crates*.js} \ + src/librustdoc/html/static/js/{search.js,main.js,storage.js} \ + --externs src/librustdoc/html/static/js/externs.js >/dev/null diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js new file mode 100644 index 0000000000000..629f90728d2f6 --- /dev/null +++ b/src/librustdoc/html/static/js/externs.js @@ -0,0 +1,32 @@ +// This file contains type definitions that are processed by the Closure Compiler but are +// not put into the JavaScript we include as part of the documentation. It is used for +// type checking. See README.md in this directory for more info. + +/* eslint-disable */ +var searchState; +function initSearch(searchIndex){} + +/** + * @typedef {{ + * raw: string, + * query: string, + * type: string, + * id: string, + * }} + */ +var ParsedQuery; + +/** + * @typedef {{ + * crate: string, + * desc: string, + * id: number, + * name: string, + * normalizedName: string, + * parent: (Object|null|undefined), + * path: string, + * ty: (Number|null|number), + * type: (Array|null) + * }} + */ +var Row; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 411a94ef2d1c1..f81f6d5d61fed 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -420,6 +420,13 @@ function hideThemeButtonState() { return document.getElementById("help"); } + /** + * Show the help popup. + * + * @param {boolean} display - Whether to show or hide the popup + * @param {Event} ev - The event that triggered this call + * @param {Element} [help] - The help element if it already exists + */ function displayHelp(display, ev, help) { if (display) { help = help ? help : getHelpElement(true); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 971bd3930cffd..cf320f7b4958a 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -113,7 +113,15 @@ window.initSearch = function(rawSearchIndex) { var INPUTS_DATA = 0; var OUTPUT_DATA = 1; var NO_TYPE_FILTER = -1; - var currentResults, index, searchIndex; + /** + * @type {Array} + */ + var searchIndex; + /** + * @type {Array} + */ + var searchWords; + var currentResults; var ALIASES = {}; var params = searchState.getQueryStringParams(); @@ -126,12 +134,15 @@ window.initSearch = function(rawSearchIndex) { } /** - * Executes the query and builds an index of results - * @param {[Object]} query [The user query] - * @param {[type]} searchWords [The list of search words to query - * against] - * @param {[type]} filterCrates [Crate to search in if defined] - * @return {[type]} [A search index of results] + * Executes the query and returns a list of results for each results tab. + * @param {Object} query - The user query + * @param {Array} searchWords - The list of search words to query against + * @param {string} [filterCrates] - Crate to search in + * @return {{ + * in_args: Array, + * returned: Array, + * others: Array + * }} */ function execQuery(query, searchWords, filterCrates) { function itemTypeFromName(typename) { @@ -847,11 +858,11 @@ window.initSearch = function(rawSearchIndex) { * This could be written functionally, but I wanted to minimise * functions on stack. * - * @param {[string]} name [The name of the result] - * @param {[string]} path [The path of the result] - * @param {[string]} keys [The keys to be used (["file", "open"])] - * @param {[object]} parent [The parent of the result] - * @return {boolean} [Whether the result is valid or not] + * @param {string} name - The name of the result + * @param {string} path - The path of the result + * @param {string} keys - The keys to be used (["file", "open"]) + * @param {Object} parent - The parent of the result + * @return {boolean} - Whether the result is valid or not */ function validateResult(name, path, keys, parent) { for (var i = 0, len = keys.length; i < len; ++i) { @@ -872,8 +883,14 @@ window.initSearch = function(rawSearchIndex) { return true; } + /** + * Parse a string into a query object. + * + * @param {string} raw - The text that the user typed. + * @returns {ParsedQuery} + */ function getQuery(raw) { - var matches, type, query; + var matches, type = "", query; query = raw; matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i); @@ -974,6 +991,12 @@ window.initSearch = function(rawSearchIndex) { return tmp; } + /** + * Render a set of search results for a single tab. + * @param {Array} array - The search results for this tab + * @param {ParsedQuery} query + * @param {boolean} display - True if this is the active tab + */ function addTab(array, query, display) { var extraClass = ""; if (display === true) { @@ -1083,7 +1106,7 @@ window.initSearch = function(rawSearchIndex) { currentResults = query.id; - var ret_others = addTab(results.others, query); + var ret_others = addTab(results.others, query, true); var ret_in_args = addTab(results.in_args, query, false); var ret_returned = addTab(results.returned, query, false); @@ -1253,6 +1276,12 @@ window.initSearch = function(rawSearchIndex) { return undefined; } + /** + * Perform a search based on the current state of the search input element + * and display the results. + * @param {Event} [e] - The event that triggered this search, if any + * @param {boolean} [forced] + */ function search(e, forced) { var params = searchState.getQueryStringParams(); var query = getQuery(searchState.input.value.trim()); @@ -1287,11 +1316,14 @@ window.initSearch = function(rawSearchIndex) { } var filterCrates = getFilterCrates(); - showResults(execSearch(query, index, filterCrates), params.go_to_first); + showResults(execSearch(query, searchWords, filterCrates), params["go_to_first"]); } function buildIndex(rawSearchIndex) { searchIndex = []; + /** + * @type {Array} + */ var searchWords = []; var i, word; var currentIndex = 0; @@ -1304,6 +1336,38 @@ window.initSearch = function(rawSearchIndex) { var crateSize = 0; + /** + * The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f` + * are arrays with the same length. n[i] contains the name of an item. + * t[i] contains the type of that item (as a small integer that represents an + * offset in `itemTypes`). d[i] contains the description of that item. + * + * q[i] contains the full path of the item, or an empty string indicating + * "same as q[i-1]". + * + * i[i], f[i] are a mystery. + * + * `a` defines aliases with an Array of pairs: [name, offset], where `offset` + * points into the n/t/d/q/i/f arrays. + * + * `doc` contains the description of the crate. + * + * `p` is a mystery and isn't the same length as n/t/d/q/i/f. + * + * @type {{ + * doc: string, + * a: Object, + * n: Array, + * t: Array, + * d: Array, + * q: Array, + * i: Array, + * f: Array>, + * p: Array, + * }} + */ + var crateCorpus = rawSearchIndex[crate]; + searchWords.push(crate); // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. @@ -1313,7 +1377,7 @@ window.initSearch = function(rawSearchIndex) { ty: 1, // == ExternCrate name: crate, path: "", - desc: rawSearchIndex[crate].doc, + desc: crateCorpus.doc, parent: undefined, type: null, id: id, @@ -1324,23 +1388,23 @@ window.initSearch = function(rawSearchIndex) { currentIndex += 1; // an array of (Number) item types - var itemTypes = rawSearchIndex[crate].t; + var itemTypes = crateCorpus.t; // an array of (String) item names - var itemNames = rawSearchIndex[crate].n; + var itemNames = crateCorpus.n; // an array of (String) full paths (or empty string for previous path) - var itemPaths = rawSearchIndex[crate].q; + var itemPaths = crateCorpus.q; // an array of (String) descriptions - var itemDescs = rawSearchIndex[crate].d; + var itemDescs = crateCorpus.d; // an array of (Number) the parent path index + 1 to `paths`, or 0 if none - var itemParentIdxs = rawSearchIndex[crate].i; + var itemParentIdxs = crateCorpus.i; // an array of (Object | null) the type of the function, if any - var itemFunctionSearchTypes = rawSearchIndex[crate].f; + var itemFunctionSearchTypes = crateCorpus.f; // an array of [(Number) item type, // (String) name] - var paths = rawSearchIndex[crate].p; + var paths = crateCorpus.p; // an array of [(String) alias name // [Number] index to items] - var aliases = rawSearchIndex[crate].a; + var aliases = crateCorpus.a; // convert `rawPaths` entries into object form var len = paths.length; @@ -1406,6 +1470,16 @@ window.initSearch = function(rawSearchIndex) { return searchWords; } + /** + * Callback for when the search form is submitted. + * @param {Event} [e] - The event that triggered this call, if any + */ + function onSearchSubmit(e) { + e.preventDefault(); + searchState.clearInputTimeout(); + search(); + } + function registerSearchEvents() { var searchAfter500ms = function() { searchState.clearInputTimeout(); @@ -1421,11 +1495,7 @@ window.initSearch = function(rawSearchIndex) { }; searchState.input.onkeyup = searchAfter500ms; searchState.input.oninput = searchAfter500ms; - document.getElementsByClassName("search-form")[0].onsubmit = function(e) { - e.preventDefault(); - searchState.clearInputTimeout(); - search(); - }; + document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; searchState.input.onchange = function(e) { if (e.target !== document.activeElement) { // To prevent doing anything when it's from a blur event. @@ -1546,7 +1616,7 @@ window.initSearch = function(rawSearchIndex) { }; } - index = buildIndex(rawSearchIndex); + searchWords = buildIndex(rawSearchIndex); registerSearchEvents(); // If there's a search term in the URL, execute the search now. if (searchState.getQueryStringParams().search) { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 606c237aea7d0..d8b3ba92dcba2 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -55,6 +55,12 @@ function removeClass(elem, className) { elem.classList.remove(className); } +/** + * Run a callback for every element of an Array. + * @param {Array} arr - The array to iterate over + * @param {function(?)} func - The callback + * @param {boolean} [reversed] - Whether to iterate in reverse + */ function onEach(arr, func, reversed) { if (arr && arr.length > 0 && func) { var length = arr.length; @@ -76,6 +82,16 @@ function onEach(arr, func, reversed) { return false; } +/** + * Turn an HTMLCollection or a NodeList into an Array, then run a callback + * for every element. This is useful because iterating over an HTMLCollection + * or a "live" NodeList while modifying it can be very slow. + * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection + * https://developer.mozilla.org/en-US/docs/Web/API/NodeList + * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over + * @param {function(?)} func - The callback + * @param {boolean} [reversed] - Whether to iterate in reverse + */ function onEachLazy(lazyArray, func, reversed) { return onEach( Array.prototype.slice.call(lazyArray),