diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index d5a4de86d4d1e..f3a5fbbb44467 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -1195,11 +1195,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(); @@ -1236,23 +1236,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/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 7bd0abbad0192..e92881b104928 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1183,6 +1183,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..5f30d5790a04f 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 = "case_mapping_double_ended", since = "1.59.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 = "case_mapping_double_ended", since = "1.59.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 6e434cf1a8d0f..2b857a6591929 100644 --- a/library/core/tests/char.rs +++ b/library/core/tests/char.rs @@ -103,6 +103,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"); @@ -130,6 +133,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"); 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 } diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 61ab0eb55e6ca..e5cdc4737068a 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -420,3 +420,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/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) } 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<()> { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 961a98a72053f..d300afa313237 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -272,7 +272,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)] @@ -732,6 +735,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 9c849b7789af7..45a436c448710 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -397,6 +397,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { show_type_layout, generate_link_to_definition, call_locations, + no_emit_shared, .. } = options; @@ -516,13 +517,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/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), 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 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`.