From 6729186908bc8f962caede8f7bf548ebccd93f43 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 12 Jun 2024 12:43:49 +0200 Subject: [PATCH 01/12] store the lint levels in the clippy structs themselves --- src/bootstrap/src/core/build_steps/clippy.rs | 60 +++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 6fb37e8cfc4a4..de96648d59d68 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -20,14 +20,12 @@ const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[ "wrong_self_convention", ]; -fn lint_args(builder: &Builder<'_>, ignored_rules: &[&str]) -> Vec { +fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) -> Vec { fn strings<'a>(arr: &'a [&str]) -> impl Iterator + 'a { arr.iter().copied().map(String::from) } - let Subcommand::Clippy { fix, allow_dirty, allow_staged, allow, deny, warn, forbid } = - &builder.config.cmd - else { + let Subcommand::Clippy { fix, allow_dirty, allow_staged, .. } = &builder.config.cmd else { unreachable!("clippy::lint_args can only be called from `clippy` subcommands."); }; @@ -53,12 +51,12 @@ fn lint_args(builder: &Builder<'_>, ignored_rules: &[&str]) -> Vec { args.extend(strings(&["--"])); - if deny.is_empty() && forbid.is_empty() { + if config.deny.is_empty() && config.forbid.is_empty() { args.extend(strings(&["--cap-lints", "warn"])); } let all_args = std::env::args().collect::>(); - args.extend(get_clippy_rules_in_order(&all_args, allow, deny, warn, forbid)); + args.extend(get_clippy_rules_in_order(&all_args, config)); args.extend(ignored_rules.iter().map(|lint| format!("-Aclippy::{}", lint))); args.extend(builder.config.free_args.clone()); @@ -68,21 +66,17 @@ fn lint_args(builder: &Builder<'_>, ignored_rules: &[&str]) -> Vec { /// We need to keep the order of the given clippy lint rules before passing them. /// Since clap doesn't offer any useful interface for this purpose out of the box, /// we have to handle it manually. -pub(crate) fn get_clippy_rules_in_order( - all_args: &[String], - allow_rules: &[String], - deny_rules: &[String], - warn_rules: &[String], - forbid_rules: &[String], -) -> Vec { +fn get_clippy_rules_in_order(all_args: &[String], config: &LintConfig) -> Vec { let mut result = vec![]; for (prefix, item) in - [("-A", allow_rules), ("-D", deny_rules), ("-W", warn_rules), ("-F", forbid_rules)] + [("-A", &config.allow), ("-D", &config.deny), ("-W", &config.warn), ("-F", &config.forbid)] { item.iter().for_each(|v| { let rule = format!("{prefix}{v}"); - let position = all_args.iter().position(|t| t == &rule || t == v).unwrap(); + // Arguments added by bootstrap in LintConfig won't show up in the all_args list, so + // put them at the end of the command line. + let position = all_args.iter().position(|t| t == &rule || t == v).unwrap_or(usize::MAX); result.push((position, rule)); }); } @@ -91,9 +85,29 @@ pub(crate) fn get_clippy_rules_in_order( result.into_iter().map(|v| v.1).collect() } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct LintConfig { + allow: Vec, + warn: Vec, + deny: Vec, + forbid: Vec, +} + +impl LintConfig { + fn new(builder: &Builder<'_>) -> Self { + match builder.config.cmd.clone() { + Subcommand::Clippy { allow, deny, warn, forbid, .. } => { + Self { allow, warn, deny, forbid } + } + _ => unreachable!("LintConfig can only be called from `clippy` subcommands."), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { pub target: TargetSelection, + config: LintConfig, /// Whether to lint only a subset of crates. crates: Vec, } @@ -108,7 +122,8 @@ impl Step for Std { fn make_run(run: RunConfig<'_>) { let crates = std_crates_for_run_make(&run); - run.builder.ensure(Std { target: run.target, crates }); + let config = LintConfig::new(run.builder); + run.builder.ensure(Std { target: run.target, config, crates }); } fn run(self, builder: &Builder<'_>) { @@ -138,7 +153,7 @@ impl Step for Std { run_cargo( builder, cargo, - lint_args(builder, IGNORED_RULES_FOR_STD_AND_RUSTC), + lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), &libstd_stamp(builder, compiler, target), vec![], true, @@ -150,6 +165,7 @@ impl Step for Std { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustc { pub target: TargetSelection, + config: LintConfig, /// Whether to lint only a subset of crates. crates: Vec, } @@ -165,7 +181,8 @@ impl Step for Rustc { fn make_run(run: RunConfig<'_>) { let crates = run.make_run_crates(Alias::Compiler); - run.builder.ensure(Rustc { target: run.target, crates }); + let config = LintConfig::new(run.builder); + run.builder.ensure(Rustc { target: run.target, config, crates }); } /// Lints the compiler. @@ -212,7 +229,7 @@ impl Step for Rustc { run_cargo( builder, cargo, - lint_args(builder, IGNORED_RULES_FOR_STD_AND_RUSTC), + lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), &librustc_stamp(builder, compiler, target), vec![], true, @@ -232,6 +249,7 @@ macro_rules! lint_any { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $name { pub target: TargetSelection, + config: LintConfig, } impl Step for $name { @@ -243,8 +261,10 @@ macro_rules! lint_any { } fn make_run(run: RunConfig<'_>) { + let config = LintConfig::new(run.builder); run.builder.ensure($name { target: run.target, + config, }); } @@ -281,7 +301,7 @@ macro_rules! lint_any { run_cargo( builder, cargo, - lint_args(builder, &[]), + lint_args(builder, &self.config, &[]), &stamp, vec![], true, From 3c6841725c05c3e75a8321b114b2132eb1699fb8 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 12 Jun 2024 13:01:21 +0200 Subject: [PATCH 02/12] define all the clippy lints we check in CI in a step --- src/bootstrap/src/core/build_steps/clippy.rs | 68 +++++++++++++++++++ src/bootstrap/src/core/builder/mod.rs | 1 + .../docker/host-x86_64/mingw-check/Dockerfile | 4 +- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index de96648d59d68..ad2b349bab670 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -102,6 +102,19 @@ impl LintConfig { _ => unreachable!("LintConfig can only be called from `clippy` subcommands."), } } + + fn merge(&self, other: &Self) -> Self { + let merged = |self_attr: &[String], other_attr: &[String]| -> Vec { + self_attr.iter().cloned().chain(other_attr.iter().cloned()).collect() + }; + // This is written this way to ensure we get a compiler error if we add a new field. + Self { + allow: merged(&self.allow, &other.allow), + warn: merged(&self.warn, &other.warn), + deny: merged(&self.deny, &other.deny), + forbid: merged(&self.forbid, &other.forbid), + } + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -339,3 +352,58 @@ lint_any!( Tidy, "src/tools/tidy", "tidy"; TestFloatParse, "src/etc/test-float-parse", "test-float-parse"; ); + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CI { + target: TargetSelection, + config: LintConfig, +} + +impl Step for CI { + type Output = (); + const DEFAULT: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("ci") + } + + fn make_run(run: RunConfig<'_>) { + let config = LintConfig::new(run.builder); + run.builder.ensure(CI { target: run.target, config }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + builder.ensure(Bootstrap { + target: self.target, + config: self.config.merge(&LintConfig { + allow: vec![], + warn: vec![], + deny: vec!["warnings".into()], + forbid: vec![], + }), + }); + let library_clippy_cfg = LintConfig { + allow: vec!["clippy::all".into()], + warn: vec![], + deny: vec!["clippy::correctness".into()], + forbid: vec![], + }; + let compiler_clippy_cfg = LintConfig { + allow: vec!["clippy::all".into()], + warn: vec![], + deny: vec!["clippy::correctness".into(), "clippy::clone_on_ref_ptr".into()], + forbid: vec![], + }; + + builder.ensure(Std { + target: self.target, + config: self.config.merge(&library_clippy_cfg), + crates: vec![], + }); + builder.ensure(Rustc { + target: self.target, + config: self.config.merge(&compiler_clippy_cfg), + crates: vec![], + }); + } +} diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index f1b3cf6da13ec..d59e0fa728807 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -839,6 +839,7 @@ impl<'a> Builder<'a> { clippy::RustInstaller, clippy::TestFloatParse, clippy::Tidy, + clippy::CI, ), Kind::Check | Kind::Fix => describe!( check::Std, diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index fdc8a7310c8fe..f0afb570cc4a0 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -50,9 +50,7 @@ ENV SCRIPT \ python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/check-default-config-profiles.sh && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ - python3 ../x.py clippy bootstrap -Dwarnings && \ - python3 ../x.py clippy library -Aclippy::all -Dclippy::correctness && \ - python3 ../x.py clippy compiler -Aclippy::all -Dclippy::correctness -Dclippy::clone_on_ref_ptr && \ + python3 ../x.py clippy ci && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \ From dad667b6ac5a4d0ed806d53932f5491df418bfaa Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 5 Nov 2024 21:19:28 +0300 Subject: [PATCH 03/12] fix tests --- src/bootstrap/src/core/build_steps/clippy.rs | 12 ++++++------ src/bootstrap/src/core/config/tests.rs | 12 +++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index ad2b349bab670..0884d86cc6d5f 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -66,7 +66,7 @@ fn lint_args(builder: &Builder<'_>, config: &LintConfig, ignored_rules: &[&str]) /// We need to keep the order of the given clippy lint rules before passing them. /// Since clap doesn't offer any useful interface for this purpose out of the box, /// we have to handle it manually. -fn get_clippy_rules_in_order(all_args: &[String], config: &LintConfig) -> Vec { +pub fn get_clippy_rules_in_order(all_args: &[String], config: &LintConfig) -> Vec { let mut result = vec![]; for (prefix, item) in @@ -86,11 +86,11 @@ fn get_clippy_rules_in_order(all_args: &[String], config: &LintConfig) -> Vec, - warn: Vec, - deny: Vec, - forbid: Vec, +pub struct LintConfig { + pub allow: Vec, + pub warn: Vec, + pub deny: Vec, + pub forbid: Vec, } impl LintConfig { diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index f911581b7eece..24f932a172432 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -9,7 +9,7 @@ use serde::Deserialize; use super::flags::Flags; use super::{ChangeIdWrapper, Config, RUSTC_IF_UNCHANGED_ALLOWED_PATHS}; -use crate::core::build_steps::clippy::get_clippy_rules_in_order; +use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; use crate::core::build_steps::llvm; use crate::core::config::{LldMode, Target, TargetSelection, TomlConfig}; @@ -309,9 +309,10 @@ fn order_of_clippy_rules() { ]; let config = Config::parse(Flags::parse(&args)); - let actual = match &config.cmd { + let actual = match config.cmd.clone() { crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { - get_clippy_rules_in_order(&args, &allow, &deny, &warn, &forbid) + let cfg = LintConfig { allow, deny, warn, forbid }; + get_clippy_rules_in_order(&args, &cfg) } _ => panic!("invalid subcommand"), }; @@ -332,9 +333,10 @@ fn clippy_rule_separate_prefix() { vec!["clippy".to_string(), "-A clippy:all".to_string(), "-W clippy::style".to_string()]; let config = Config::parse(Flags::parse(&args)); - let actual = match &config.cmd { + let actual = match config.cmd.clone() { crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { - get_clippy_rules_in_order(&args, &allow, &deny, &warn, &forbid) + let cfg = LintConfig { allow, deny, warn, forbid }; + get_clippy_rules_in_order(&args, &cfg) } _ => panic!("invalid subcommand"), }; From cdd948cbc05f00bcf072808c8659b5fc6e0ab196 Mon Sep 17 00:00:00 2001 From: klensy Date: Tue, 12 Nov 2024 21:43:51 +0300 Subject: [PATCH 04/12] fix clippy warns on windows (not checked by CI) --- src/bootstrap/src/core/build_steps/clean.rs | 2 ++ src/bootstrap/src/utils/helpers.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 040690623a108..d857de96cce73 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -226,6 +226,8 @@ where Err(ref e) if e.kind() == ErrorKind::PermissionDenied => { let m = t!(path.symlink_metadata()); let mut p = m.permissions(); + // this os not unix, so clippy gives FP + #[expect(clippy::permissions_set_readonly_false)] p.set_readonly(false); t!(fs::set_permissions(path, p)); f(path).unwrap_or_else(|e| { diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 7162007e9f0f7..3fb2330469aef 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -145,7 +145,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result< #[cfg(windows)] fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> { - junction::create(&target, &junction) + junction::create(target, junction) } } From 86da4be47f3a0d52b894535a227ee3f2515b9af3 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 13 Nov 2024 10:46:27 -0700 Subject: [PATCH 05/12] rustdoc: use a trie for name-based search Preview and profiler results ---------------------------- Here's some quick profiling in Firefox done on the rust compiler docs: - Before: https://share.firefox.dev/3UPm3M8 - After: https://share.firefox.dev/40LXvYb Here's the results for the node.js profiler: - https://notriddle.com/rustdoc-html-demo-15/trie-perf/index.html Here's a copy that you can use to try it out. Compare it with [the nightly]. Try typing `typecheckercontext` one character at a time, slowly. - https://notriddle.com/rustdoc-html-demo-15/compiler-doc-trie/index.html [the nightly]: https://doc.rust-lang.org/nightly/nightly-rustc/ The fuzzy match algo is based on [Fast String Correction with Levenshtein-Automata] and the corresponding implementation code in [moman] and [Lucene]; the bit-packing representation comes from Lucene, but the actual matcher is more based on `fsc.py`. As suggested in the paper, a trie is used to represent the FSA dictionary. The same trie is used for prefix matching. Substring matching is done with a side table of three-character[^1] windows that point into the trie. [Fast String Correction with Levenshtein-Automata]: https://github.com/tpn/pdfs/blob/master/Fast%20String%20Correction%20with%20Levenshtein-Automata%20(2002)%20(10.1.1.16.652).pdf [Lucene]: https://fossies.org/linux/lucene/lucene/core/src/java/org/apache/lucene/util/automaton/Lev1TParametricDescription.java [moman]: https://gitlab.com/notriddle/moman-rustdoc User-visible changes -------------------- I don't expect anybody to notice anything, but it does cause two changes: - Substring matches, in the middle of a name, only apply if there's three or more characters in the search query. - Levenshtein distance limit now maxes out at two. In the old version, the limit was w/3, so you could get looser matches for queries with 9 or more characters[^1] in them. [^1]: technically utf-16 code units --- src/librustdoc/html/static/js/search.js | 799 ++++++++++++++++--- tests/rustdoc-js-std/deduplication.js | 1 - tests/rustdoc-js-std/path-maxeditdistance.js | 12 +- tests/rustdoc-js/non-english-identifier.js | 26 +- tests/rustdoc-js/path-maxeditdistance.js | 25 +- tests/rustdoc-js/prototype.js | 4 +- 6 files changed, 740 insertions(+), 127 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 4e1bbbbf59d89..41e17d88ed57c 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1106,6 +1106,137 @@ class RoaringBitmapBits { } } +/** + * @typedef {{ + * children: [NameTrie], + * matches: [number], + * }} + */ +class NameTrie { + constructor() { + this.children = []; + this.matches = []; + } + insert(name, id, tailTable) { + this.insertSubstring(name, 0, id, tailTable); + } + insertSubstring(name, substart, id, tailTable) { + const l = name.length; + if (substart === l) { + this.matches.push(id); + } else { + const sb = name.charCodeAt(substart); + let child; + if (this.children[sb] !== undefined) { + child = this.children[sb]; + } else { + child = new NameTrie(); + this.children[sb] = child; + let sste; + if (substart >= 2) { + const tail = name.substring(substart - 2, substart + 1); + if (tailTable.has(tail)) { + sste = tailTable.get(tail); + } else { + sste = []; + tailTable.set(tail, sste); + } + sste.push(child); + } + } + child.insertSubstring(name, substart + 1, id, tailTable); + } + } + search(name, tailTable) { + const results = new Set(); + this.searchSubstringPrefix(name, 0, results); + if (results.size < MAX_RESULTS && name.length >= 3) { + const levParams = name.length >= 6 ? + new Lev2TParametricDescription(name.length) : + new Lev1TParametricDescription(name.length); + this.searchLev(name, 0, levParams, results); + const tail = name.substring(0, 3); + if (tailTable.has(tail)) { + for (const entry of tailTable.get(tail)) { + entry.searchSubstringPrefix(name, 3, results); + } + } else { + console.log(tailTable); + console.log(tail); + } + } + return [...results]; + } + searchSubstringPrefix(name, substart, results) { + const l = name.length; + if (substart === l) { + for (const match of this.matches) { + results.add(match); + } + // breadth-first traversal orders prefix matches by length + let unprocessedChildren = []; + for (const child of this.children) { + if (child) { + unprocessedChildren.push(child); + } + } + let nextSet = []; + while (unprocessedChildren.length !== 0) { + const next = unprocessedChildren.pop(); + for (const child of next.children) { + if (child) { + nextSet.push(child); + } + } + for (const match of next.matches) { + results.add(match); + } + if (unprocessedChildren.length === 0) { + const tmp = unprocessedChildren; + unprocessedChildren = nextSet; + nextSet = tmp; + } + } + } else { + const sb = name.charCodeAt(substart); + if (this.children[sb] !== undefined) { + this.children[sb].searchSubstringPrefix(name, substart + 1, results); + } + } + } + searchLev(name, substart, levParams, results) { + const stack = [[this, 0]]; + const n = levParams.n; + while (stack.length !== 0) { + const [trie, levState] = stack.pop(); + for (const [charCode, child] of trie.children.entries()) { + if (!child) { + continue; + } + const levPos = levParams.getPosition(levState); + const vector = levParams.getVector( + name, + charCode, + levPos, + Math.min(name.length, levPos + (2 * n) + 1), + ); + const newLevState = levParams.transition( + levState, + levPos, + vector, + ); + if (newLevState >= 0) { + stack.push([child, newLevState]); + if (levParams.isAccept(newLevState)) { + for (const match of child.matches) { + results.add(match); + } + } + } + } + } + } +} class DocSearch { constructor(rawSearchIndex, rootPath, searchState) { @@ -1210,6 +1341,20 @@ class DocSearch { */ this.TYPES_POOL = new Map(); + /** + * A trie for finding items by name. + * This is used for edit distance and prefix finding. + * + * @type {NameTrie} + */ + this.nameTrie = new NameTrie(); + + /** + * Find items by 3-substring. This is a map from three-char + * prefixes into lists of subtries. + */ + this.tailTable = new Map(); + /** * @type {Array} */ @@ -1613,6 +1758,7 @@ class DocSearch { // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. // https://mathiasbynens.be/notes/shapes-ics + let normalizedName = crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""); const crateRow = { crate, ty: 3, // == ExternCrate @@ -1627,10 +1773,11 @@ class DocSearch { paramNames: lastParamNames, id, word: crate, - normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), + normalizedName, bitIndex: 0, implDisambiguator: null, }; + this.nameTrie.insert(normalizedName, id, this.tailTable); id += 1; searchIndex.push(crateRow); currentIndex += 1; @@ -1749,6 +1896,7 @@ class DocSearch { // This object should have exactly the same set of fields as the "crateRow" // object defined above. const itemParentIdx = itemParentIdxDecoder.next(); + normalizedName = word.indexOf("_") === -1 ? word : word.replace(/_/g, ""); const row = { crate, ty: itemTypes.charCodeAt(i) - 65, // 65 = "A" @@ -1763,11 +1911,12 @@ class DocSearch { paramNames, id, word, - normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), + normalizedName, bitIndex, implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null, }; + this.nameTrie.insert(normalizedName, id, this.tailTable); id += 1; searchIndex.push(row); lastPath = row.path; @@ -2060,6 +2209,7 @@ class DocSearch { "/index.html#reexport." + name; } else if (type === "primitive" || type === "keyword") { displayPath = ""; + exactPath = ""; href = this.rootPath + path.replace(/::/g, "/") + "/" + type + "." + name + ".html"; } else if (type === "externcrate") { @@ -2075,6 +2225,7 @@ class DocSearch { if (parentType === "primitive") { displayPath = myparent.name + "::"; + exactPath = myparent.name; } else if (type === "structfield" && parentType === "variant") { // Structfields belonging to variants are special: the // final path element is the enum name. @@ -3588,96 +3739,6 @@ class DocSearch { } } - /** - * This function is called in case the query is only one element (with or without generics). - * This element will be compared to arguments' and returned values' items and also to items. - * - * Other important thing to note: since there is only one element, we use edit - * distance for name comparisons. - * - * @param {Row} row - * @param {integer} pos - Position in the `searchIndex`. - * @param {QueryElement} elem - The element from the parsed query. - * @param {Results} results_others - Unqualified results (not in arguments nor in - * returned values). - * @param {Results} results_in_args - Matching arguments results. - * @param {Results} results_returned - Matching returned arguments results. - */ - function handleSingleArg( - row, - pos, - elem, - results_others, - results_in_args, - results_returned, - maxEditDistance, - ) { - if (!row || (filterCrates !== null && row.crate !== filterCrates)) { - return; - } - let path_dist = 0; - const fullId = row.id; - - // fpDist is a minimum possible type distance, where "type distance" is the number of - // atoms in the function not present in the query - const tfpDist = compareTypeFingerprints( - fullId, - parsedQuery.typeFingerprint, - ); - if (tfpDist !== null) { - const in_args = row.type && row.type.inputs - && checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0); - const returned = row.type && row.type.output - && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); - if (in_args) { - results_in_args.max_dist = Math.max(results_in_args.max_dist || 0, tfpDist); - const maxDist = results_in_args.size < MAX_RESULTS ? - (tfpDist + 1) : - results_in_args.max_dist; - addIntoResults(results_in_args, fullId, pos, -1, tfpDist, 0, maxDist); - } - if (returned) { - results_returned.max_dist = Math.max(results_returned.max_dist || 0, tfpDist); - const maxDist = results_returned.size < MAX_RESULTS ? - (tfpDist + 1) : - results_returned.max_dist; - addIntoResults(results_returned, fullId, pos, -1, tfpDist, 0, maxDist); - } - } - - if (!typePassesFilter(elem.typeFilter, row.ty)) { - return; - } - - let index = row.word.indexOf(elem.pathLast); - const normalizedIndex = row.normalizedName.indexOf(elem.pathLast); - if (index === -1 || (index > normalizedIndex && normalizedIndex !== -1)) { - index = normalizedIndex; - } - - if (elem.fullPath.length > 1) { - path_dist = checkPath(elem.pathWithoutLast, row); - if (path_dist === null) { - return; - } - } - - if (parsedQuery.literalSearch) { - if (row.word === elem.pathLast) { - addIntoResults(results_others, fullId, pos, index, 0, path_dist); - } - return; - } - - const dist = editDistance(row.normalizedName, elem.normalizedPathLast, maxEditDistance); - - if (index === -1 && dist > maxEditDistance) { - return; - } - - addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance); - } - /** * This function is called in case the query has more than one element. In this case, it'll * try to match the items which validates all the elements. For `aa -> bb` will look for @@ -3893,23 +3954,79 @@ class DocSearch { } if (parsedQuery.foundElems === 1 && !parsedQuery.hasReturnArrow) { - if (parsedQuery.elems.length === 1) { - const elem = parsedQuery.elems[0]; - const length = this.searchIndex.length; - for (let i = 0, nSearchIndex = length; i < nSearchIndex; ++i) { - // It means we want to check for this element everywhere (in names, args and - // returned). - handleSingleArg( - this.searchIndex[i], - i, - elem, + const elem = parsedQuery.elems[0]; + for (const id of this.nameTrie.search(elem.normalizedPathLast, this.tailTable)) { + const row = this.searchIndex[id]; + if (!typePassesFilter(elem.typeFilter, row.ty) || + (filterCrates !== null && row.crate !== filterCrates)) { + continue; + } + + let pathDist = 0; + if (elem.fullPath.length > 1) { + pathDist = checkPath(elem.pathWithoutLast, row); + if (pathDist === null) { + continue; + } + } + + if (parsedQuery.literalSearch) { + if (row.word === elem.pathLast) { + addIntoResults(results_others, row.id, id, 0, 0, pathDist); + } + } else { + addIntoResults( results_others, - results_in_args, - results_returned, + row.id, + id, + row.normalizedName.indexOf(elem.normalizedPathLast), + editDistance( + row.normalizedName, + elem.normalizedPathLast, + maxEditDistance, + ), + pathDist, maxEditDistance, ); } } + const length = this.searchIndex.length; + for (let i = 0, nSearchIndex = length; i < nSearchIndex; ++i) { + const row = this.searchIndex[i]; + if (filterCrates !== null && row.crate !== filterCrates) { + continue; + } + const tfpDist = compareTypeFingerprints( + row.id, + parsedQuery.typeFingerprint, + ); + if (tfpDist !== null) { + const in_args = row.type && row.type.inputs + && checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0); + const returned = row.type && row.type.output + && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); + if (in_args) { + results_in_args.max_dist = Math.max( + results_in_args.max_dist || 0, + tfpDist, + ); + const maxDist = results_in_args.size < MAX_RESULTS ? + (tfpDist + 1) : + results_in_args.max_dist; + addIntoResults(results_in_args, row.id, i, -1, tfpDist, 0, maxDist); + } + if (returned) { + results_returned.max_dist = Math.max( + results_returned.max_dist || 0, + tfpDist, + ); + const maxDist = results_returned.size < MAX_RESULTS ? + (tfpDist + 1) : + results_returned.max_dist; + addIntoResults(results_returned, row.id, i, -1, tfpDist, 0, maxDist); + } + } + } } else if (parsedQuery.foundElems > 0) { // Sort input and output so that generic type variables go first and // types with generic parameters go last. @@ -4653,3 +4770,477 @@ if (typeof window !== "undefined") { // exports. initSearch(new Map()); } + +// Parts of this code are based on Lucene, which is licensed under the +// Apache/2.0 license. +// More information found here: +// https://fossies.org/linux/lucene/lucene/core/src/java/org/apache/lucene/util/automaton/ +// LevenshteinAutomata.java +class ParametricDescription { + constructor(w, n, minErrors) { + this.w = w; + this.n = n; + this.minErrors = minErrors; + } + isAccept(absState) { + const state = Math.floor(absState / (this.w + 1)); + const offset = absState % (this.w + 1); + return this.w - offset + this.minErrors[state] <= this.n; + } + getPosition(absState) { + return absState % (this.w + 1); + } + getVector(name, charCode, pos, end) { + let vector = 0; + for (let i = pos; i < end; i += 1) { + vector = vector << 1; + if (name.charCodeAt(i) === charCode) { + vector |= 1; + } + } + return vector; + } + unpack(data, index, bitsPerValue) { + const bitLoc = (bitsPerValue * index); + const dataLoc = bitLoc >> 5; + const bitStart = bitLoc & 31; + if (bitStart + bitsPerValue <= 32) { + // not split + return ((data[dataLoc] >> bitStart) & this.MASKS[bitsPerValue - 1]); + } else { + // split + const part = 32 - bitStart; + return ~~(((data[dataLoc] >> bitStart) & this.MASKS[part - 1]) + + ((data[1 + dataLoc] & this.MASKS[bitsPerValue - part - 1]) << part)); + } + } +} +ParametricDescription.prototype.MASKS = new Int32Array([ + 0x1, 0x3, 0x7, 0xF, + 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3F, 0x7FF, 0xFFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, + 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, + 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, +]); + +// The following code was generated with the moman/finenight pkg +// This package is available under the MIT License, see NOTICE.txt +// for more details. +// This class is auto-generated, Please do not modify it directly. +// You should modify the https://gitlab.com/notriddle/createAutomata.py instead. +// The following code was generated with the moman/finenight pkg +// This package is available under the MIT License, see NOTICE.txt +// for more details. +// This class is auto-generated, Please do not modify it directly. +// You should modify https://gitlab.com/notriddle/moman-rustdoc instead. + +class Lev2TParametricDescription extends ParametricDescription { + /** + * @param {number} absState + * @param {number} position + * @param {number} vector + * @returns {number} + */ + transition(absState, position, vector) { + let state = Math.floor(absState / (this.w + 1)); + let offset = absState % (this.w + 1); + + if (position === this.w) { + if (state < 3) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 3) + state; + offset += this.unpack(this.offsetIncrs0, loc, 1); + state = this.unpack(this.toStates0, loc, 2) - 1; + } + } else if (position === this.w - 1) { + if (state < 5) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 5) + state; + offset += this.unpack(this.offsetIncrs1, loc, 1); + state = this.unpack(this.toStates1, loc, 3) - 1; + } + } else if (position === this.w - 2) { + if (state < 13) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 13) + state; + offset += this.unpack(this.offsetIncrs2, loc, 2); + state = this.unpack(this.toStates2, loc, 4) - 1; + } + } else if (position === this.w - 3) { + if (state < 28) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 28) + state; + offset += this.unpack(this.offsetIncrs3, loc, 2); + state = this.unpack(this.toStates3, loc, 5) - 1; + } + } else if (position === this.w - 4) { + if (state < 45) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 45) + state; + offset += this.unpack(this.offsetIncrs4, loc, 3); + state = this.unpack(this.toStates4, loc, 6) - 1; + } + } else { + if (state < 45) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 45) + state; + offset += this.unpack(this.offsetIncrs5, loc, 3); + state = this.unpack(this.toStates5, loc, 6) - 1; + } + } + + if (state === -1) { + // null state + return -1; + } else { + // translate back to abs + return Math.imul(state, this.w + 1) + offset; + } + } + + // state map + // 0 -> [(0, 0)] + // 1 -> [(0, 1)] + // 2 -> [(0, 2)] + // 3 -> [(0, 1), (1, 1)] + // 4 -> [(0, 2), (1, 2)] + // 5 -> [(0, 1), (1, 1), (2, 1)] + // 6 -> [(0, 2), (1, 2), (2, 2)] + // 7 -> [(0, 1), (2, 1)] + // 8 -> [(0, 1), (2, 2)] + // 9 -> [(0, 2), (2, 1)] + // 10 -> [(0, 2), (2, 2)] + // 11 -> [t(0, 1), (0, 1), (1, 1), (2, 1)] + // 12 -> [t(0, 2), (0, 2), (1, 2), (2, 2)] + // 13 -> [(0, 2), (1, 2), (2, 2), (3, 2)] + // 14 -> [(0, 1), (1, 1), (3, 2)] + // 15 -> [(0, 1), (2, 2), (3, 2)] + // 16 -> [(0, 1), (3, 2)] + // 17 -> [(0, 1), t(1, 2), (2, 2), (3, 2)] + // 18 -> [(0, 2), (1, 2), (3, 1)] + // 19 -> [(0, 2), (1, 2), (3, 2)] + // 20 -> [(0, 2), (1, 2), t(1, 2), (2, 2), (3, 2)] + // 21 -> [(0, 2), (2, 1), (3, 1)] + // 22 -> [(0, 2), (2, 2), (3, 2)] + // 23 -> [(0, 2), (3, 1)] + // 24 -> [(0, 2), (3, 2)] + // 25 -> [(0, 2), t(1, 2), (1, 2), (2, 2), (3, 2)] + // 26 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (3, 2)] + // 27 -> [t(0, 2), (0, 2), (1, 2), (3, 1)] + // 28 -> [(0, 2), (1, 2), (2, 2), (3, 2), (4, 2)] + // 29 -> [(0, 2), (1, 2), (2, 2), (4, 2)] + // 30 -> [(0, 2), (1, 2), (2, 2), t(2, 2), (3, 2), (4, 2)] + // 31 -> [(0, 2), (1, 2), (3, 2), (4, 2)] + // 32 -> [(0, 2), (1, 2), (4, 2)] + // 33 -> [(0, 2), (1, 2), t(1, 2), (2, 2), (3, 2), (4, 2)] + // 34 -> [(0, 2), (1, 2), t(2, 2), (2, 2), (3, 2), (4, 2)] + // 35 -> [(0, 2), (2, 1), (4, 2)] + // 36 -> [(0, 2), (2, 2), (3, 2), (4, 2)] + // 37 -> [(0, 2), (2, 2), (4, 2)] + // 38 -> [(0, 2), (3, 2), (4, 2)] + // 39 -> [(0, 2), (4, 2)] + // 40 -> [(0, 2), t(1, 2), (1, 2), (2, 2), (3, 2), (4, 2)] + // 41 -> [(0, 2), t(2, 2), (2, 2), (3, 2), (4, 2)] + // 42 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (3, 2), (4, 2)] + // 43 -> [t(0, 2), (0, 2), (1, 2), (2, 2), (4, 2)] + // 44 -> [t(0, 2), (0, 2), (1, 2), (2, 2), t(2, 2), (3, 2), (4, 2)] + + + /** @param {number} w - length of word being checked */ + constructor(w) { + super(w, 2, new Int32Array([ + 0,1,2,0,1,-1,0,-1,0,-1,0,-1,0,-1,-1,-1,-1,-1,-2,-1,-1,-2,-1,-2, + -1,-1,-1,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2, + ])); + } +} + +Lev2TParametricDescription.prototype.toStates0 = /*2 bits per value */ new Int32Array([ + 0xe, +]); +Lev2TParametricDescription.prototype.offsetIncrs0 = /*1 bits per value */ new Int32Array([ + 0x0, +]); + +Lev2TParametricDescription.prototype.toStates1 = /*3 bits per value */ new Int32Array([ + 0x1a688a2c, +]); +Lev2TParametricDescription.prototype.offsetIncrs1 = /*1 bits per value */ new Int32Array([ + 0x3e0, +]); + +Lev2TParametricDescription.prototype.toStates2 = /*4 bits per value */ new Int32Array([ + 0x70707054,0xdc07035,0x3dd3a3a,0x2323213a, + 0x15435223,0x22545432,0x5435, +]); +Lev2TParametricDescription.prototype.offsetIncrs2 = /*2 bits per value */ new Int32Array([ + 0x80000,0x55582088,0x55555555,0x55, +]); + +Lev2TParametricDescription.prototype.toStates3 = /*5 bits per value */ new Int32Array([ + 0x1c0380a4,0x700a570,0xca529c0,0x180a00, + 0xa80af180,0xc5498e60,0x5a546398,0x8c4300e8, + 0xac18c601,0xd8d43501,0x863500ad,0x51976d6a, + 0x8ca0180a,0xc3501ac2,0xb0c5be16,0x76dda8a5, + 0x18c4519,0xc41294a,0xe248d231,0x1086520c, + 0xce31ac42,0x13946358,0x2d0348c4,0x6732d494, + 0x1ad224a5,0xd635ad4b,0x520c4139,0xce24948, + 0x22110a52,0x58ce729d,0xc41394e3,0x941cc520, + 0x90e732d4,0x4729d224,0x39ce35ad, +]); +Lev2TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([ + 0x80000,0xc0c830,0x300f3c30,0x2200fcff, + 0xcaa00a08,0x3c2200a8,0xa8fea00a,0x55555555, + 0x55555555,0x55555555,0x55555555,0x55555555, + 0x55555555,0x55555555, +]); + +Lev2TParametricDescription.prototype.toStates4 = /*6 bits per value */ new Int32Array([ + 0x801c0144,0x1453803,0x14700038,0xc0005145, + 0x1401,0x14,0x140000,0x0, + 0x510000,0x6301f007,0x301f00d1,0xa186178, + 0xc20ca0c3,0xc20c30,0xc30030c,0xc00c00cd, + 0xf0c00c30,0x4c054014,0xc30944c3,0x55150c34, + 0x8300550,0x430c0143,0x50c31,0xc30850c, + 0xc3143000,0x50053c50,0x5130d301,0x850d30c2, + 0x30a08608,0xc214414,0x43142145,0x21450031, + 0x1400c314,0x4c143145,0x32832803,0x28014d6c, + 0xcd34a0c3,0x1c50c76,0x1c314014,0x430c30c3, + 0x1431,0xc300500,0xca00d303,0xd36d0e40, + 0x90b0e400,0xcb2abb2c,0x70c20ca1,0x2c32ca2c, + 0xcd2c70cb,0x31c00c00,0x34c2c32c,0x5583280, + 0x558309b7,0x6cd6ca14,0x430850c7,0x51c51401, + 0x1430c714,0xc3087,0x71451450,0xca00d30, + 0xc26dc156,0xb9071560,0x1cb2abb2,0xc70c2144, + 0xb1c51ca1,0x1421c70c,0xc51c00c3,0x30811c51, + 0x24324308,0xc51031c2,0x70820820,0x5c33830d, + 0xc33850c3,0x30c30c30,0xc30c31c,0x451450c3, + 0x20c20c20,0xda0920d,0x5145914f,0x36596114, + 0x51965865,0xd9643653,0x365a6590,0x51964364, + 0x43081505,0x920b2032,0x2c718b28,0xd7242249, + 0x35cb28b0,0x2cb3872c,0x972c30d7,0xb0c32cb2, + 0x4e1c75c,0xc80c90c2,0x62ca2482,0x4504171c, + 0xd65d9610,0x33976585,0xd95cb5d,0x4b5ca5d7, + 0x73975c36,0x10308138,0xc2245105,0x41451031, + 0x14e24208,0xc35c3387,0x51453851,0x1c51c514, + 0xc70c30c3,0x20451450,0x14f1440c,0x4f0da092, + 0x4513d41,0x6533944d,0x1350e658,0xe1545055, + 0x64365a50,0x5519383,0x51030815,0x28920718, + 0x441c718b,0x714e2422,0x1c35cb28,0x4e1c7387, + 0xb28e1c51,0x5c70c32c,0xc204e1c7,0x81c61440, + 0x1c62ca24,0xd04503ce,0x85d63944,0x39338e65, + 0x8e154387,0x364b5ca3,0x38739738, +]); +Lev2TParametricDescription.prototype.offsetIncrs4 = /*3 bits per value */ new Int32Array([ + 0x10000000,0xc00000,0x60061,0x400, + 0x0,0x80010008,0x249248a4,0x8229048, + 0x2092,0x6c3603,0xb61b6c30,0x6db6036d, + 0xdb6c0,0x361b0180,0x91b72000,0xdb11b71b, + 0x6db6236,0x1008200,0x12480012,0x24924906, + 0x48200049,0x80410002,0x24000900,0x4924a489, + 0x10822492,0x20800125,0x48360,0x9241b692, + 0x6da4924,0x40009268,0x241b010,0x291b4900, + 0x6d249249,0x49493423,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x92492492, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x92492492, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x2492, +]); + +Lev2TParametricDescription.prototype.toStates5 = /*6 bits per value */ new Int32Array([ + 0x801c0144,0x1453803,0x14700038,0xc0005145, + 0x1401,0x14,0x140000,0x0, + 0x510000,0x4e00e007,0xe0051,0x3451451c, + 0xd015000,0x30cd0000,0xc30c30c,0xc30c30d4, + 0x40c30c30,0x7c01c014,0xc03458c0,0x185e0c07, + 0x2830c286,0x830c3083,0xc30030,0x33430c, + 0x30c3003,0x70051030,0x16301f00,0x8301f00d, + 0x30a18617,0xc20ca0c,0x431420c3,0xb1450c51, + 0x14314315,0x4f143145,0x34c05401,0x4c30944c, + 0x55150c3,0x30830055,0x1430c014,0xc00050c3, + 0xc30850,0xc314300,0x150053c5,0x25130d30, + 0x5430d30c,0xc0354154,0x300d0c90,0x1cb2cd0c, + 0xc91cb0c3,0x72c30cb2,0x14f1cb2c,0xc34c0540, + 0x34c30944,0x82182214,0x851050c2,0x50851430, + 0x1400c50c,0x30c5085,0x50c51450,0x150053c, + 0xc25130d3,0x8850d30,0x1430a086,0x450c2144, + 0x51cb1c21,0x1c91c70c,0xc71c314b,0x34c1cb1, + 0x6c328328,0xc328014d,0x76cd34a0,0x1401c50c, + 0xc31c3140,0x31430c30,0x14,0x30c3005, + 0xa0ca00d3,0x535b0c,0x4d2830ca,0x514369b3, + 0xc500d01,0x5965965a,0x30d46546,0x6435030c, + 0x8034c659,0xdb439032,0x2c390034,0xcaaecb24, + 0x30832872,0xcb28b1c,0x4b1c32cb,0x70030033, + 0x30b0cb0c,0xe40ca00d,0x400d36d0,0xb2c90b0e, + 0xca1cb2ab,0xa2c70c20,0x6575d95c,0x4315b5ce, + 0x95c53831,0x28034c5d,0x9b705583,0xa1455830, + 0xc76cd6c,0x40143085,0x71451c51,0x871430c, + 0x450000c3,0xd3071451,0x1560ca00,0x560c26dc, + 0xb35b2851,0xc914369,0x1a14500d,0x46593945, + 0xcb2c939,0x94507503,0x328034c3,0x9b70558, + 0xe41c5583,0x72caaeca,0x1c308510,0xc7147287, + 0x50871c32,0x1470030c,0xd307147,0xc1560ca0, + 0x1560c26d,0xabb2b907,0x21441cb2,0x38a1c70c, + 0x8e657394,0x314b1c93,0x39438738,0x43083081, + 0x31c22432,0x820c510,0x830d7082,0x50c35c33, + 0xc30c338,0xc31c30c3,0x50c30c30,0xc204514, + 0x890c90c2,0x31440c70,0xa8208208,0xea0df0c3, + 0x8a231430,0xa28a28a2,0x28a28a1e,0x1861868a, + 0x48308308,0xc3682483,0x14516453,0x4d965845, + 0xd4659619,0x36590d94,0xd969964,0x546590d9, + 0x20c20541,0x920d20c,0x5914f0da,0x96114514, + 0x65865365,0xe89d3519,0x99e7a279,0x9e89e89e, + 0x81821827,0xb2032430,0x18b28920,0x422492c7, + 0xb28b0d72,0x3872c35c,0xc30d72cb,0x32cb2972, + 0x1c75cb0c,0xc90c204e,0xa2482c80,0x24b1c62c, + 0xc3a89089,0xb0ea2e42,0x9669a31c,0xa4966a28, + 0x59a8a269,0x8175e7a,0xb203243,0x718b2892, + 0x4114105c,0x17597658,0x74ce5d96,0x5c36572d, + 0xd92d7297,0xe1ce5d70,0xc90c204,0xca2482c8, + 0x4171c62,0x5d961045,0x976585d6,0x79669533, + 0x964965a2,0x659689e6,0x308175e7,0x24510510, + 0x451031c2,0xe2420841,0x5c338714,0x453851c3, + 0x51c51451,0xc30c31c,0x451450c7,0x41440c20, + 0xc708914,0x82105144,0xf1c58c90,0x1470ea0d, + 0x61861863,0x8a1e85e8,0x8687a8a2,0x3081861, + 0x24853c51,0x5053c368,0x1341144f,0x96194ce5, + 0x1544d439,0x94385514,0xe0d90d96,0x5415464, + 0x4f1440c2,0xf0da0921,0x4513d414,0x533944d0, + 0x350e6586,0x86082181,0xe89e981d,0x18277689, + 0x10308182,0x89207185,0x41c718b2,0x14e24224, + 0xc35cb287,0xe1c73871,0x28e1c514,0xc70c32cb, + 0x204e1c75,0x1c61440c,0xc62ca248,0x90891071, + 0x2e41c58c,0xa31c70ea,0xe86175e7,0xa269a475, + 0x5e7a57a8,0x51030817,0x28920718,0xf38718b, + 0xe5134114,0x39961758,0xe1ce4ce,0x728e3855, + 0x5ce0d92d,0xc204e1ce,0x81c61440,0x1c62ca24, + 0xd04503ce,0x85d63944,0x75338e65,0x5d86075e, + 0x89e69647,0x75e76576, +]); +Lev2TParametricDescription.prototype.offsetIncrs5 = /*3 bits per value */ new Int32Array([ + 0x10000000,0xc00000,0x60061,0x400, + 0x0,0x60000008,0x6b003080,0xdb6ab6db, + 0x2db6,0x800400,0x49245240,0x11482412, + 0x104904,0x40020000,0x92292000,0xa4b25924, + 0x9649658,0xd80c000,0xdb0c001b,0x80db6d86, + 0x6db01b6d,0xc0600003,0x86000d86,0x6db6c36d, + 0xddadb6ed,0x300001b6,0x6c360,0xe37236e4, + 0x46db6236,0xdb6c,0x361b018,0xb91b7200, + 0x6dbb1b71,0x6db763,0x20100820,0x61248001, + 0x92492490,0x24820004,0x8041000,0x92400090, + 0x24924830,0x555b6a49,0x2080012,0x20004804, + 0x49252449,0x84112492,0x4000928,0x240201, + 0x92922490,0x58924924,0x49456,0x120d8082, + 0x6da4800,0x69249249,0x249a01b,0x6c04100, + 0x6d240009,0x92492483,0x24d5adb4,0x60208001, + 0x92000483,0x24925236,0x6846da49,0x10400092, + 0x241b0,0x49291b49,0x636d2492,0x92494935, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x92492492, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x92492492, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x92492492, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x92492492, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924,0x49249249, + 0x92492492,0x24924924,0x49249249,0x92492492, + 0x24924924,0x49249249,0x92492492,0x24924924, + 0x49249249,0x92492492,0x24924924, +]); + +class Lev1TParametricDescription extends ParametricDescription { + /** + * @param {number} absState + * @param {number} position + * @param {number} vector + * @returns {number} + */ + transition(absState, position, vector) { + let state = Math.floor(absState / (this.w + 1)); + let offset = absState % (this.w + 1); + + if (position === this.w) { + if (state < 2) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 2) + state; + offset += this.unpack(this.offsetIncrs0, loc, 1); + state = this.unpack(this.toStates0, loc, 2) - 1; + } + } else if (position === this.w - 1) { + if (state < 3) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 3) + state; + offset += this.unpack(this.offsetIncrs1, loc, 1); + state = this.unpack(this.toStates1, loc, 2) - 1; + } + } else if (position === this.w - 2) { + if (state < 6) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 6) + state; + offset += this.unpack(this.offsetIncrs2, loc, 2); + state = this.unpack(this.toStates2, loc, 3) - 1; + } + } else { + if (state < 6) { // eslint-disable-line no-lonely-if + const loc = Math.imul(vector, 6) + state; + offset += this.unpack(this.offsetIncrs3, loc, 2); + state = this.unpack(this.toStates3, loc, 3) - 1; + } + } + + if (state === -1) { + // null state + return -1; + } else { + // translate back to abs + return Math.imul(state, this.w + 1) + offset; + } + } + + // state map + // 0 -> [(0, 0)] + // 1 -> [(0, 1)] + // 2 -> [(0, 1), (1, 1)] + // 3 -> [(0, 1), (1, 1), (2, 1)] + // 4 -> [(0, 1), (2, 1)] + // 5 -> [t(0, 1), (0, 1), (1, 1), (2, 1)] + + + /** @param {number} w - length of word being checked */ + constructor(w) { + super(w, 1, new Int32Array([0,1,0,-1,-1,-1])); + } +} + +Lev1TParametricDescription.prototype.toStates0 = /*2 bits per value */ new Int32Array([ + 0x2, +]); +Lev1TParametricDescription.prototype.offsetIncrs0 = /*1 bits per value */ new Int32Array([ + 0x0, +]); + +Lev1TParametricDescription.prototype.toStates1 = /*2 bits per value */ new Int32Array([ + 0xa43, +]); +Lev1TParametricDescription.prototype.offsetIncrs1 = /*1 bits per value */ new Int32Array([ + 0x38, +]); + +Lev1TParametricDescription.prototype.toStates2 = /*3 bits per value */ new Int32Array([ + 0x12180003,0xb45a4914,0x69, +]); +Lev1TParametricDescription.prototype.offsetIncrs2 = /*2 bits per value */ new Int32Array([ + 0x558a0000,0x5555, +]); + +Lev1TParametricDescription.prototype.toStates3 = /*3 bits per value */ new Int32Array([ + 0x900c0003,0xa1904864,0x45a49169,0x5a6d196a, + 0x9634, +]); +Lev1TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([ + 0xa0fc0000,0x5555ba08,0x55555555, +]); diff --git a/tests/rustdoc-js-std/deduplication.js b/tests/rustdoc-js-std/deduplication.js index 51279dd5ed467..95049d0a174db 100644 --- a/tests/rustdoc-js-std/deduplication.js +++ b/tests/rustdoc-js-std/deduplication.js @@ -5,6 +5,5 @@ const EXPECTED = { 'others': [ { 'path': 'std::f32', 'name': 'is_nan' }, { 'path': 'std::f64', 'name': 'is_nan' }, - { 'path': 'std::option::Option', 'name': 'is_none' }, ], }; diff --git a/tests/rustdoc-js-std/path-maxeditdistance.js b/tests/rustdoc-js-std/path-maxeditdistance.js index 632df658f756d..af71713f05581 100644 --- a/tests/rustdoc-js-std/path-maxeditdistance.js +++ b/tests/rustdoc-js-std/path-maxeditdistance.js @@ -3,16 +3,8 @@ const FILTER_CRATE = "std"; const EXPECTED = [ { query: 'vec::intoiterator', - others: [ - // trait std::iter::IntoIterator is not the first result - { 'path': 'std::vec', 'name': 'IntoIter' }, - { 'path': 'std::vec::Vec', 'name': 'into_iter' }, - { 'path': 'std::vec::Drain', 'name': 'into_iter' }, - { 'path': 'std::vec::IntoIter', 'name': 'into_iter' }, - { 'path': 'std::vec::ExtractIf', 'name': 'into_iter' }, - { 'path': 'std::vec::Splice', 'name': 'into_iter' }, - { 'path': 'std::collections::vec_deque::VecDeque', 'name': 'into_iter' }, - ], + // trait std::iter::IntoIterator is not the first result + others: [], }, { query: 'vec::iter', diff --git a/tests/rustdoc-js/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js index 6a2c4cc637c96..f2180b4c75530 100644 --- a/tests/rustdoc-js/non-english-identifier.js +++ b/tests/rustdoc-js/non-english-identifier.js @@ -133,22 +133,34 @@ const EXPECTED = [ path: "non_english_identifier", href: "../non_english_identifier/trait.加法.html", desc: "Add" - }, + }], + in_args: [{ + name: "加上", + path: "non_english_identifier::加法", + href: "../non_english_identifier/trait.加法.html#tymethod.加上", + }], + returned: [], + }, + { // levensthein and substring checking only kick in at three characters + query: '加法宏', + others: [ { name: "中文名称的加法宏", path: "non_english_identifier", href: "../non_english_identifier/macro.中文名称的加法宏.html", - }, + }], + in_args: [], + returned: [], + }, + { // levensthein and substring checking only kick in at three characters + query: '加法A', + others: [ { name: "中文名称的加法API", path: "non_english_identifier", href: "../non_english_identifier/fn.中文名称的加法API.html", }], - in_args: [{ - name: "加上", - path: "non_english_identifier::加法", - href: "../non_english_identifier/trait.加法.html#tymethod.加上", - }], + in_args: [], returned: [], }, { // Extensive type-based search is still buggy, experimental & work-in-progress. diff --git a/tests/rustdoc-js/path-maxeditdistance.js b/tests/rustdoc-js/path-maxeditdistance.js index 73b24a6dddf0a..cf700193ac468 100644 --- a/tests/rustdoc-js/path-maxeditdistance.js +++ b/tests/rustdoc-js/path-maxeditdistance.js @@ -14,21 +14,38 @@ const EXPECTED = [ ], }, { - // swap br/rb; that's edit distance 2, where maxPathEditDistance = 3 (11 / 3) + // swap br/rb; that's edit distance 1, where maxPathEditDistance = 2 'query': 'arbacadarba::hocuspocusprestidigitation', 'others': [ { 'path': 'abracadabra', 'name': 'HocusPocusPrestidigitation' }, ], }, { - // truncate 5 chars, where maxEditDistance = 7 (21 / 3) - 'query': 'abracadarba::hocusprestidigitation', + // swap p/o o/p, that's also edit distance 1 + 'query': 'abracadabra::hocusopcusprestidigitation', 'others': [ { 'path': 'abracadabra', 'name': 'HocusPocusPrestidigitation' }, ], }, { - // truncate 9 chars, where maxEditDistance = 5 (17 / 3) + // swap p/o o/p and gi/ig, that's edit distance 2 + 'query': 'abracadabra::hocusopcusprestidiigtation', + 'others': [ + { 'path': 'abracadabra', 'name': 'HocusPocusPrestidigitation' }, + ], + }, + { + // swap p/o o/p, gi/ig, and ti/it, that's edit distance 3 and not shown (we stop at 2) + 'query': 'abracadabra::hocusopcusprestidiigtaiton', + 'others': [], + }, + { + // truncate 5 chars, where maxEditDistance = 2 + 'query': 'abracadarba::hocusprestidigitation', + 'others': [], + }, + { + // truncate 9 chars, where maxEditDistance = 2 'query': 'abracadarba::hprestidigitation', 'others': [], }, diff --git a/tests/rustdoc-js/prototype.js b/tests/rustdoc-js/prototype.js index da72fdce3db93..0862acd8ffa89 100644 --- a/tests/rustdoc-js/prototype.js +++ b/tests/rustdoc-js/prototype.js @@ -9,7 +9,9 @@ const EXPECTED = [ }, { 'query': '__proto__', - 'others': [], + 'others': [ + {"path": "", "name": "prototype"}, + ], 'returned': [], 'in_args': [], }, From 1d133993eda7434392569f5e5ea8136afe6d5d8c Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 13 Nov 2024 14:55:06 -0700 Subject: [PATCH 06/12] Remove console.log --- src/librustdoc/html/static/js/search.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 41e17d88ed57c..1817492a7d6fb 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1160,9 +1160,6 @@ class NameTrie { for (const entry of tailTable.get(tail)) { entry.searchSubstringPrefix(name, 3, results); } - } else { - console.log(tailTable); - console.log(tail); } } return [...results]; From e534f47e955d3ab50191acb2fa4874bb4fb1cde6 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 13 Nov 2024 15:26:57 -0700 Subject: [PATCH 07/12] Add descriptive comment for NameTrie --- src/librustdoc/html/static/js/search.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1817492a7d6fb..6c2b264063d24 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1107,6 +1107,25 @@ class RoaringBitmapBits { } /** + * A prefix tree, used for name-based search. + * + * This data structure is used to drive prefix matches, + * such as matching the query "link" to `LinkedList`, + * and Lev-distance matches, such as matching the + * query "hahsmap" to `HashMap`. Substring matches, + * such as "list" to `LinkedList`, are done with a + * tailTable that deep-links into this trie. + * + * children + * : A [sparse array] of subtrees. The array index + * is a charCode. + * + * [sparse array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/ + * Indexed_collections#sparse_arrays + * + * matches + * : A list of search index IDs for this node. + * * @typedef {{ * children: [NameTrie], * matches: [number], From 2280d8ea0028bf163b1d9ac25514614823843140 Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 20 Oct 2024 04:22:07 -0700 Subject: [PATCH 08/12] Provide borrow-instead-of-move suggestions for calls of fn-like items from other crates This also downgrades its applicability to MaybeIncorrect. Its suggestion can result in ill-typed code when the type parameter it suggests providing a different generic argument for appears elsewhere in the callee's signature or predicates. --- .../src/diagnostics/conflict_errors.rs | 79 ++++++++++--------- .../suggest-borrow-for-generic-arg-aux.rs | 23 ++++++ .../suggest-borrow-for-generic-arg.fixed | 22 ++++++ .../moves/suggest-borrow-for-generic-arg.rs | 22 ++++++ .../suggest-borrow-for-generic-arg.stderr | 63 +++++++++++++++ 5 files changed, 170 insertions(+), 39 deletions(-) create mode 100644 tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs create mode 100644 tests/ui/moves/suggest-borrow-for-generic-arg.fixed create mode 100644 tests/ui/moves/suggest-borrow-for-generic-arg.rs create mode 100644 tests/ui/moves/suggest-borrow-for-generic-arg.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index e5b28289faa4e..ede1e815f4df0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -440,47 +440,46 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } else { (None, &[][..], 0) }; + let ty = place.ty(self.body, self.infcx.tcx).ty; - // If the moved value is a mut reference, it is used in a - // generic function and it's type is a generic param, it can be - // reborrowed to avoid moving. - // for example: - // struct Y(u32); - // x's type is '& mut Y' and it is used in `fn generic(x: T) {}`. + let mut can_suggest_clone = true; if let Some(def_id) = def_id - && self.infcx.tcx.def_kind(def_id).is_fn_like() && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) - && let Some(arg) = self - .infcx - .tcx - .fn_sig(def_id) - .skip_binder() - .skip_binder() - .inputs() - .get(pos + offset) - && let ty::Param(_) = arg.kind() { - let place = &self.move_data.move_paths[mpi].place; - let ty = place.ty(self.body, self.infcx.tcx).ty; - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { + // The move occurred as one of the arguments to a function call. Is that + // argument generic? `def_id` can't be a closure here, so using `fn_sig` is fine + let arg_param = if self.infcx.tcx.def_kind(def_id).is_fn_like() + && let Some(arg_ty) = self + .infcx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_binder() + .inputs() + .get(pos + offset) + && let ty::Param(arg_param) = arg_ty.kind() + { + Some(arg_param) + } else { + None + }; + + // If the moved value is a mut reference, it is used in a + // generic function and it's type is a generic param, it can be + // reborrowed to avoid moving. + // for example: + // struct Y(u32); + // x's type is '& mut Y' and it is used in `fn generic(x: T) {}`. + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() + && arg_param.is_some() + { *has_suggest_reborrow = true; self.suggest_reborrow(err, expr.span, moved_place); return; } - } - let mut can_suggest_clone = true; - if let Some(def_id) = def_id - && let Some(local_def_id) = def_id.as_local() - && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id) - && let Some(fn_sig) = node.fn_sig() - && let Some(ident) = node.ident() - && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) - && let Some(arg) = fn_sig.decl.inputs.get(pos + offset) - { let mut is_mut = false; - if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = arg.kind - && let Res::Def(DefKind::TyParam, param_def_id) = path.res + if let Some(param) = arg_param && self .infcx .tcx @@ -497,10 +496,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.infcx.tcx.get_diagnostic_item(sym::BorrowMut), ] .contains(&Some(predicate.def_id())) - && let ty::Param(param) = predicate.self_ty().kind() - && let generics = self.infcx.tcx.generics_of(def_id) - && let param = generics.type_param(*param, self.infcx.tcx) - && param.def_id == param_def_id + && *predicate.self_ty().kind() == ty::Param(*param) { if [ self.infcx.tcx.get_diagnostic_item(sym::AsMut), @@ -522,10 +518,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { expr.span.shrink_to_lo(), "borrow the value to avoid moving it", format!("&{}", if is_mut { "mut " } else { "" }), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); can_suggest_clone = is_mut; - } else { + } else if let Some(local_def_id) = def_id.as_local() + && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id) + && let Some(fn_decl) = node.fn_decl() + && let Some(ident) = node.ident() + && let Some(arg) = fn_decl.inputs.get(pos + offset) + { + // If we can't suggest borrowing in the call, but the function definition + // is local, instead offer changing the function to borrow that argument. let mut span: MultiSpan = arg.span.into(); span.push_span_label( arg.span, @@ -546,8 +549,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } } - let place = &self.move_data.move_paths[mpi].place; - let ty = place.ty(self.body, self.infcx.tcx).ty; if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) = diff --git a/tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs b/tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs new file mode 100644 index 0000000000000..62e8510cf4dea --- /dev/null +++ b/tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs @@ -0,0 +1,23 @@ +//! auxiliary definitons for suggest-borrow-for-generic-arg.rs, to ensure the suggestion works on +//! functions defined in other crates. + +use std::borrow::{Borrow, BorrowMut}; +use std::convert::{AsMut, AsRef}; +pub struct Bar; + +impl AsRef for Bar { + fn as_ref(&self) -> &Bar { + self + } +} + +impl AsMut for Bar { + fn as_mut(&mut self) -> &mut Bar { + self + } +} + +pub fn foo>(_: T) {} +pub fn qux>(_: T) {} +pub fn bat>(_: T) {} +pub fn baz>(_: T) {} diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.fixed b/tests/ui/moves/suggest-borrow-for-generic-arg.fixed new file mode 100644 index 0000000000000..3f711924d1e9c --- /dev/null +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.fixed @@ -0,0 +1,22 @@ +//! Test suggetions to borrow generic arguments instead of moving when all predicates hold after +//! substituting in the reference type. +//@ run-rustfix +//@ aux-build:suggest-borrow-for-generic-arg-aux.rs + +#![allow(unused_mut)] +extern crate suggest_borrow_for_generic_arg_aux as aux; + +pub fn main() { + let bar = aux::Bar; + aux::foo(&bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value + let mut bar = aux::Bar; + aux::qux(&mut bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value + let bar = aux::Bar; + aux::bat(&bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value + let mut bar = aux::Bar; + aux::baz(&mut bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value +} diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.rs b/tests/ui/moves/suggest-borrow-for-generic-arg.rs new file mode 100644 index 0000000000000..eb6abcdf0d570 --- /dev/null +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.rs @@ -0,0 +1,22 @@ +//! Test suggetions to borrow generic arguments instead of moving when all predicates hold after +//! substituting in the reference type. +//@ run-rustfix +//@ aux-build:suggest-borrow-for-generic-arg-aux.rs + +#![allow(unused_mut)] +extern crate suggest_borrow_for_generic_arg_aux as aux; + +pub fn main() { + let bar = aux::Bar; + aux::foo(bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value + let mut bar = aux::Bar; + aux::qux(bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value + let bar = aux::Bar; + aux::bat(bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value + let mut bar = aux::Bar; + aux::baz(bar); //~ HELP borrow the value + let _baa = bar; //~ ERROR use of moved value +} diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.stderr b/tests/ui/moves/suggest-borrow-for-generic-arg.stderr new file mode 100644 index 0000000000000..79602c4af4aa4 --- /dev/null +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.stderr @@ -0,0 +1,63 @@ +error[E0382]: use of moved value: `bar` + --> $DIR/suggest-borrow-for-generic-arg.rs:12:16 + | +LL | let bar = aux::Bar; + | --- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait +LL | aux::foo(bar); + | --- value moved here +LL | let _baa = bar; + | ^^^ value used here after move + | +help: borrow the value to avoid moving it + | +LL | aux::foo(&bar); + | + + +error[E0382]: use of moved value: `bar` + --> $DIR/suggest-borrow-for-generic-arg.rs:15:16 + | +LL | let mut bar = aux::Bar; + | ------- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait +LL | aux::qux(bar); + | --- value moved here +LL | let _baa = bar; + | ^^^ value used here after move + | +help: borrow the value to avoid moving it + | +LL | aux::qux(&mut bar); + | ++++ + +error[E0382]: use of moved value: `bar` + --> $DIR/suggest-borrow-for-generic-arg.rs:18:16 + | +LL | let bar = aux::Bar; + | --- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait +LL | aux::bat(bar); + | --- value moved here +LL | let _baa = bar; + | ^^^ value used here after move + | +help: borrow the value to avoid moving it + | +LL | aux::bat(&bar); + | + + +error[E0382]: use of moved value: `bar` + --> $DIR/suggest-borrow-for-generic-arg.rs:21:16 + | +LL | let mut bar = aux::Bar; + | ------- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait +LL | aux::baz(bar); + | --- value moved here +LL | let _baa = bar; + | ^^^ value used here after move + | +help: borrow the value to avoid moving it + | +LL | aux::baz(&mut bar); + | ++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0382`. From ea37000b565021086c560f3fd881508bfed6f531 Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 23 Oct 2024 10:31:58 -0700 Subject: [PATCH 09/12] Use a common subdiagnostic format for generic borrows This is setup for unifing the logic for suggestions to borrow arguments in generic positions. As of this commit, it's still special cases for `AsRef`/`Borrow`-like traits and `Fn`-like traits. --- .../src/diagnostics/conflict_errors.rs | 15 ++++++++++----- tests/ui/moves/moved-value-on-as-ref-arg.stderr | 8 ++++---- .../ui/moves/suggest-borrow-for-generic-arg.fixed | 8 ++++---- tests/ui/moves/suggest-borrow-for-generic-arg.rs | 8 ++++---- .../moves/suggest-borrow-for-generic-arg.stderr | 8 ++++---- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ede1e815f4df0..5ffd635f1ccda 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -478,7 +478,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return; } - let mut is_mut = false; + let mut mutbl = ty::Mutability::Not; if let Some(param) = arg_param && self .infcx @@ -504,7 +504,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ] .contains(&Some(predicate.def_id())) { - is_mut = true; + mutbl = ty::Mutability::Mut; } true } else { @@ -514,13 +514,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { { // The type of the argument corresponding to the expression that got moved // is a type parameter `T`, which is has a `T: AsRef` obligation. + let place_desc = if let Some(desc) = self.describe_place(moved_place) { + format!("`{desc}`") + } else { + "here".to_owned() + }; err.span_suggestion_verbose( expr.span.shrink_to_lo(), - "borrow the value to avoid moving it", - format!("&{}", if is_mut { "mut " } else { "" }), + format!("consider {}borrowing {place_desc}", mutbl.mutably_str()), + mutbl.ref_prefix_str(), Applicability::MaybeIncorrect, ); - can_suggest_clone = is_mut; + can_suggest_clone = mutbl.is_mut(); } else if let Some(local_def_id) = def_id.as_local() && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id) && let Some(fn_decl) = node.fn_decl() diff --git a/tests/ui/moves/moved-value-on-as-ref-arg.stderr b/tests/ui/moves/moved-value-on-as-ref-arg.stderr index 4004b7a43bc09..a99bdb4fe9d41 100644 --- a/tests/ui/moves/moved-value-on-as-ref-arg.stderr +++ b/tests/ui/moves/moved-value-on-as-ref-arg.stderr @@ -8,7 +8,7 @@ LL | foo(bar); LL | let _baa = bar; | ^^^ value used here after move | -help: borrow the value to avoid moving it +help: consider borrowing `bar` | LL | foo(&bar); | + @@ -31,7 +31,7 @@ LL | struct Bar; ... LL | qux(bar); | --- you could clone this value -help: borrow the value to avoid moving it +help: consider mutably borrowing `bar` | LL | qux(&mut bar); | ++++ @@ -46,7 +46,7 @@ LL | bat(bar); LL | let _baa = bar; | ^^^ value used here after move | -help: borrow the value to avoid moving it +help: consider borrowing `bar` | LL | bat(&bar); | + @@ -69,7 +69,7 @@ LL | struct Bar; ... LL | baz(bar); | --- you could clone this value -help: borrow the value to avoid moving it +help: consider mutably borrowing `bar` | LL | baz(&mut bar); | ++++ diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.fixed b/tests/ui/moves/suggest-borrow-for-generic-arg.fixed index 3f711924d1e9c..372a469dcd63d 100644 --- a/tests/ui/moves/suggest-borrow-for-generic-arg.fixed +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.fixed @@ -8,15 +8,15 @@ extern crate suggest_borrow_for_generic_arg_aux as aux; pub fn main() { let bar = aux::Bar; - aux::foo(&bar); //~ HELP borrow the value + aux::foo(&bar); //~ HELP consider borrowing `bar` let _baa = bar; //~ ERROR use of moved value let mut bar = aux::Bar; - aux::qux(&mut bar); //~ HELP borrow the value + aux::qux(&mut bar); //~ HELP consider mutably borrowing `bar` let _baa = bar; //~ ERROR use of moved value let bar = aux::Bar; - aux::bat(&bar); //~ HELP borrow the value + aux::bat(&bar); //~ HELP consider borrowing `bar` let _baa = bar; //~ ERROR use of moved value let mut bar = aux::Bar; - aux::baz(&mut bar); //~ HELP borrow the value + aux::baz(&mut bar); //~ HELP consider mutably borrowing `bar` let _baa = bar; //~ ERROR use of moved value } diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.rs b/tests/ui/moves/suggest-borrow-for-generic-arg.rs index eb6abcdf0d570..c8b421fc03c52 100644 --- a/tests/ui/moves/suggest-borrow-for-generic-arg.rs +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.rs @@ -8,15 +8,15 @@ extern crate suggest_borrow_for_generic_arg_aux as aux; pub fn main() { let bar = aux::Bar; - aux::foo(bar); //~ HELP borrow the value + aux::foo(bar); //~ HELP consider borrowing `bar` let _baa = bar; //~ ERROR use of moved value let mut bar = aux::Bar; - aux::qux(bar); //~ HELP borrow the value + aux::qux(bar); //~ HELP consider mutably borrowing `bar` let _baa = bar; //~ ERROR use of moved value let bar = aux::Bar; - aux::bat(bar); //~ HELP borrow the value + aux::bat(bar); //~ HELP consider borrowing `bar` let _baa = bar; //~ ERROR use of moved value let mut bar = aux::Bar; - aux::baz(bar); //~ HELP borrow the value + aux::baz(bar); //~ HELP consider mutably borrowing `bar` let _baa = bar; //~ ERROR use of moved value } diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.stderr b/tests/ui/moves/suggest-borrow-for-generic-arg.stderr index 79602c4af4aa4..1a5106e6cd9f2 100644 --- a/tests/ui/moves/suggest-borrow-for-generic-arg.stderr +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.stderr @@ -8,7 +8,7 @@ LL | aux::foo(bar); LL | let _baa = bar; | ^^^ value used here after move | -help: borrow the value to avoid moving it +help: consider borrowing `bar` | LL | aux::foo(&bar); | + @@ -23,7 +23,7 @@ LL | aux::qux(bar); LL | let _baa = bar; | ^^^ value used here after move | -help: borrow the value to avoid moving it +help: consider mutably borrowing `bar` | LL | aux::qux(&mut bar); | ++++ @@ -38,7 +38,7 @@ LL | aux::bat(bar); LL | let _baa = bar; | ^^^ value used here after move | -help: borrow the value to avoid moving it +help: consider borrowing `bar` | LL | aux::bat(&bar); | + @@ -53,7 +53,7 @@ LL | aux::baz(bar); LL | let _baa = bar; | ^^^ value used here after move | -help: borrow the value to avoid moving it +help: consider mutably borrowing `bar` | LL | aux::baz(&mut bar); | ++++ From 2ab848060527f50a02cda91b9b8d98df6eb7ceab Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 23 Oct 2024 01:32:12 -0700 Subject: [PATCH 10/12] Suggest borrowing arguments in generic positions when trait bounds are satisfied This subsumes the suggestions to borrow arguments with `AsRef`/`Borrow` bounds and those to borrow arguments with `Fn` and `FnMut` bounds. It works for other traits implemented on references as well, such as `std::io::Read`, `std::io::Write`, and `core::fmt::Write`. Incidentally, by making the logic for suggesting borrowing closures general, this removes some spurious suggestions to mutably borrow `FnMut` closures in assignments, as well as an unhelpful suggestion to add a `Clone` constraint to an `impl Fn` argument. --- .../src/diagnostics/conflict_errors.rs | 316 +++++++++--------- ...re-origin-multi-variant-diagnostics.stderr | 4 - ...e-origin-single-variant-diagnostics.stderr | 4 - .../closure-origin-struct-diagnostics.stderr | 4 - .../closure-origin-tuple-diagnostics-1.stderr | 4 - .../suggest-borrow-for-generic-arg-aux.rs | 27 +- .../moves/borrow-closures-instead-of-move.rs | 2 +- .../borrow-closures-instead-of-move.stderr | 22 -- ...-on-type-no-recursive-stack-closure.stderr | 4 - .../suggest-borrow-for-generic-arg.fixed | 58 +++- .../moves/suggest-borrow-for-generic-arg.rs | 58 +++- .../suggest-borrow-for-generic-arg.stderr | 120 ++++--- tests/ui/not-copy-closure.stderr | 4 - 13 files changed, 327 insertions(+), 300 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 5ffd635f1ccda..454fd14ea7480 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -27,7 +27,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ - self, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast, + self, ClauseKind, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast, suggest_constraining_type_params, }; use rustc_middle::util::CallKind; @@ -39,6 +39,7 @@ use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::{debug, instrument}; @@ -201,16 +202,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut has_suggest_reborrow = false; if !seen_spans.contains(&move_span) { - if !closure { - self.suggest_ref_or_clone( - mpi, - &mut err, - &mut in_pattern, - move_spans, - moved_place.as_ref(), - &mut has_suggest_reborrow, - ); - } + self.suggest_ref_or_clone( + mpi, + &mut err, + &mut in_pattern, + move_spans, + moved_place.as_ref(), + &mut has_suggest_reborrow, + closure, + ); let msg_opt = CapturedMessageOpt { is_partial_move, @@ -266,27 +266,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - let opt_name = self.describe_place_with_options(place.as_ref(), DescribePlaceOpt { - including_downcast: true, - including_tuple_field: true, - }); - let note_msg = match opt_name { - Some(name) => format!("`{name}`"), - None => "value".to_owned(), - }; - if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) - || if let UseSpans::FnSelfUse { kind, .. } = use_spans - && let CallKind::FnCall { fn_trait_id, self_ty } = kind - && let ty::Param(_) = self_ty.kind() - && ty == self_ty - && self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) - { - // this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`. - true - } else { - false - } - { + if self.param_env.caller_bounds().iter().any(|c| { + c.as_trait_clause().is_some_and(|pred| { + pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id()) + }) + }) { // Suppress the next suggestion since we don't want to put more bounds onto // something that already has `Fn`-like bounds (or is a closure), so we can't // restrict anyways. @@ -295,6 +279,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_adding_bounds(&mut err, ty, copy_did, span); } + let opt_name = self.describe_place_with_options(place.as_ref(), DescribePlaceOpt { + including_downcast: true, + including_tuple_field: true, + }); + let note_msg = match opt_name { + Some(name) => format!("`{name}`"), + None => "value".to_owned(), + }; if needs_note { if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; @@ -341,6 +333,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { move_spans: UseSpans<'tcx>, moved_place: PlaceRef<'tcx>, has_suggest_reborrow: &mut bool, + moved_or_invoked_closure: bool, ) { let move_span = match move_spans { UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span, @@ -428,17 +421,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let typeck = self.infcx.tcx.typeck(self.mir_def_id()); let parent = self.infcx.tcx.parent_hir_node(expr.hir_id); - let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent + let (def_id, call_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind { - (typeck.type_dependent_def_id(parent_expr.hir_id), args, 1) + let def_id = typeck.type_dependent_def_id(parent_expr.hir_id); + (def_id, Some(parent_expr.hir_id), args, 1) } else if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::Call(call, args) = parent_expr.kind && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind() { - (Some(*def_id), args, 0) + (Some(*def_id), Some(call.hir_id), args, 0) } else { - (None, &[][..], 0) + (None, None, &[][..], 0) }; let ty = place.ty(self.body, self.infcx.tcx).ty; @@ -449,14 +443,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // The move occurred as one of the arguments to a function call. Is that // argument generic? `def_id` can't be a closure here, so using `fn_sig` is fine let arg_param = if self.infcx.tcx.def_kind(def_id).is_fn_like() - && let Some(arg_ty) = self - .infcx - .tcx - .fn_sig(def_id) - .instantiate_identity() - .skip_binder() - .inputs() - .get(pos + offset) + && let sig = + self.infcx.tcx.fn_sig(def_id).instantiate_identity().skip_binder() + && let Some(arg_ty) = sig.inputs().get(pos + offset) && let ty::Param(arg_param) = arg_ty.kind() { Some(arg_param) @@ -478,54 +467,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return; } - let mut mutbl = ty::Mutability::Not; - if let Some(param) = arg_param - && self - .infcx - .tcx - .predicates_of(def_id) - .instantiate_identity(self.infcx.tcx) - .predicates - .into_iter() - .any(|pred| { - if let ty::ClauseKind::Trait(predicate) = pred.kind().skip_binder() - && [ - self.infcx.tcx.get_diagnostic_item(sym::AsRef), - self.infcx.tcx.get_diagnostic_item(sym::AsMut), - self.infcx.tcx.get_diagnostic_item(sym::Borrow), - self.infcx.tcx.get_diagnostic_item(sym::BorrowMut), - ] - .contains(&Some(predicate.def_id())) - && *predicate.self_ty().kind() == ty::Param(*param) - { - if [ - self.infcx.tcx.get_diagnostic_item(sym::AsMut), - self.infcx.tcx.get_diagnostic_item(sym::BorrowMut), - ] - .contains(&Some(predicate.def_id())) - { - mutbl = ty::Mutability::Mut; - } - true - } else { - false - } - }) + // If the moved place is used generically by the callee and a reference to it + // would still satisfy any bounds on its type, suggest borrowing. + if let Some(¶m) = arg_param + && let Some(generic_args) = call_id.and_then(|id| typeck.node_args_opt(id)) + && let Some(ref_mutability) = self.suggest_borrow_generic_arg( + err, + def_id, + generic_args, + param, + moved_place, + pos + offset, + ty, + expr.span, + ) { - // The type of the argument corresponding to the expression that got moved - // is a type parameter `T`, which is has a `T: AsRef` obligation. - let place_desc = if let Some(desc) = self.describe_place(moved_place) { - format!("`{desc}`") - } else { - "here".to_owned() - }; - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - format!("consider {}borrowing {place_desc}", mutbl.mutably_str()), - mutbl.ref_prefix_str(), - Applicability::MaybeIncorrect, - ); - can_suggest_clone = mutbl.is_mut(); + can_suggest_clone = ref_mutability.is_mut(); } else if let Some(local_def_id) = def_id.as_local() && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id) && let Some(fn_decl) = node.fn_decl() @@ -563,6 +520,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } = move_spans { // We already suggest cloning for these cases in `explain_captures`. + } else if moved_or_invoked_closure { + // Do not suggest `closure.clone()()`. } else if let UseSpans::ClosureUse { closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)), @@ -671,6 +630,113 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } + /// If a place is used after being moved as an argument to a function, the function is generic + /// in that argument, and a reference to the argument's type would still satisfy the function's + /// bounds, suggest borrowing. This covers, e.g., borrowing an `impl Fn()` argument being passed + /// in an `impl FnOnce()` position. + /// Returns `Some(mutability)` when suggesting to borrow with mutability `mutability`, or `None` + /// if no suggestion is made. + fn suggest_borrow_generic_arg( + &self, + err: &mut Diag<'_>, + callee_did: DefId, + generic_args: ty::GenericArgsRef<'tcx>, + param: ty::ParamTy, + moved_place: PlaceRef<'tcx>, + moved_arg_pos: usize, + moved_arg_ty: Ty<'tcx>, + place_span: Span, + ) -> Option { + let tcx = self.infcx.tcx; + let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder(); + let clauses = tcx.predicates_of(callee_did).instantiate_identity(self.infcx.tcx).predicates; + + // First, is there at least one method on one of `param`'s trait bounds? + // This keeps us from suggesting borrowing the argument to `mem::drop`, e.g. + if !clauses.iter().any(|clause| { + clause.as_trait_clause().is_some_and(|tc| { + tc.self_ty().skip_binder().is_param(param.index) + && tc.polarity() == ty::PredicatePolarity::Positive + && tcx + .supertrait_def_ids(tc.def_id()) + .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order()) + .any(|item| item.fn_has_self_parameter) + }) + }) { + return None; + } + + // Try borrowing a shared reference first, then mutably. + if let Some(mutbl) = [ty::Mutability::Not, ty::Mutability::Mut].into_iter().find(|&mutbl| { + let re = self.infcx.tcx.lifetimes.re_erased; + let ref_ty = Ty::new_ref(self.infcx.tcx, re, moved_arg_ty, mutbl); + + // Ensure that substituting `ref_ty` in the callee's signature doesn't break + // other inputs or the return type. + let new_args = tcx.mk_args_from_iter(generic_args.iter().enumerate().map( + |(i, arg)| { + if i == param.index as usize { ref_ty.into() } else { arg } + }, + )); + let can_subst = |ty: Ty<'tcx>| { + // Normalize before comparing to see through type aliases and projections. + let old_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, generic_args); + let new_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, new_args); + if let Ok(old_ty) = tcx.try_normalize_erasing_regions(self.param_env, old_ty) + && let Ok(new_ty) = tcx.try_normalize_erasing_regions(self.param_env, new_ty) + { + old_ty == new_ty + } else { + false + } + }; + if !can_subst(sig.output()) + || sig + .inputs() + .iter() + .enumerate() + .any(|(i, &input_ty)| i != moved_arg_pos && !can_subst(input_ty)) + { + return false; + } + + // Test the callee's predicates, substituting a reference in for the self ty + // in bounds on `param`. + clauses.iter().all(|&clause| { + let clause_for_ref = clause.kind().map_bound(|kind| match kind { + ClauseKind::Trait(c) if c.self_ty().is_param(param.index) => { + ClauseKind::Trait(c.with_self_ty(tcx, ref_ty)) + } + ClauseKind::Projection(c) if c.self_ty().is_param(param.index) => { + ClauseKind::Projection(c.with_self_ty(tcx, ref_ty)) + } + _ => kind, + }); + self.infcx.predicate_must_hold_modulo_regions(&Obligation::new( + tcx, + ObligationCause::dummy(), + self.param_env, + ty::EarlyBinder::bind(clause_for_ref).instantiate(tcx, generic_args), + )) + }) + }) { + let place_desc = if let Some(desc) = self.describe_place(moved_place) { + format!("`{desc}`") + } else { + "here".to_owned() + }; + err.span_suggestion_verbose( + place_span.shrink_to_lo(), + format!("consider {}borrowing {place_desc}", mutbl.mutably_str()), + mutbl.ref_prefix_str(), + Applicability::MaybeIncorrect, + ); + Some(mutbl) + } else { + None + } + } + fn report_use_of_uninitialized( &self, mpi: MovePathIndex, @@ -851,74 +917,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } - fn suggest_borrow_fn_like( - &self, - err: &mut Diag<'_>, - ty: Ty<'tcx>, - move_sites: &[MoveSite], - value_name: &str, - ) -> bool { - let tcx = self.infcx.tcx; - - // Find out if the predicates show that the type is a Fn or FnMut - let find_fn_kind_from_did = |(pred, _): (ty::Clause<'tcx>, _)| { - if let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() - && pred.self_ty() == ty - { - if tcx.is_lang_item(pred.def_id(), LangItem::Fn) { - return Some(hir::Mutability::Not); - } else if tcx.is_lang_item(pred.def_id(), LangItem::FnMut) { - return Some(hir::Mutability::Mut); - } - } - None - }; - - // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably) - // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`. - // These types seem reasonably opaque enough that they could be instantiated with their - // borrowed variants in a function body when we see a move error. - let borrow_level = match *ty.kind() { - ty::Param(_) => tcx - .explicit_predicates_of(self.mir_def_id().to_def_id()) - .predicates - .iter() - .copied() - .find_map(find_fn_kind_from_did), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => tcx - .explicit_item_super_predicates(def_id) - .iter_instantiated_copied(tcx, args) - .find_map(|(clause, span)| find_fn_kind_from_did((clause, span))), - ty::Closure(_, args) => match args.as_closure().kind() { - ty::ClosureKind::Fn => Some(hir::Mutability::Not), - ty::ClosureKind::FnMut => Some(hir::Mutability::Mut), - _ => None, - }, - _ => None, - }; - - let Some(borrow_level) = borrow_level else { - return false; - }; - let sugg = move_sites - .iter() - .map(|move_site| { - let move_out = self.move_data.moves[(*move_site).moi]; - let moved_place = &self.move_data.move_paths[move_out.path].place; - let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); - let move_span = move_spans.args_or_use(); - let suggestion = borrow_level.ref_prefix_str().to_owned(); - (move_span.shrink_to_lo(), suggestion) - }) - .collect(); - err.multipart_suggestion_verbose( - format!("consider {}borrowing {value_name}", borrow_level.mutably_str()), - sugg, - Applicability::MaybeIncorrect, - ); - true - } - /// In a move error that occurs on a call within a loop, we try to identify cases where cloning /// the value would lead to a logic error. We infer these cases by seeing if the moved value is /// part of the logic to break the loop, either through an explicit `break` or if the expression diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr index 347fa3fa8920d..4cfe51eccac09 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr @@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t | LL | if let MultiVariant::Point(ref mut x, _) = point { | ^^^^^ -help: consider mutably borrowing `c` - | -LL | let a = &mut c; - | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr index c9b27e7687959..2ed611d6b5238 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr @@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t | LL | let SingleVariant::Point(ref mut x, _) = point; | ^^^^^ -help: consider mutably borrowing `c` - | -LL | let b = &mut c; - | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr index 079a9abedf976..47db2c76a2f1d 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr @@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t | LL | x.y.a += 1; | ^^^^^ -help: consider mutably borrowing `hello` - | -LL | let b = &mut hello; - | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr index 0bf717404ceb7..9db64ec04b9db 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr @@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t | LL | x.0 += 1; | ^^^ -help: consider mutably borrowing `hello` - | -LL | let b = &mut hello; - | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs b/tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs index 62e8510cf4dea..c71238ba07296 100644 --- a/tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs +++ b/tests/ui/moves/auxiliary/suggest-borrow-for-generic-arg-aux.rs @@ -1,23 +1,20 @@ //! auxiliary definitons for suggest-borrow-for-generic-arg.rs, to ensure the suggestion works on //! functions defined in other crates. -use std::borrow::{Borrow, BorrowMut}; -use std::convert::{AsMut, AsRef}; -pub struct Bar; +use std::io::{self, Read, Write}; +use std::iter::Sum; -impl AsRef for Bar { - fn as_ref(&self) -> &Bar { - self - } +pub fn write_stuff(mut writer: W) -> io::Result<()> { + writeln!(writer, "stuff") } -impl AsMut for Bar { - fn as_mut(&mut self) -> &mut Bar { - self - } +pub fn read_and_discard(mut reader: R) -> io::Result<()> { + let mut buf = Vec::new(); + reader.read_to_end(&mut buf).map(|_| ()) } -pub fn foo>(_: T) {} -pub fn qux>(_: T) {} -pub fn bat>(_: T) {} -pub fn baz>(_: T) {} +pub fn sum_three(iter: I) -> ::Item + where ::Item: Sum +{ + iter.into_iter().take(3).sum() +} diff --git a/tests/ui/moves/borrow-closures-instead-of-move.rs b/tests/ui/moves/borrow-closures-instead-of-move.rs index e4bca54e995fa..869aa654ef727 100644 --- a/tests/ui/moves/borrow-closures-instead-of-move.rs +++ b/tests/ui/moves/borrow-closures-instead-of-move.rs @@ -1,4 +1,4 @@ -fn takes_fn(f: impl Fn()) { //~ HELP if `impl Fn()` implemented `Clone` +fn takes_fn(f: impl Fn()) { loop { takes_fnonce(f); //~^ ERROR use of moved value diff --git a/tests/ui/moves/borrow-closures-instead-of-move.stderr b/tests/ui/moves/borrow-closures-instead-of-move.stderr index ab6ff417efb60..ea145f365c2da 100644 --- a/tests/ui/moves/borrow-closures-instead-of-move.stderr +++ b/tests/ui/moves/borrow-closures-instead-of-move.stderr @@ -8,21 +8,6 @@ LL | loop { LL | takes_fnonce(f); | ^ value moved here, in previous iteration of loop | -note: consider changing this parameter type in function `takes_fnonce` to borrow instead if owning the value isn't necessary - --> $DIR/borrow-closures-instead-of-move.rs:34:20 - | -LL | fn takes_fnonce(_: impl FnOnce()) {} - | ------------ ^^^^^^^^^^^^^ this parameter takes ownership of the value - | | - | in this function -help: if `impl Fn()` implemented `Clone`, you could clone the value - --> $DIR/borrow-closures-instead-of-move.rs:1:16 - | -LL | fn takes_fn(f: impl Fn()) { - | ^^^^^^^^^ consider constraining this type parameter with `Clone` -LL | loop { -LL | takes_fnonce(f); - | - you could clone this value help: consider borrowing `f` | LL | takes_fnonce(&f); @@ -40,13 +25,6 @@ LL | takes_fnonce(m); LL | takes_fnonce(m); | ^ value used here after move | -note: consider changing this parameter type in function `takes_fnonce` to borrow instead if owning the value isn't necessary - --> $DIR/borrow-closures-instead-of-move.rs:34:20 - | -LL | fn takes_fnonce(_: impl FnOnce()) {} - | ------------ ^^^^^^^^^^^^^ this parameter takes ownership of the value - | | - | in this function help: if `impl FnMut()` implemented `Clone`, you could clone the value --> $DIR/borrow-closures-instead-of-move.rs:9:20 | diff --git a/tests/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr b/tests/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr index a8473bb81983d..a4c8401ce5756 100644 --- a/tests/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr +++ b/tests/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr @@ -24,10 +24,6 @@ LL | fn conspirator(mut f: F) where F: FnMut(&mut R, bool) { | ^ consider constraining this type parameter with `Clone` LL | let mut r = R {c: Box::new(f)}; | - you could clone this value -help: consider mutably borrowing `f` - | -LL | let mut r = R {c: Box::new(&mut f)}; - | ++++ error: aborting due to 2 previous errors diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.fixed b/tests/ui/moves/suggest-borrow-for-generic-arg.fixed index 372a469dcd63d..b5e0b468aa60a 100644 --- a/tests/ui/moves/suggest-borrow-for-generic-arg.fixed +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.fixed @@ -1,22 +1,46 @@ -//! Test suggetions to borrow generic arguments instead of moving when all predicates hold after -//! substituting in the reference type. +//! Test suggetions to borrow generic arguments instead of moving. Tests for other instances of this +//! can be found in `moved-value-on-as-ref-arg.rs` and `borrow-closures-instead-of-move.rs` //@ run-rustfix -//@ aux-build:suggest-borrow-for-generic-arg-aux.rs +//@ aux-crate:aux=suggest-borrow-for-generic-arg-aux.rs +//@ edition: 2021 #![allow(unused_mut)] -extern crate suggest_borrow_for_generic_arg_aux as aux; +use std::io::{self, Write}; -pub fn main() { - let bar = aux::Bar; - aux::foo(&bar); //~ HELP consider borrowing `bar` - let _baa = bar; //~ ERROR use of moved value - let mut bar = aux::Bar; - aux::qux(&mut bar); //~ HELP consider mutably borrowing `bar` - let _baa = bar; //~ ERROR use of moved value - let bar = aux::Bar; - aux::bat(&bar); //~ HELP consider borrowing `bar` - let _baa = bar; //~ ERROR use of moved value - let mut bar = aux::Bar; - aux::baz(&mut bar); //~ HELP consider mutably borrowing `bar` - let _baa = bar; //~ ERROR use of moved value +// test for `std::io::Write` (#131413) +fn test_write() -> io::Result<()> { + let mut stdout = io::stdout(); + aux::write_stuff(&stdout)?; //~ HELP consider borrowing `stdout` + writeln!(stdout, "second line")?; //~ ERROR borrow of moved value: `stdout` + + let mut buf = Vec::new(); + aux::write_stuff(&mut buf.clone())?; //~ HELP consider mutably borrowing `buf` + //~^ HELP consider cloning the value + writeln!(buf, "second_line") //~ ERROR borrow of moved value: `buf` +} + +/// test for `std::io::Read` (#131413) +fn test_read() -> io::Result<()> { + let stdin = io::stdin(); + aux::read_and_discard(&stdin)?; //~ HELP consider borrowing `stdin` + aux::read_and_discard(stdin)?; //~ ERROR use of moved value: `stdin` + + let mut bytes = std::collections::VecDeque::from([1, 2, 3, 4, 5, 6]); + aux::read_and_discard(&mut bytes.clone())?; //~ HELP consider mutably borrowing `bytes` + //~^ HELP consider cloning the value + aux::read_and_discard(bytes) //~ ERROR use of moved value: `bytes` +} + +/// test that suggestions work with projection types in the callee's signature +fn test_projections() { + let mut iter = [1, 2, 3, 4, 5, 6].into_iter(); + let _six: usize = aux::sum_three(&mut iter.clone()); //~ HELP consider mutably borrowing `iter` + //~^ HELP consider cloning the value + let _fifteen: usize = aux::sum_three(iter); //~ ERROR use of moved value: `iter` +} + +fn main() { + test_write().unwrap(); + test_read().unwrap(); + test_projections(); } diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.rs b/tests/ui/moves/suggest-borrow-for-generic-arg.rs index c8b421fc03c52..e08978db63aeb 100644 --- a/tests/ui/moves/suggest-borrow-for-generic-arg.rs +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.rs @@ -1,22 +1,46 @@ -//! Test suggetions to borrow generic arguments instead of moving when all predicates hold after -//! substituting in the reference type. +//! Test suggetions to borrow generic arguments instead of moving. Tests for other instances of this +//! can be found in `moved-value-on-as-ref-arg.rs` and `borrow-closures-instead-of-move.rs` //@ run-rustfix -//@ aux-build:suggest-borrow-for-generic-arg-aux.rs +//@ aux-crate:aux=suggest-borrow-for-generic-arg-aux.rs +//@ edition: 2021 #![allow(unused_mut)] -extern crate suggest_borrow_for_generic_arg_aux as aux; +use std::io::{self, Write}; -pub fn main() { - let bar = aux::Bar; - aux::foo(bar); //~ HELP consider borrowing `bar` - let _baa = bar; //~ ERROR use of moved value - let mut bar = aux::Bar; - aux::qux(bar); //~ HELP consider mutably borrowing `bar` - let _baa = bar; //~ ERROR use of moved value - let bar = aux::Bar; - aux::bat(bar); //~ HELP consider borrowing `bar` - let _baa = bar; //~ ERROR use of moved value - let mut bar = aux::Bar; - aux::baz(bar); //~ HELP consider mutably borrowing `bar` - let _baa = bar; //~ ERROR use of moved value +// test for `std::io::Write` (#131413) +fn test_write() -> io::Result<()> { + let mut stdout = io::stdout(); + aux::write_stuff(stdout)?; //~ HELP consider borrowing `stdout` + writeln!(stdout, "second line")?; //~ ERROR borrow of moved value: `stdout` + + let mut buf = Vec::new(); + aux::write_stuff(buf)?; //~ HELP consider mutably borrowing `buf` + //~^ HELP consider cloning the value + writeln!(buf, "second_line") //~ ERROR borrow of moved value: `buf` +} + +/// test for `std::io::Read` (#131413) +fn test_read() -> io::Result<()> { + let stdin = io::stdin(); + aux::read_and_discard(stdin)?; //~ HELP consider borrowing `stdin` + aux::read_and_discard(stdin)?; //~ ERROR use of moved value: `stdin` + + let mut bytes = std::collections::VecDeque::from([1, 2, 3, 4, 5, 6]); + aux::read_and_discard(bytes)?; //~ HELP consider mutably borrowing `bytes` + //~^ HELP consider cloning the value + aux::read_and_discard(bytes) //~ ERROR use of moved value: `bytes` +} + +/// test that suggestions work with projection types in the callee's signature +fn test_projections() { + let mut iter = [1, 2, 3, 4, 5, 6].into_iter(); + let _six: usize = aux::sum_three(iter); //~ HELP consider mutably borrowing `iter` + //~^ HELP consider cloning the value + let _fifteen: usize = aux::sum_three(iter); //~ ERROR use of moved value: `iter` +} + +fn main() { + test_write().unwrap(); + test_read().unwrap(); + test_projections(); } diff --git a/tests/ui/moves/suggest-borrow-for-generic-arg.stderr b/tests/ui/moves/suggest-borrow-for-generic-arg.stderr index 1a5106e6cd9f2..07e24f566cb9b 100644 --- a/tests/ui/moves/suggest-borrow-for-generic-arg.stderr +++ b/tests/ui/moves/suggest-borrow-for-generic-arg.stderr @@ -1,63 +1,93 @@ -error[E0382]: use of moved value: `bar` - --> $DIR/suggest-borrow-for-generic-arg.rs:12:16 +error[E0382]: borrow of moved value: `stdout` + --> $DIR/suggest-borrow-for-generic-arg.rs:14:14 | -LL | let bar = aux::Bar; - | --- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait -LL | aux::foo(bar); - | --- value moved here -LL | let _baa = bar; - | ^^^ value used here after move +LL | let mut stdout = io::stdout(); + | ---------- move occurs because `stdout` has type `Stdout`, which does not implement the `Copy` trait +LL | aux::write_stuff(stdout)?; + | ------ value moved here +LL | writeln!(stdout, "second line")?; + | ^^^^^^ value borrowed here after move | -help: consider borrowing `bar` +help: consider borrowing `stdout` | -LL | aux::foo(&bar); - | + +LL | aux::write_stuff(&stdout)?; + | + -error[E0382]: use of moved value: `bar` - --> $DIR/suggest-borrow-for-generic-arg.rs:15:16 +error[E0382]: borrow of moved value: `buf` + --> $DIR/suggest-borrow-for-generic-arg.rs:19:14 | -LL | let mut bar = aux::Bar; - | ------- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait -LL | aux::qux(bar); - | --- value moved here -LL | let _baa = bar; - | ^^^ value used here after move +LL | let mut buf = Vec::new(); + | ------- move occurs because `buf` has type `Vec`, which does not implement the `Copy` trait +LL | aux::write_stuff(buf)?; + | --- value moved here +LL | +LL | writeln!(buf, "second_line") + | ^^^ value borrowed here after move | -help: consider mutably borrowing `bar` +help: consider mutably borrowing `buf` | -LL | aux::qux(&mut bar); - | ++++ +LL | aux::write_stuff(&mut buf)?; + | ++++ +help: consider cloning the value if the performance cost is acceptable + | +LL | aux::write_stuff(buf.clone())?; + | ++++++++ -error[E0382]: use of moved value: `bar` - --> $DIR/suggest-borrow-for-generic-arg.rs:18:16 +error[E0382]: use of moved value: `stdin` + --> $DIR/suggest-borrow-for-generic-arg.rs:26:27 | -LL | let bar = aux::Bar; - | --- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait -LL | aux::bat(bar); - | --- value moved here -LL | let _baa = bar; - | ^^^ value used here after move +LL | let stdin = io::stdin(); + | ----- move occurs because `stdin` has type `Stdin`, which does not implement the `Copy` trait +LL | aux::read_and_discard(stdin)?; + | ----- value moved here +LL | aux::read_and_discard(stdin)?; + | ^^^^^ value used here after move | -help: consider borrowing `bar` +help: consider borrowing `stdin` | -LL | aux::bat(&bar); - | + +LL | aux::read_and_discard(&stdin)?; + | + -error[E0382]: use of moved value: `bar` - --> $DIR/suggest-borrow-for-generic-arg.rs:21:16 +error[E0382]: use of moved value: `bytes` + --> $DIR/suggest-borrow-for-generic-arg.rs:31:27 + | +LL | let mut bytes = std::collections::VecDeque::from([1, 2, 3, 4, 5, 6]); + | --------- move occurs because `bytes` has type `VecDeque`, which does not implement the `Copy` trait +LL | aux::read_and_discard(bytes)?; + | ----- value moved here +LL | +LL | aux::read_and_discard(bytes) + | ^^^^^ value used here after move + | +help: consider mutably borrowing `bytes` + | +LL | aux::read_and_discard(&mut bytes)?; + | ++++ +help: consider cloning the value if the performance cost is acceptable + | +LL | aux::read_and_discard(bytes.clone())?; + | ++++++++ + +error[E0382]: use of moved value: `iter` + --> $DIR/suggest-borrow-for-generic-arg.rs:39:42 + | +LL | let mut iter = [1, 2, 3, 4, 5, 6].into_iter(); + | -------- move occurs because `iter` has type `std::array::IntoIter`, which does not implement the `Copy` trait +LL | let _six: usize = aux::sum_three(iter); + | ---- value moved here +LL | +LL | let _fifteen: usize = aux::sum_three(iter); + | ^^^^ value used here after move | -LL | let mut bar = aux::Bar; - | ------- move occurs because `bar` has type `Bar`, which does not implement the `Copy` trait -LL | aux::baz(bar); - | --- value moved here -LL | let _baa = bar; - | ^^^ value used here after move +help: consider mutably borrowing `iter` | -help: consider mutably borrowing `bar` +LL | let _six: usize = aux::sum_three(&mut iter); + | ++++ +help: consider cloning the value if the performance cost is acceptable | -LL | aux::baz(&mut bar); - | ++++ +LL | let _six: usize = aux::sum_three(iter.clone()); + | ++++++++ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/not-copy-closure.stderr b/tests/ui/not-copy-closure.stderr index 50e25a24d811f..60cb135231306 100644 --- a/tests/ui/not-copy-closure.stderr +++ b/tests/ui/not-copy-closure.stderr @@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t | LL | a += 1; | ^ -help: consider mutably borrowing `hello` - | -LL | let b = &mut hello; - | ++++ error: aborting due to 1 previous error From 49f9b4b4decd6dd198416f493ad666d469897ebe Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 14 Nov 2024 14:56:48 +0300 Subject: [PATCH 11/12] update download-rustc comments and default Signed-off-by: onur-ozkan --- config.example.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.example.toml b/config.example.toml index 2bc79a6d6ba9d..9dc71b10f7024 100644 --- a/config.example.toml +++ b/config.example.toml @@ -503,8 +503,8 @@ # Set this to "if-unchanged" if you are working on `src/tools`, `tests` or `library` (on CI, `library` # changes triggers in-tree compiler build) to speed up the build process. # -# Set this to `true` to download unconditionally. -#download-rustc = false +# Set this to `true` to always download or `false` to always use the in-tree compiler. +#download-rustc = "if-unchanged" # Number of codegen units to use for each compiler invocation. A value of 0 # means "the number of cores on this machine", and 1+ is passed through to the From 2b8c3453939c4a7adc257c5f073db2f5c254fec8 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 14 Nov 2024 15:06:12 +0300 Subject: [PATCH 12/12] add myself into `users_on_vacation` on triagebot Signed-off-by: onur-ozkan --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 0ac971d48d8d0..d5bc549ca9ff5 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -977,6 +977,7 @@ contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" users_on_vacation = [ "jyn514", "oli-obk", + "onur-ozkan", ] [assign.adhoc_groups]