From 1ceb2ea414fc7f7251e20f0a6dafea69b4e397f9 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Tue, 19 Sep 2023 12:44:30 -0700 Subject: [PATCH 001/118] Show which roots are being scanned in progress messages See: #12613 --- crates/load-cargo/src/lib.rs | 2 +- crates/rust-analyzer/src/main_loop.rs | 17 ++++++++++++++-- crates/vfs-notify/src/lib.rs | 28 +++++++++++++++++++++++++-- crates/vfs/src/loader.rs | 14 ++++++++++++-- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index e6ddfd580c30e..8f02f20ad1b94 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -317,7 +317,7 @@ fn load_crate_graph( // wait until Vfs has loaded all roots for task in receiver { match task { - vfs::loader::Message::Progress { n_done, n_total, config_version: _ } => { + vfs::loader::Message::Progress { n_done, n_total, .. } => { if n_done == n_total { break; } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ca7893faf5dd7..714476c482caf 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -586,7 +586,7 @@ impl GlobalState { } } } - vfs::loader::Message::Progress { n_total, n_done, config_version } => { + vfs::loader::Message::Progress { n_total, n_done, file, config_version } => { always!(config_version <= self.vfs_config_version); self.vfs_progress_config_version = config_version; @@ -601,10 +601,23 @@ impl GlobalState { assert_eq!(n_done, n_total); Progress::End }; + + let mut message = format!("{n_done}/{n_total}"); + if let Some(file) = file { + message += &format!( + ": {}", + match file.strip_prefix(&self.config.root_path()) { + Some(relative_path) => relative_path.as_ref(), + None => file.as_ref(), + } + .display() + ); + } + self.report_progress( "Roots Scanned", state, - Some(format!("{n_done}/{n_total}")), + Some(message), Some(Progress::fraction(n_done, n_total)), None, ); diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 19b34ffe6b9d7..a6e4da5615b74 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -103,21 +103,39 @@ impl NotifyActor { let config_version = config.version; let n_total = config.load.len(); - self.send(loader::Message::Progress { n_total, n_done: 0, config_version }); + self.send(loader::Message::Progress { + n_total, + n_done: 0, + config_version, + file: None, + }); self.watched_entries.clear(); for (i, entry) in config.load.into_iter().enumerate() { + self.send(loader::Message::Progress { + n_total, + n_done: i, + config_version, + file: None, + }); let watch = config.watch.contains(&i); if watch { self.watched_entries.push(entry.clone()); } - let files = self.load_entry(entry, watch); + let files = + self.load_entry(entry, watch, |file| loader::Message::Progress { + n_total, + n_done: i, + file: Some(file), + config_version, + }); self.send(loader::Message::Loaded { files }); self.send(loader::Message::Progress { n_total, n_done: i + 1, config_version, + file: None, }); } } @@ -170,6 +188,7 @@ impl NotifyActor { &mut self, entry: loader::Entry, watch: bool, + make_message: impl Fn(AbsPathBuf) -> loader::Message, ) -> Vec<(AbsPathBuf, Option>)> { match entry { loader::Entry::Files(files) => files @@ -186,6 +205,7 @@ impl NotifyActor { let mut res = Vec::new(); for root in &dirs.include { + self.send(make_message(root.clone())); let walkdir = WalkDir::new(root).follow_links(true).into_iter().filter_entry(|entry| { if !entry.file_type().is_dir() { @@ -197,9 +217,13 @@ impl NotifyActor { }); let files = walkdir.filter_map(|it| it.ok()).filter_map(|entry| { + let depth = entry.depth(); let is_dir = entry.file_type().is_dir(); let is_file = entry.file_type().is_file(); let abs_path = AbsPathBuf::assert(entry.into_path()); + if depth < 2 && is_dir { + self.send(make_message(abs_path.clone())); + } if is_dir && watch { self.watch(abs_path.clone()); } diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs index 89a544c81d8c9..5c507c3dbef3a 100644 --- a/crates/vfs/src/loader.rs +++ b/crates/vfs/src/loader.rs @@ -48,7 +48,16 @@ pub enum Message { /// Indicate a gradual progress. /// /// This is supposed to be the number of loaded files. - Progress { n_total: usize, n_done: usize, config_version: u32 }, + Progress { + /// The total files to be loaded. + n_total: usize, + /// The files that have been loaded successfully. + n_done: usize, + /// The file being loaded, if any. + file: Option, + /// The [`Config`] version. + config_version: u32, + }, /// The handle loaded the following files' content. Loaded { files: Vec<(AbsPathBuf, Option>)> }, /// The handle loaded the following files' content. @@ -204,10 +213,11 @@ impl fmt::Debug for Message { Message::Changed { files } => { f.debug_struct("Changed").field("n_files", &files.len()).finish() } - Message::Progress { n_total, n_done, config_version } => f + Message::Progress { n_total, n_done, file, config_version } => f .debug_struct("Progress") .field("n_total", n_total) .field("n_done", n_done) + .field("file", file) .field("config_version", config_version) .finish(), } From e6dd522761ded0e73ff2dfeed2ef598fcb508819 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 11 Jan 2024 19:50:35 +0800 Subject: [PATCH 002/118] fix: add err recovery for use_tree_list parsing --- crates/parser/src/grammar/items/use_item.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/parser/src/grammar/items/use_item.rs b/crates/parser/src/grammar/items/use_item.rs index 69880b7946b69..e34bf9668c1fa 100644 --- a/crates/parser/src/grammar/items/use_item.rs +++ b/crates/parser/src/grammar/items/use_item.rs @@ -11,7 +11,7 @@ pub(super) fn use_(p: &mut Parser<'_>, m: Marker) { // test use_tree // use outer::tree::{inner::tree}; -fn use_tree(p: &mut Parser<'_>, top_level: bool) { +fn use_tree(p: &mut Parser<'_>, top_level: bool) -> bool { let m = p.start(); match p.current() { // test use_tree_star @@ -70,24 +70,25 @@ fn use_tree(p: &mut Parser<'_>, top_level: bool) { // main balanced `{}` p.err_and_bump(msg); } - return; + return false; } } m.complete(p, USE_TREE); + true } +pub(super) const USE_TREE_LIST_RECOVERY_SET: TokenSet = + TokenSet::new(&[T![;], T![,], T![.], T![ident]]); + // test use_tree_list // use {a, b, c}; pub(crate) fn use_tree_list(p: &mut Parser<'_>) { assert!(p.at(T!['{'])); let m = p.start(); - p.bump(T!['{']); - while !p.at(EOF) && !p.at(T!['}']) { - use_tree(p, false); - if !p.at(T!['}']) { - p.expect(T![,]); - } - } - p.expect(T!['}']); + + delimited(p, T!['{'], T!['}'], T![,], USE_TREE_LIST_RECOVERY_SET, |p: &mut Parser<'_>| { + use_tree(p, false) || p.at_ts(USE_TREE_LIST_RECOVERY_SET) + }); + m.complete(p, USE_TREE_LIST); } From afe05fe1d869c1ca759a6604cd486d5acee87f80 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 11 Jan 2024 19:51:45 +0800 Subject: [PATCH 003/118] fix broken test --- .../parser/err/0036_partial_use.rast | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/crates/parser/test_data/parser/err/0036_partial_use.rast b/crates/parser/test_data/parser/err/0036_partial_use.rast index 13e76e68307e6..ad4bc89e83aa5 100644 --- a/crates/parser/test_data/parser/err/0036_partial_use.rast +++ b/crates/parser/test_data/parser/err/0036_partial_use.rast @@ -22,30 +22,23 @@ SOURCE_FILE IDENT "Error" ERROR SEMICOLON ";" - WHITESPACE "\n" - ERROR - USE_KW "use" - WHITESPACE " " - USE_TREE - PATH - PATH - PATH_SEGMENT - NAME_REF - IDENT "std" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "io" - ERROR - SEMICOLON ";" + WHITESPACE "\n" + USE + USE_KW "use" + WHITESPACE " " + USE_TREE + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "std" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "io" + SEMICOLON ";" WHITESPACE "\n" error 22: expected COMMA error 22: expected one of `*`, `::`, `{`, `self`, `super` or an identifier -error 23: expected COMMA -error 24: expected one of `*`, `::`, `{`, `self`, `super` or an identifier -error 27: expected COMMA -error 35: expected COMMA -error 35: expected one of `*`, `::`, `{`, `self`, `super` or an identifier -error 36: expected COMMA -error 36: expected R_CURLY -error 36: expected SEMICOLON +error 23: expected R_CURLY +error 23: expected SEMICOLON From 45eea5711525ad19f8b6669f4e1317964efa4d8a Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 11 Jan 2024 19:52:23 +0800 Subject: [PATCH 004/118] add in-comment test case for use_tree_list_err_recovery --- crates/parser/src/grammar/items/use_item.rs | 5 ++ .../err/0026_use_tree_list_err_recovery.rast | 50 +++++++++++++++++++ .../err/0026_use_tree_list_err_recovery.rs | 4 ++ 3 files changed, 59 insertions(+) create mode 100644 crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast create mode 100644 crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rs diff --git a/crates/parser/src/grammar/items/use_item.rs b/crates/parser/src/grammar/items/use_item.rs index e34bf9668c1fa..b397454173569 100644 --- a/crates/parser/src/grammar/items/use_item.rs +++ b/crates/parser/src/grammar/items/use_item.rs @@ -86,6 +86,11 @@ pub(crate) fn use_tree_list(p: &mut Parser<'_>) { assert!(p.at(T!['{'])); let m = p.start(); + // test_err use_tree_list_err_recovery + // use {a; + // use b; + // struct T; + // fn test() {} delimited(p, T!['{'], T!['}'], T![,], USE_TREE_LIST_RECOVERY_SET, |p: &mut Parser<'_>| { use_tree(p, false) || p.at_ts(USE_TREE_LIST_RECOVERY_SET) }); diff --git a/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast b/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast new file mode 100644 index 0000000000000..7732bdf72f40b --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast @@ -0,0 +1,50 @@ +SOURCE_FILE + USE + USE_KW "use" + WHITESPACE " " + USE_TREE + USE_TREE_LIST + L_CURLY "{" + USE_TREE + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + ERROR + SEMICOLON ";" + WHITESPACE "\n" + USE + USE_KW "use" + WHITESPACE " " + USE_TREE + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + SEMICOLON ";" + WHITESPACE "\n" + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "T" + SEMICOLON ";" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "test" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 6: expected COMMA +error 6: expected one of `*`, `::`, `{`, `self`, `super` or an identifier +error 7: expected R_CURLY +error 7: expected SEMICOLON diff --git a/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rs b/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rs new file mode 100644 index 0000000000000..f16959c25f276 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rs @@ -0,0 +1,4 @@ +use {a; +use b; +struct T; +fn test() {} From 4a6b16bfbe744ffcce373d3ef675b5052e23c0d8 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Thu, 11 Jan 2024 19:54:24 +0300 Subject: [PATCH 005/118] order use trees following rustfmt's algorithm for ordering imports --- crates/ide-db/src/imports/insert_use.rs | 11 +- crates/ide-db/src/imports/merge_imports.rs | 207 ++++++++++++++------- 2 files changed, 141 insertions(+), 77 deletions(-) diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index a0cfd3836ddf0..8c9ced108aa21 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -16,7 +16,7 @@ use syntax::{ use crate::{ imports::merge_imports::{ - common_prefix, eq_attrs, eq_visibility, try_merge_imports, use_tree_path_cmp, MergeBehavior, + common_prefix, eq_attrs, eq_visibility, try_merge_imports, use_tree_cmp, MergeBehavior, }, RootDatabase, }; @@ -357,6 +357,8 @@ fn insert_use_( use_item: ast::Use, ) { let scope_syntax = scope.as_syntax_node(); + let insert_use_tree = + use_item.use_tree().expect("`use_item` should have a use tree for `insert_path`"); let group = ImportGroup::new(insert_path); let path_node_iter = scope_syntax .children() @@ -364,8 +366,7 @@ fn insert_use_( .flat_map(|(use_, node)| { let tree = use_.use_tree()?; let path = tree.path()?; - let has_tl = tree.use_tree_list().is_some(); - Some((path, has_tl, node)) + Some((path, tree, node)) }); if group_imports { @@ -381,8 +382,8 @@ fn insert_use_( // find the element that would come directly after our new import let post_insert: Option<(_, _, SyntaxNode)> = group_iter .inspect(|(.., node)| last = Some(node.clone())) - .find(|&(ref path, has_tl, _)| { - use_tree_path_cmp(insert_path, false, path, has_tl) != Ordering::Greater + .find(|&(_, ref use_tree, _)| { + use_tree_cmp(&insert_use_tree, use_tree) != Ordering::Greater }); if let Some((.., node)) = post_insert { diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index ff84e9ffaeec7..df43bb553fc13 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs @@ -3,8 +3,8 @@ use std::cmp::Ordering; use itertools::{EitherOrBoth, Itertools}; use syntax::{ - ast::{self, AstNode, HasAttrs, HasVisibility, PathSegmentKind}, - ted, + ast::{self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind}, + ted, TokenText, }; use crate::syntax_helpers::node_ext::vis_eq; @@ -97,7 +97,7 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) // same as a `filter` op). .map(|tree| merge.is_tree_allowed(&tree).then_some(tree)) .collect::>()?; - use_trees.sort_unstable_by(|a, b| path_cmp_for_sort(a.path(), b.path())); + use_trees.sort_unstable_by(|a, b| use_tree_cmp(a, b)); for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { if !merge.is_tree_allowed(&rhs_t) { return None; @@ -190,25 +190,6 @@ pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast } } -/// Orders paths in the following way: -/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers -// FIXME: rustfmt sorts lowercase idents before uppercase, in general we want to have the same ordering rustfmt has -// which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports. -// Example foo::{self, foo, baz, Baz, Qux, *, {Bar}} -fn path_cmp_for_sort(a: Option, b: Option) -> Ordering { - match (a, b) { - (None, None) => Ordering::Equal, - (None, Some(_)) => Ordering::Less, - (Some(_), None) => Ordering::Greater, - (Some(ref a), Some(ref b)) => match (path_is_self(a), path_is_self(b)) { - (true, true) => Ordering::Equal, - (true, false) => Ordering::Less, - (false, true) => Ordering::Greater, - (false, false) => path_cmp_short(a, b), - }, - } -} - /// Path comparison func for binary searching for merging. fn path_cmp_bin_search(lhs: Option, rhs: Option<&ast::Path>) -> Ordering { match (lhs.as_ref().and_then(ast::Path::first_segment), rhs.and_then(ast::Path::first_segment)) @@ -220,59 +201,141 @@ fn path_cmp_bin_search(lhs: Option, rhs: Option<&ast::Path>) -> Order } } -/// Short circuiting comparison, if both paths are equal until one of them ends they are considered -/// equal -fn path_cmp_short(a: &ast::Path, b: &ast::Path) -> Ordering { - let a = a.segments(); - let b = b.segments(); - // cmp_by would be useful for us here but that is currently unstable - // cmp doesn't work due the lifetimes on text's return type - a.zip(b) - .find_map(|(a, b)| match path_segment_cmp(&a, &b) { - Ordering::Equal => None, - ord => Some(ord), - }) - .unwrap_or(Ordering::Equal) -} - -/// Compares two paths, if one ends earlier than the other the has_tl parameters decide which is -/// greater as a path that has a tree list should be greater, while one that just ends without -/// a tree list should be considered less. -pub(super) fn use_tree_path_cmp( - a: &ast::Path, - a_has_tl: bool, - b: &ast::Path, - b_has_tl: bool, -) -> Ordering { - let a_segments = a.segments(); - let b_segments = b.segments(); - // cmp_by would be useful for us here but that is currently unstable - // cmp doesn't work due the lifetimes on text's return type - a_segments - .zip_longest(b_segments) - .find_map(|zipped| match zipped { - EitherOrBoth::Both(ref a, ref b) => match path_segment_cmp(a, b) { - Ordering::Equal => None, - ord => Some(ord), - }, - EitherOrBoth::Left(_) if !b_has_tl => Some(Ordering::Greater), - EitherOrBoth::Left(_) => Some(Ordering::Less), - EitherOrBoth::Right(_) if !a_has_tl => Some(Ordering::Less), - EitherOrBoth::Right(_) => Some(Ordering::Greater), - }) - .unwrap_or(Ordering::Equal) +/// Orders use trees following `rustfmt`'s algorithm for ordering imports, which is `self`, `super` and `crate` first, +/// then identifier imports with lowercase ones first and upper snake case (e.g. UPPER_SNAKE_CASE) ones last, +/// then glob imports, and at last list imports. +/// +/// Example foo::{self, foo, baz, Baz, Qux, FOO_BAZ, *, {Bar}} +/// Ref: . +pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering { + let tiebreak_by_glob_or_alias = || match (a.star_token().is_some(), b.star_token().is_some()) { + (true, false) => Ordering::Greater, + (false, true) => Ordering::Less, + _ => match (a.rename(), b.rename()) { + (None, None) => Ordering::Equal, + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(a_rename), Some(b_rename)) => a_rename + .name() + .as_ref() + .map(ast::Name::text) + .as_ref() + .map_or("_", TokenText::as_str) + .cmp( + b_rename + .name() + .as_ref() + .map(ast::Name::text) + .as_ref() + .map_or("_", TokenText::as_str), + ), + }, + }; + let tiebreak_by_tree_list_glob_or_alias = || match (a.use_tree_list(), b.use_tree_list()) { + (None, None) => tiebreak_by_glob_or_alias(), + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(a_list), Some(b_list)) => a_list + .use_trees() + .zip_longest(b_list.use_trees()) + .find_map(|zipped| match zipped { + EitherOrBoth::Both(ref a_tree, ref b_tree) => match use_tree_cmp(a_tree, b_tree) { + Ordering::Equal => None, + ord => Some(ord), + }, + EitherOrBoth::Left(_) => Some(Ordering::Greater), + EitherOrBoth::Right(_) => Some(Ordering::Less), + }) + .unwrap_or_else(tiebreak_by_glob_or_alias), + }; + match (a.path(), b.path()) { + (None, None) => match (a.is_simple_path(), b.is_simple_path()) { + (true, true) => Ordering::Equal, + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + (false, false) => tiebreak_by_tree_list_glob_or_alias(), + }, + (Some(_), None) if !b.is_simple_path() => Ordering::Less, + (Some(_), None) => Ordering::Greater, + (None, Some(_)) if !a.is_simple_path() => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(ref a_path), Some(ref b_path)) => { + // cmp_by would be useful for us here but that is currently unstable + // cmp doesn't work due the lifetimes on text's return type + a_path + .segments() + .zip_longest(b_path.segments()) + .find_map(|zipped| match zipped { + EitherOrBoth::Both(ref a_segment, ref b_segment) => { + match path_segment_cmp(a_segment, b_segment) { + Ordering::Equal => None, + ord => Some(ord), + } + } + EitherOrBoth::Left(_) if b.is_simple_path() => Some(Ordering::Greater), + EitherOrBoth::Left(_) => Some(Ordering::Less), + EitherOrBoth::Right(_) if a.is_simple_path() => Some(Ordering::Less), + EitherOrBoth::Right(_) => Some(Ordering::Greater), + }) + .unwrap_or_else(tiebreak_by_tree_list_glob_or_alias) + } + } } fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { - let a = a.kind().and_then(|kind| match kind { - PathSegmentKind::Name(name_ref) => Some(name_ref), - _ => None, - }); - let b = b.kind().and_then(|kind| match kind { - PathSegmentKind::Name(name_ref) => Some(name_ref), - _ => None, - }); - a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text)) + match (a.kind(), b.kind()) { + (None, None) => Ordering::Equal, + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + // self + (Some(PathSegmentKind::SelfKw), Some(PathSegmentKind::SelfKw)) => Ordering::Equal, + (Some(PathSegmentKind::SelfKw), _) => Ordering::Less, + (_, Some(PathSegmentKind::SelfKw)) => Ordering::Greater, + // super + (Some(PathSegmentKind::SuperKw), Some(PathSegmentKind::SuperKw)) => Ordering::Equal, + (Some(PathSegmentKind::SuperKw), _) => Ordering::Less, + (_, Some(PathSegmentKind::SuperKw)) => Ordering::Greater, + // crate + (Some(PathSegmentKind::CrateKw), Some(PathSegmentKind::CrateKw)) => Ordering::Equal, + (Some(PathSegmentKind::CrateKw), _) => Ordering::Less, + (_, Some(PathSegmentKind::CrateKw)) => Ordering::Greater, + // identifiers (everything else is treated as an identifier). + _ => { + match ( + a.name_ref().as_ref().map(ast::NameRef::text), + b.name_ref().as_ref().map(ast::NameRef::text), + ) { + (None, None) => Ordering::Equal, + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(a_name), Some(b_name)) => { + // snake_case < CamelCase < UPPER_SNAKE_CASE + let a_text = a_name.as_str(); + let b_text = b_name.as_str(); + fn is_upper_snake_case(s: &str) -> bool { + s.chars().all(|c| c.is_uppercase() || c == '_' || c.is_numeric()) + } + if a_text.starts_with(char::is_lowercase) + && b_text.starts_with(char::is_uppercase) + { + return Ordering::Less; + } + if a_text.starts_with(char::is_uppercase) + && b_text.starts_with(char::is_lowercase) + { + return Ordering::Greater; + } + if !is_upper_snake_case(a_text) && is_upper_snake_case(b_text) { + return Ordering::Less; + } + if is_upper_snake_case(a_text) && !is_upper_snake_case(b_text) { + return Ordering::Greater; + } + a_text.cmp(&b_text) + } + } + } + } } pub fn eq_visibility(vis0: Option, vis1: Option) -> bool { From 22cda959b334cc82b85745cd7af6e554d97efc9b Mon Sep 17 00:00:00 2001 From: YangzeLuo Date: Fri, 12 Jan 2024 13:35:53 +0800 Subject: [PATCH 006/118] fix: failed to infer OUT_DIR when workspace root contains symlink --- crates/project-model/src/build_scripts.rs | 19 ++++++-- crates/project-model/src/workspace.rs | 13 +++-- crates/rust-analyzer/src/reload.rs | 9 +++- crates/rust-analyzer/tests/slow-tests/main.rs | 47 ++++++++++++------- .../rust-analyzer/tests/slow-tests/support.rs | 16 ++++++- .../rust-analyzer/tests/slow-tests/testdir.rs | 37 ++++++++++++++- 6 files changed, 114 insertions(+), 27 deletions(-) diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 68cd40c040b3d..c1670c2004902 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -60,6 +60,7 @@ impl WorkspaceBuildScripts { fn build_command( config: &CargoConfig, allowed_features: &FxHashSet, + workspace_root: &AbsPathBuf, ) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { @@ -73,6 +74,9 @@ impl WorkspaceBuildScripts { cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&config.extra_args); + cmd.arg("--manifest-path"); + cmd.arg(workspace_root.join("Cargo.toml").as_os_str()); + if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); } @@ -143,7 +147,11 @@ impl WorkspaceBuildScripts { let allowed_features = workspace.workspace_features(); match Self::run_per_ws( - Self::build_command(config, &allowed_features)?, + Self::build_command( + config, + &allowed_features, + &workspace.workspace_root().to_path_buf(), + )?, workspace, current_dir, progress, @@ -153,7 +161,11 @@ impl WorkspaceBuildScripts { { // building build scripts failed, attempt to build with --keep-going so // that we potentially get more build data - let mut cmd = Self::build_command(config, &allowed_features)?; + let mut cmd = Self::build_command( + config, + &allowed_features, + &workspace.workspace_root().to_path_buf(), + )?; cmd.args(["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?; res.error = Some(error); @@ -169,6 +181,7 @@ impl WorkspaceBuildScripts { config: &CargoConfig, workspaces: &[&CargoWorkspace], progress: &dyn Fn(String), + workspace_root: &AbsPathBuf, ) -> io::Result> { assert_eq!(config.invocation_strategy, InvocationStrategy::Once); @@ -181,7 +194,7 @@ impl WorkspaceBuildScripts { )) } }; - let cmd = Self::build_command(config, &Default::default())?; + let cmd = Self::build_command(config, &Default::default(), workspace_root)?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index c04eddc56fb7f..e14090a972860 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -407,6 +407,7 @@ impl ProjectWorkspace { workspaces: &[ProjectWorkspace], config: &CargoConfig, progress: &dyn Fn(String), + workspace_root: &AbsPathBuf, ) -> Vec> { if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace) || config.run_build_script_command.is_none() @@ -421,11 +422,13 @@ impl ProjectWorkspace { _ => None, }) .collect(); - let outputs = &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) { - Ok(it) => Ok(it.into_iter()), - // io::Error is not Clone? - Err(e) => Err(sync::Arc::new(e)), - }; + let outputs = + &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress, workspace_root) + { + Ok(it) => Ok(it.into_iter()), + // io::Error is not Clone? + Err(e) => Err(sync::Arc::new(e)), + }; workspaces .iter() diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 8e3fa7fa4dace..d90933b004d38 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -275,6 +275,8 @@ impl GlobalState { tracing::info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(); + let root_path = self.config.root_path().clone(); + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); @@ -284,7 +286,12 @@ impl GlobalState { sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() } }; - let res = ProjectWorkspace::run_all_build_scripts(&workspaces, &config, &progress); + let res = ProjectWorkspace::run_all_build_scripts( + &workspaces, + &config, + &progress, + &root_path, + ); sender.send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))).unwrap(); }); diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 78411e2d58d09..58a99cc4471a9 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -682,13 +682,12 @@ version = \"0.0.0\" ); } -#[test] -fn out_dirs_check() { +fn out_dirs_check_impl(root_contains_symlink: bool) { if skip_slow_tests() { return; } - let server = Project::with_fixture( + let mut server = Project::with_fixture( r###" //- /Cargo.toml [package] @@ -745,20 +744,26 @@ fn main() { let another_str = include_str!("main.rs"); } "###, - ) - .with_config(serde_json::json!({ - "cargo": { - "buildScripts": { - "enable": true - }, - "sysroot": null, - "extraEnv": { - "RUSTC_BOOTSTRAP": "1" + ); + + if root_contains_symlink { + server = server.with_root_dir_contains_symlink(); + } + + let server = server + .with_config(serde_json::json!({ + "cargo": { + "buildScripts": { + "enable": true + }, + "sysroot": null, + "extraEnv": { + "RUSTC_BOOTSTRAP": "1" + } } - } - })) - .server() - .wait_until_workspace_is_loaded(); + })) + .server() + .wait_until_workspace_is_loaded(); let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( @@ -831,6 +836,16 @@ fn main() { ); } +#[test] +fn out_dirs_check() { + out_dirs_check_impl(false); +} + +#[test] +fn root_contains_symlink_out_dirs_check() { + out_dirs_check_impl(true); +} + #[test] #[cfg(any(feature = "sysroot-abi", rust_analyzer))] fn resolve_proc_macro() { diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 106b99cb9352f..e16990eabd092 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -23,6 +23,7 @@ pub(crate) struct Project<'a> { tmp_dir: Option, roots: Vec, config: serde_json::Value, + root_dir_contains_symlink: bool, } impl Project<'_> { @@ -45,6 +46,7 @@ impl Project<'_> { "enable": false, } }), + root_dir_contains_symlink: false, } } @@ -58,6 +60,11 @@ impl Project<'_> { self } + pub(crate) fn with_root_dir_contains_symlink(mut self) -> Self { + self.root_dir_contains_symlink = true; + self + } + pub(crate) fn with_config(mut self, config: serde_json::Value) -> Self { fn merge(dst: &mut serde_json::Value, src: serde_json::Value) { match (dst, src) { @@ -74,7 +81,14 @@ impl Project<'_> { } pub(crate) fn server(self) -> Server { - let tmp_dir = self.tmp_dir.unwrap_or_else(TestDir::new); + let tmp_dir = self.tmp_dir.unwrap_or_else(|| { + if self.root_dir_contains_symlink { + TestDir::new_symlink() + } else { + TestDir::new() + } + }); + static INIT: Once = Once::new(); INIT.call_once(|| { let filter: tracing_subscriber::filter::Targets = diff --git a/crates/rust-analyzer/tests/slow-tests/testdir.rs b/crates/rust-analyzer/tests/slow-tests/testdir.rs index f7fceb5888696..45241ad63e578 100644 --- a/crates/rust-analyzer/tests/slow-tests/testdir.rs +++ b/crates/rust-analyzer/tests/slow-tests/testdir.rs @@ -11,6 +11,14 @@ pub(crate) struct TestDir { impl TestDir { pub(crate) fn new() -> TestDir { + return TestDir::new_dir(false); + } + + pub(crate) fn new_symlink() -> TestDir { + return TestDir::new_dir(true); + } + + fn new_dir(symlink: bool) -> TestDir { let temp_dir = std::env::temp_dir(); // On MacOS builders on GitHub actions, the temp dir is a symlink, and // that causes problems down the line. Specifically: @@ -33,10 +41,24 @@ impl TestDir { continue; } fs::create_dir_all(&path).unwrap(); + + #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] + if symlink { + let symlink_path = base.join(format!("{pid}_{cnt}_symlink")); + #[cfg(any(target_os = "macos", target_os = "linux"))] + std::os::unix::fs::symlink(path, &symlink_path).unwrap(); + + #[cfg(target_os = "windows")] + std::os::windows::fs::symlink_dir(path, &symlink_path).unwrap(); + + return TestDir { path: symlink_path, keep: false }; + } + return TestDir { path, keep: false }; } panic!("Failed to create a temporary directory") } + #[allow(unused)] pub(crate) fn keep(mut self) -> TestDir { self.keep = true; @@ -52,9 +74,22 @@ impl Drop for TestDir { if self.keep { return; } + + let filetype = fs::symlink_metadata(&self.path).unwrap().file_type(); + let actual_path = filetype.is_symlink().then(|| fs::read_link(&self.path).unwrap()); + + if let Some(actual_path) = actual_path { + remove_dir_all(&actual_path).unwrap_or_else(|err| { + panic!( + "failed to remove temporary link to directory {}: {err}", + actual_path.display() + ) + }) + } + remove_dir_all(&self.path).unwrap_or_else(|err| { panic!("failed to remove temporary directory {}: {err}", self.path.display()) - }) + }); } } From 7c3744e4db426b197e79034481298004fc24dbf3 Mon Sep 17 00:00:00 2001 From: roife Date: Fri, 12 Jan 2024 15:57:38 +0800 Subject: [PATCH 007/118] internal: Speedup line index calculation via NEON for aarch64 --- lib/line-index/src/lib.rs | 112 +++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/lib/line-index/src/lib.rs b/lib/line-index/src/lib.rs index 58f266d67f629..42be26f5725ca 100644 --- a/lib/line-index/src/lib.rs +++ b/lib/line-index/src/lib.rs @@ -227,6 +227,22 @@ fn analyze_source_file_dispatch( } } +#[cfg(target_arch = "aarch64")] +fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut IntMap>, +) { + if std::arch::is_aarch64_feature_detected!("neon") { + // SAFETY: NEON support was checked + unsafe { + analyze_source_file_neon(src, lines, multi_byte_chars); + } + } else { + analyze_source_file_generic(src, src.len(), TextSize::from(0), lines, multi_byte_chars); + } +} + /// Checks 16 byte chunks of text at a time. If the chunk contains /// something other than printable ASCII characters and newlines, the /// function falls back to the generic implementation. Otherwise it uses @@ -322,7 +338,101 @@ unsafe fn analyze_source_file_sse2( } } -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +#[target_feature(enable = "neon")] +#[cfg(any(target_arch = "aarch64"))] +// See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon +// +// The mask is a 64-bit integer, where each 4-bit corresponds to a u8 in the +// input vector. The least significant 4 bits correspond to the first byte in +// the vector. +unsafe fn move_mask(v: std::arch::aarch64::uint8x16_t) -> u64 { + use std::arch::aarch64::*; + + let nibble_mask = vshrn_n_u16(vreinterpretq_u16_u8(v), 4); + vget_lane_u64(vreinterpret_u64_u8(nibble_mask), 0) +} + +#[target_feature(enable = "neon")] +#[cfg(any(target_arch = "aarch64"))] +unsafe fn analyze_source_file_neon( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut IntMap>, +) { + use std::arch::aarch64::*; + + const CHUNK_SIZE: usize = 16; + + let src_bytes = src.as_bytes(); + + let chunk_count = src.len() / CHUNK_SIZE; + + let newline = vdupq_n_s8(b'\n' as i8); + + // This variable keeps track of where we should start decoding a + // chunk. If a multi-byte character spans across chunk boundaries, + // we need to skip that part in the next chunk because we already + // handled it. + let mut intra_chunk_offset = 0; + + for chunk_index in 0..chunk_count { + let ptr = src_bytes.as_ptr() as *const i8; + let chunk = vld1q_s8(ptr.add(chunk_index * CHUNK_SIZE)); + + // For character in the chunk, see if its byte value is < 0, which + // indicates that it's part of a UTF-8 char. + let multibyte_test = vcltzq_s8(chunk); + // Create a bit mask from the comparison results. + let multibyte_mask = move_mask(multibyte_test); + + // If the bit mask is all zero, we only have ASCII chars here: + if multibyte_mask == 0 { + assert!(intra_chunk_offset == 0); + + // Check for newlines in the chunk + let newlines_test = vceqq_s8(chunk, newline); + let mut newlines_mask = move_mask(newlines_test); + + // If the bit mask is all zero, there are no newlines in this chunk. + if newlines_mask != 0 { + let output_offset = TextSize::from((chunk_index * CHUNK_SIZE + 1) as u32); + + while newlines_mask != 0 { + let trailing_zeros = newlines_mask.trailing_zeros(); + let index = trailing_zeros / 4; + + lines.push(TextSize::from(index) + output_offset); + + // Clear the current 4-bit, so we can find the next one. + newlines_mask &= (!0xF) << trailing_zeros; + } + } + continue; + } + + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start..], + CHUNK_SIZE - intra_chunk_offset, + TextSize::from(scan_start as u32), + lines, + multi_byte_chars, + ); + } + + let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset; + if tail_start < src.len() { + analyze_source_file_generic( + &src[tail_start..], + src.len() - tail_start, + TextSize::from(tail_start as u32), + lines, + multi_byte_chars, + ); + } +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] // The target (or compiler version) does not support SSE2 ... fn analyze_source_file_dispatch( src: &str, From d0b310fc93c28611f8f13c22834aa48b99e77767 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 12 Jan 2024 17:41:46 +0800 Subject: [PATCH 008/118] fix: add USE_TREE_LIST_FIRST_SET --- crates/parser/src/grammar/items/use_item.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/parser/src/grammar/items/use_item.rs b/crates/parser/src/grammar/items/use_item.rs index b397454173569..f689c06b31c29 100644 --- a/crates/parser/src/grammar/items/use_item.rs +++ b/crates/parser/src/grammar/items/use_item.rs @@ -78,7 +78,9 @@ fn use_tree(p: &mut Parser<'_>, top_level: bool) -> bool { } pub(super) const USE_TREE_LIST_RECOVERY_SET: TokenSet = - TokenSet::new(&[T![;], T![,], T![.], T![ident]]); + TokenSet::new(&[T![;], T![,], T![.], T![ident]]).union(ITEM_RECOVERY_SET); + +pub(super) const USE_TREE_LIST_FIRST_SET: TokenSet = TokenSet::new(&[T!['{'], T![ident]]); // test use_tree_list // use {a, b, c}; @@ -91,7 +93,7 @@ pub(crate) fn use_tree_list(p: &mut Parser<'_>) { // use b; // struct T; // fn test() {} - delimited(p, T!['{'], T!['}'], T![,], USE_TREE_LIST_RECOVERY_SET, |p: &mut Parser<'_>| { + delimited(p, T!['{'], T!['}'], T![,], USE_TREE_LIST_FIRST_SET, |p: &mut Parser<'_>| { use_tree(p, false) || p.at_ts(USE_TREE_LIST_RECOVERY_SET) }); From 6033b66ce00f59ebc95a00db57099c9d898b5e00 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Fri, 12 Jan 2024 17:42:18 +0800 Subject: [PATCH 009/118] test: correct expected test result --- crates/parser/test_data/parser/err/0036_partial_use.rast | 8 ++------ .../inline/err/0026_use_tree_list_err_recovery.rast | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/crates/parser/test_data/parser/err/0036_partial_use.rast b/crates/parser/test_data/parser/err/0036_partial_use.rast index ad4bc89e83aa5..e27c941793915 100644 --- a/crates/parser/test_data/parser/err/0036_partial_use.rast +++ b/crates/parser/test_data/parser/err/0036_partial_use.rast @@ -20,8 +20,7 @@ SOURCE_FILE PATH_SEGMENT NAME_REF IDENT "Error" - ERROR - SEMICOLON ";" + SEMICOLON ";" WHITESPACE "\n" USE USE_KW "use" @@ -38,7 +37,4 @@ SOURCE_FILE IDENT "io" SEMICOLON ";" WHITESPACE "\n" -error 22: expected COMMA -error 22: expected one of `*`, `::`, `{`, `self`, `super` or an identifier -error 23: expected R_CURLY -error 23: expected SEMICOLON +error 22: expected R_CURLY diff --git a/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast b/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast index 7732bdf72f40b..cb90b093ba0de 100644 --- a/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast +++ b/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast @@ -10,8 +10,7 @@ SOURCE_FILE PATH_SEGMENT NAME_REF IDENT "a" - ERROR - SEMICOLON ";" + SEMICOLON ";" WHITESPACE "\n" USE USE_KW "use" @@ -44,7 +43,4 @@ SOURCE_FILE L_CURLY "{" R_CURLY "}" WHITESPACE "\n" -error 6: expected COMMA -error 6: expected one of `*`, `::`, `{`, `self`, `super` or an identifier -error 7: expected R_CURLY -error 7: expected SEMICOLON +error 6: expected R_CURLY From df538288e09a45483a56a9af99fb7fd5333d53d8 Mon Sep 17 00:00:00 2001 From: roife Date: Fri, 12 Jan 2024 20:27:49 +0800 Subject: [PATCH 010/118] internal: add inline for move_mask in line-index --- lib/line-index/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/line-index/src/lib.rs b/lib/line-index/src/lib.rs index 42be26f5725ca..1614504f80a36 100644 --- a/lib/line-index/src/lib.rs +++ b/lib/line-index/src/lib.rs @@ -340,6 +340,7 @@ unsafe fn analyze_source_file_sse2( #[target_feature(enable = "neon")] #[cfg(any(target_arch = "aarch64"))] +#[inline] // See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon // // The mask is a 64-bit integer, where each 4-bit corresponds to a u8 in the @@ -393,7 +394,7 @@ unsafe fn analyze_source_file_neon( let newlines_test = vceqq_s8(chunk, newline); let mut newlines_mask = move_mask(newlines_test); - // If the bit mask is all zero, there are no newlines in this chunk. + // If the bit mask is not all zero, there are newlines in this chunk. if newlines_mask != 0 { let output_offset = TextSize::from((chunk_index * CHUNK_SIZE + 1) as u32); From 22ae5f49baa76965281b8a3a52bcead99094feaf Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Fri, 12 Jan 2024 15:09:04 +0300 Subject: [PATCH 011/118] order merged use trees --- .../ide-assists/src/handlers/bool_to_enum.rs | 4 +- .../ide-assists/src/handlers/merge_imports.rs | 24 +- .../replace_qualified_name_with_use.rs | 2 +- crates/ide-db/src/imports/insert_use/tests.rs | 28 ++- crates/ide-db/src/imports/merge_imports.rs | 220 +++++++++++++----- 5 files changed, 186 insertions(+), 92 deletions(-) diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs index dc1952c3ff3ce..bec540790b2c7 100644 --- a/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -986,7 +986,7 @@ fn foo() { } //- /main.rs -use foo::{Foo, Bool}; +use foo::{Bool, Foo}; mod foo; @@ -1662,7 +1662,7 @@ impl Foo { } //- /foo.rs -use crate::{Foo, Bool}; +use crate::{Bool, Foo}; fn foo() -> bool { Foo::BOOL == Bool::True diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs index d7ddc5f23f5dc..78f3825c3c0bc 100644 --- a/crates/ide-assists/src/handlers/merge_imports.rs +++ b/crates/ide-assists/src/handlers/merge_imports.rs @@ -179,7 +179,7 @@ use std::fmt::Debug; use std::fmt$0::Display; ", r" -use std::fmt::{Display, Debug}; +use std::fmt::{Debug, Display}; ", ); } @@ -206,7 +206,7 @@ use std::fmt::{self, Display}; use std::{fmt, $0fmt::Display}; ", r" -use std::{fmt::{Display, self}}; +use std::{fmt::{self, Display}}; ", ); } @@ -318,7 +318,7 @@ use std::{fmt::{Debug, Display}}; use std::{fmt::Debug, fmt$0::Display}; ", r" -use std::{fmt::{Display, Debug}}; +use std::{fmt::{Debug, Display}}; ", ); } @@ -332,7 +332,7 @@ use std$0::{fmt::{Write, Display}}; use std::{fmt::{self, Debug}}; ", r" -use std::{fmt::{Write, Display, self, Debug}}; +use std::{fmt::{self, Debug, Display, Write}}; ", ); } @@ -346,7 +346,7 @@ use std$0::{fmt::{self, Debug}}; use std::{fmt::{Write, Display}}; ", r" -use std::{fmt::{self, Debug, Write, Display}}; +use std::{fmt::{self, Debug, Display, Write}}; ", ); } @@ -359,7 +359,7 @@ use std::{fmt::{self, Debug, Write, Display}}; use std::{fmt$0::{self, Debug}, fmt::{Write, Display}}; ", r" -use std::{fmt::{self, Debug, Write, Display}}; +use std::{fmt::{self, Debug, Display, Write}}; ", ); } @@ -401,7 +401,7 @@ use std$0::{fmt::*}; use std::{fmt::{self, Display}}; ", r" -use std::{fmt::{*, self, Display}}; +use std::{fmt::{self, Display, *}}; ", ) } @@ -496,7 +496,7 @@ use foo::$0{ ", r" use foo::{ - FooBar, bar::baz, + bar::baz, FooBar }; ", ) @@ -521,7 +521,7 @@ use foo::$0*; use foo::bar::Baz; ", r" -use foo::{*, bar::Baz}; +use foo::{bar::Baz, *}; ", ); } @@ -539,7 +539,7 @@ $0use std::fmt::Result; ", r" use std::fmt::Error; -use std::fmt::{Display, Debug, Write}; +use std::fmt::{Debug, Display, Write}; use std::fmt::Result; ", ); @@ -560,7 +560,7 @@ use std::{ r" use std::{ fmt::Error, - fmt::{Display, Debug, Write}, + fmt::{Debug, Display, Write}, fmt::Result, };", ); @@ -568,7 +568,7 @@ use std::{ check_assist( merge_imports, r"use std::$0{fmt::Display, fmt::Debug}$0;", - r"use std::{fmt::{Display, Debug}};", + r"use std::{fmt::{Debug, Display}};", ); } } diff --git a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index f03eb6118a524..ba1c25fa5a749 100644 --- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -313,7 +313,7 @@ fn main() { ", r" mod std { pub mod fmt { pub trait Display {} } } -use std::fmt::{Display, self}; +use std::fmt::{self, Display}; fn main() { fmt; diff --git a/crates/ide-db/src/imports/insert_use/tests.rs b/crates/ide-db/src/imports/insert_use/tests.rs index a3abce8964247..4e626ada83e4c 100644 --- a/crates/ide-db/src/imports/insert_use/tests.rs +++ b/crates/ide-db/src/imports/insert_use/tests.rs @@ -596,7 +596,7 @@ fn merge_groups_full() { #[test] fn merge_groups_long_full() { - check_crate("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Qux, Baz};") + check_crate("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") } #[test] @@ -604,7 +604,7 @@ fn merge_groups_long_last() { check_module( "std::foo::bar::Baz", r"use std::foo::bar::Qux;", - r"use std::foo::bar::{Qux, Baz};", + r"use std::foo::bar::{Baz, Qux};", ) } @@ -613,7 +613,7 @@ fn merge_groups_long_full_list() { check_crate( "std::foo::bar::Baz", r"use std::foo::bar::{Qux, Quux};", - r"use std::foo::bar::{Qux, Quux, Baz};", + r"use std::foo::bar::{Baz, Quux, Qux};", ) } @@ -622,7 +622,7 @@ fn merge_groups_long_last_list() { check_module( "std::foo::bar::Baz", r"use std::foo::bar::{Qux, Quux};", - r"use std::foo::bar::{Qux, Quux, Baz};", + r"use std::foo::bar::{Baz, Quux, Qux};", ) } @@ -631,7 +631,7 @@ fn merge_groups_long_full_nested() { check_crate( "std::foo::bar::Baz", r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}, Baz};", + r"use std::foo::bar::{quux::{Fez, Fizz}, Baz, Qux};", ) } @@ -650,7 +650,7 @@ fn merge_groups_full_nested_deep() { check_crate( "std::foo::bar::quux::Baz", r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz, Baz}};", + r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", ) } @@ -659,7 +659,7 @@ fn merge_groups_full_nested_long() { check_crate( "std::foo::bar::Baz", r"use std::{foo::bar::Qux};", - r"use std::{foo::bar::{Qux, Baz}};", + r"use std::{foo::bar::{Baz, Qux}};", ); } @@ -668,7 +668,7 @@ fn merge_groups_last_nested_long() { check_crate( "std::foo::bar::Baz", r"use std::{foo::bar::Qux};", - r"use std::{foo::bar::{Qux, Baz}};", + r"use std::{foo::bar::{Baz, Qux}};", ); } @@ -733,7 +733,7 @@ fn merge_mod_into_glob() { check_with_config( "token::TokenKind", r"use token::TokenKind::*;", - r"use token::TokenKind::{*, self};", + r"use token::TokenKind::{self, *};", &InsertUseConfig { granularity: ImportGranularity::Crate, enforce_granularity: true, @@ -742,7 +742,6 @@ fn merge_mod_into_glob() { skip_glob_imports: false, }, ) - // FIXME: have it emit `use token::TokenKind::{self, *}`? } #[test] @@ -750,7 +749,7 @@ fn merge_self_glob() { check_with_config( "self", r"use self::*;", - r"use self::{*, self};", + r"use self::{self, *};", &InsertUseConfig { granularity: ImportGranularity::Crate, enforce_granularity: true, @@ -759,7 +758,6 @@ fn merge_self_glob() { skip_glob_imports: false, }, ) - // FIXME: have it emit `use {self, *}`? } #[test] @@ -769,7 +767,7 @@ fn merge_glob() { r" use syntax::{SyntaxKind::*};", r" -use syntax::{SyntaxKind::{*, self}};", +use syntax::{SyntaxKind::{self, *}};", ) } @@ -778,7 +776,7 @@ fn merge_glob_nested() { check_crate( "foo::bar::quux::Fez", r"use foo::bar::{Baz, quux::*};", - r"use foo::bar::{Baz, quux::{*, Fez}};", + r"use foo::bar::{Baz, quux::{Fez, *}};", ) } @@ -787,7 +785,7 @@ fn merge_nested_considers_first_segments() { check_crate( "hir_ty::display::write_bounds_like_dyn_trait", r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", - r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", + r"use hir_ty::{autoderef, display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter}, method_resolution};", ); } diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index df43bb553fc13..c64599c6b5834 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs @@ -1,10 +1,14 @@ //! Handle syntactic aspects of merging UseTrees. use std::cmp::Ordering; +use std::iter::{empty, successors}; use itertools::{EitherOrBoth, Itertools}; +use parser::{SyntaxKind, T}; +use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ - ast::{self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind}, - ted, TokenText, + ast::{self, make, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind}, + ted::{self, Position}, + SyntaxElement, TokenText, }; use crate::syntax_helpers::node_ext::vis_eq; @@ -97,20 +101,35 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) // same as a `filter` op). .map(|tree| merge.is_tree_allowed(&tree).then_some(tree)) .collect::>()?; + // Preserves some positional formatting info before sorting. + let positional_formatting_map: FxHashMap<_, _> = use_trees + .iter() + .enumerate() + .filter_map(|(idx, tree)| { + formatting_whitespace(&SyntaxElement::from(tree.syntax().clone())) + .map(|trivia| (idx, trivia)) + }) + .collect(); + let closing_formatting_trivia = if use_trees.len() > 0 { + lhs.use_tree_list() + .and_then(|list| list.r_curly_token()) + .and_then(|token| formatting_whitespace(&SyntaxElement::from(token))) + } else { + None + }; + // Sorts the use trees similar to rustfmt's algorithm for ordering imports + // (see `use_tree_cmp` doc). use_trees.sort_unstable_by(|a, b| use_tree_cmp(a, b)); for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { if !merge.is_tree_allowed(&rhs_t) { return None; } - let rhs_path = rhs_t.path(); - match use_trees - .binary_search_by(|lhs_t| path_cmp_bin_search(lhs_t.path(), rhs_path.as_ref())) - { + match use_trees.binary_search_by(|lhs_t| use_tree_cmp_bin_search(lhs_t, &rhs_t)) { Ok(idx) => { let lhs_t = &mut use_trees[idx]; let lhs_path = lhs_t.path()?; - let rhs_path = rhs_path?; + let rhs_path = rhs_t.path()?; let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; if lhs_prefix == lhs_path && rhs_prefix == rhs_path { let tree_is_self = |tree: &ast::UseTree| { @@ -159,13 +178,66 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) { return None } - Err(idx) => { - use_trees.insert(idx, rhs_t.clone()); - lhs.get_or_create_use_tree_list().add_use_tree(rhs_t); + Err(insert_idx) => { + use_trees.insert(insert_idx, rhs_t.clone()); + match lhs.use_tree_list() { + // Creates a new use tree list with the item. + None => lhs.get_or_create_use_tree_list().add_use_tree(rhs_t), + // Recreates the use tree list with sorted items (see `use_tree_cmp` doc). + // Also attempts to preserve formatting (but only in terms of index-based + // "positions" of new lines and indents). + Some(old_list) => { + ted::remove(old_list.syntax()); + + let new_list = make::use_tree_list(empty()).clone_for_update(); + let mut elements = Vec::new(); + for (idx, tree) in use_trees.iter().enumerate() { + if idx > 0 { + elements.push(make::token(T![,]).into()); + } + match positional_formatting_map.get(&idx) { + None if idx > 0 => { + elements.push(make::tokens::single_space().into()) + } + Some(prev_trivia) => { + elements.extend(prev_trivia.clone().into_iter()) + } + _ => (), + } + elements.push(tree.syntax().clone().into()); + } + if let Some(ref trivia) = closing_formatting_trivia { + elements.extend(trivia.clone().into_iter()) + } + + let trees_pos = match new_list.l_curly_token() { + Some(l_curly) => Position::after(l_curly), + None => Position::last_child_of(new_list.syntax()), + }; + ted::insert_all_raw(trees_pos, elements); + + let list_pos = Position::last_child_of(lhs.syntax()); + ted::insert_raw(list_pos, new_list.syntax()); + } + } } } } - Some(()) + return Some(()); + + // Returns all trivia preceding a syntax element if it may be relevant to formatting + // (i.e. includes at least one new line character). + fn formatting_whitespace(elem: &SyntaxElement) -> Option> { + let succ = + |item: &SyntaxElement| item.prev_sibling_or_token().filter(|it| it.kind().is_trivia()); + let first = succ(elem); + let trivia_set: FxHashSet<_> = successors(first, succ).collect(); + let contains_formatting_whitespace = trivia_set.iter().any(|it| { + it.kind() == SyntaxKind::WHITESPACE + && it.as_token().is_some_and(|token| token.text().contains('\n')) + }); + contains_formatting_whitespace.then_some(trivia_set) + } } /// Traverses both paths until they differ, returning the common prefix of both. @@ -190,70 +262,39 @@ pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast } } -/// Path comparison func for binary searching for merging. -fn path_cmp_bin_search(lhs: Option, rhs: Option<&ast::Path>) -> Ordering { - match (lhs.as_ref().and_then(ast::Path::first_segment), rhs.and_then(ast::Path::first_segment)) - { - (None, None) => Ordering::Equal, - (None, Some(_)) => Ordering::Less, +/// Use tree comparison func for binary searching for merging. +fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering { + match ( + lhs.path().as_ref().and_then(ast::Path::first_segment), + rhs.path().as_ref().and_then(ast::Path::first_segment), + ) { + (None, None) => match (lhs.is_simple_path(), lhs.is_simple_path()) { + (true, true) => Ordering::Equal, + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(lhs, rhs, false), + }, + (Some(_), None) if !rhs.is_simple_path() => Ordering::Less, (Some(_), None) => Ordering::Greater, + (None, Some(_)) if !lhs.is_simple_path() => Ordering::Greater, + (None, Some(_)) => Ordering::Less, (Some(ref a), Some(ref b)) => path_segment_cmp(a, b), } } -/// Orders use trees following `rustfmt`'s algorithm for ordering imports, which is `self`, `super` and `crate` first, -/// then identifier imports with lowercase ones first and upper snake case (e.g. UPPER_SNAKE_CASE) ones last, -/// then glob imports, and at last list imports. +/// Orders use trees following `rustfmt`'s algorithm for ordering imports, which is `self`, `super` +/// and `crate` first, then identifier imports with lowercase ones first and upper snake case +/// (e.g. UPPER_SNAKE_CASE) ones last, then glob imports, and at last list imports. /// /// Example foo::{self, foo, baz, Baz, Qux, FOO_BAZ, *, {Bar}} /// Ref: . pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering { - let tiebreak_by_glob_or_alias = || match (a.star_token().is_some(), b.star_token().is_some()) { - (true, false) => Ordering::Greater, - (false, true) => Ordering::Less, - _ => match (a.rename(), b.rename()) { - (None, None) => Ordering::Equal, - (Some(_), None) => Ordering::Greater, - (None, Some(_)) => Ordering::Less, - (Some(a_rename), Some(b_rename)) => a_rename - .name() - .as_ref() - .map(ast::Name::text) - .as_ref() - .map_or("_", TokenText::as_str) - .cmp( - b_rename - .name() - .as_ref() - .map(ast::Name::text) - .as_ref() - .map_or("_", TokenText::as_str), - ), - }, - }; - let tiebreak_by_tree_list_glob_or_alias = || match (a.use_tree_list(), b.use_tree_list()) { - (None, None) => tiebreak_by_glob_or_alias(), - (Some(_), None) => Ordering::Greater, - (None, Some(_)) => Ordering::Less, - (Some(a_list), Some(b_list)) => a_list - .use_trees() - .zip_longest(b_list.use_trees()) - .find_map(|zipped| match zipped { - EitherOrBoth::Both(ref a_tree, ref b_tree) => match use_tree_cmp(a_tree, b_tree) { - Ordering::Equal => None, - ord => Some(ord), - }, - EitherOrBoth::Left(_) => Some(Ordering::Greater), - EitherOrBoth::Right(_) => Some(Ordering::Less), - }) - .unwrap_or_else(tiebreak_by_glob_or_alias), - }; match (a.path(), b.path()) { (None, None) => match (a.is_simple_path(), b.is_simple_path()) { (true, true) => Ordering::Equal, (true, false) => Ordering::Less, (false, true) => Ordering::Greater, - (false, false) => tiebreak_by_tree_list_glob_or_alias(), + (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(a, b, true), }, (Some(_), None) if !b.is_simple_path() => Ordering::Less, (Some(_), None) => Ordering::Greater, @@ -277,7 +318,7 @@ pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering { EitherOrBoth::Right(_) if a.is_simple_path() => Some(Ordering::Less), EitherOrBoth::Right(_) => Some(Ordering::Greater), }) - .unwrap_or_else(tiebreak_by_tree_list_glob_or_alias) + .unwrap_or_else(|| use_tree_cmp_by_tree_list_glob_or_alias(a, b, true)) } } } @@ -338,6 +379,61 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { } } +// Orders for use trees with equal paths (see `use_tree_cmp` for details about use tree ordering). +// +// If the `strict` parameter is set to true and both trees have tree lists, the tree lists are +// ordered by calling `use_tree_cmp` on their "sub-tree" pairs until either the tie is broken +// or tree list equality is confirmed, otherwise (i.e. if either `strict` is false or at least +// one of the trees does *not* have tree list), this potentially recursive step is skipped, +// and only the presence of a glob pattern or an alias is used to determine the ordering. +fn use_tree_cmp_by_tree_list_glob_or_alias( + a: &ast::UseTree, + b: &ast::UseTree, + strict: bool, +) -> Ordering { + let cmp_by_glob_or_alias = || match (a.star_token().is_some(), b.star_token().is_some()) { + (true, false) => Ordering::Greater, + (false, true) => Ordering::Less, + _ => match (a.rename(), b.rename()) { + (None, None) => Ordering::Equal, + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(a_rename), Some(b_rename)) => a_rename + .name() + .as_ref() + .map(ast::Name::text) + .as_ref() + .map_or("_", TokenText::as_str) + .cmp( + b_rename + .name() + .as_ref() + .map(ast::Name::text) + .as_ref() + .map_or("_", TokenText::as_str), + ), + }, + }; + + match (a.use_tree_list(), b.use_tree_list()) { + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(a_list), Some(b_list)) if strict => a_list + .use_trees() + .zip_longest(b_list.use_trees()) + .find_map(|zipped| match zipped { + EitherOrBoth::Both(ref a_tree, ref b_tree) => match use_tree_cmp(a_tree, b_tree) { + Ordering::Equal => None, + ord => Some(ord), + }, + EitherOrBoth::Left(_) => Some(Ordering::Greater), + EitherOrBoth::Right(_) => Some(Ordering::Less), + }) + .unwrap_or_else(cmp_by_glob_or_alias), + _ => cmp_by_glob_or_alias(), + } +} + pub fn eq_visibility(vis0: Option, vis1: Option) -> bool { match (vis0, vis1) { (None, None) => true, From db3f0f17612fb4dfd0ff58cb99462cbfc614ffac Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Fri, 12 Jan 2024 18:23:58 +0300 Subject: [PATCH 012/118] improve use tree simple path comparison logic --- crates/ide-db/src/imports/merge_imports.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index c64599c6b5834..4e56ef5b4d2bd 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs @@ -264,19 +264,21 @@ pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast /// Use tree comparison func for binary searching for merging. fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering { + let lhs_is_simple_path = lhs.is_simple_path() && lhs.rename().is_none(); + let rhs_is_simple_path = rhs.is_simple_path() && rhs.rename().is_none(); match ( lhs.path().as_ref().and_then(ast::Path::first_segment), rhs.path().as_ref().and_then(ast::Path::first_segment), ) { - (None, None) => match (lhs.is_simple_path(), lhs.is_simple_path()) { + (None, None) => match (lhs_is_simple_path, rhs_is_simple_path) { (true, true) => Ordering::Equal, (true, false) => Ordering::Less, (false, true) => Ordering::Greater, (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(lhs, rhs, false), }, - (Some(_), None) if !rhs.is_simple_path() => Ordering::Less, + (Some(_), None) if !rhs_is_simple_path => Ordering::Less, (Some(_), None) => Ordering::Greater, - (None, Some(_)) if !lhs.is_simple_path() => Ordering::Greater, + (None, Some(_)) if !lhs_is_simple_path => Ordering::Greater, (None, Some(_)) => Ordering::Less, (Some(ref a), Some(ref b)) => path_segment_cmp(a, b), } @@ -289,16 +291,18 @@ fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering { /// Example foo::{self, foo, baz, Baz, Qux, FOO_BAZ, *, {Bar}} /// Ref: . pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering { + let a_is_simple_path = a.is_simple_path() && a.rename().is_none(); + let b_is_simple_path = b.is_simple_path() && b.rename().is_none(); match (a.path(), b.path()) { - (None, None) => match (a.is_simple_path(), b.is_simple_path()) { + (None, None) => match (a_is_simple_path, b_is_simple_path) { (true, true) => Ordering::Equal, (true, false) => Ordering::Less, (false, true) => Ordering::Greater, (false, false) => use_tree_cmp_by_tree_list_glob_or_alias(a, b, true), }, - (Some(_), None) if !b.is_simple_path() => Ordering::Less, + (Some(_), None) if !b_is_simple_path => Ordering::Less, (Some(_), None) => Ordering::Greater, - (None, Some(_)) if !a.is_simple_path() => Ordering::Greater, + (None, Some(_)) if !a_is_simple_path => Ordering::Greater, (None, Some(_)) => Ordering::Less, (Some(ref a_path), Some(ref b_path)) => { // cmp_by would be useful for us here but that is currently unstable @@ -313,9 +317,9 @@ pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering { ord => Some(ord), } } - EitherOrBoth::Left(_) if b.is_simple_path() => Some(Ordering::Greater), + EitherOrBoth::Left(_) if b_is_simple_path => Some(Ordering::Greater), EitherOrBoth::Left(_) => Some(Ordering::Less), - EitherOrBoth::Right(_) if a.is_simple_path() => Some(Ordering::Less), + EitherOrBoth::Right(_) if a_is_simple_path => Some(Ordering::Less), EitherOrBoth::Right(_) => Some(Ordering::Greater), }) .unwrap_or_else(|| use_tree_cmp_by_tree_list_glob_or_alias(a, b, true)) From d4b43d5a516009c67b8abcca3a399d554f4ba8aa Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Sun, 14 Jan 2024 19:49:50 +0300 Subject: [PATCH 013/118] improve ordered use tree merging implementation --- crates/ide-db/src/imports/merge_imports.rs | 117 +++++++++------------ 1 file changed, 50 insertions(+), 67 deletions(-) diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index 4e56ef5b4d2bd..52c94678ade77 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs @@ -1,14 +1,14 @@ //! Handle syntactic aspects of merging UseTrees. use std::cmp::Ordering; -use std::iter::{empty, successors}; +use std::iter::empty; use itertools::{EitherOrBoth, Itertools}; -use parser::{SyntaxKind, T}; -use rustc_hash::{FxHashMap, FxHashSet}; +use parser::T; use syntax::{ + algo, ast::{self, make, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind}, ted::{self, Position}, - SyntaxElement, TokenText, + Direction, TokenText, }; use crate::syntax_helpers::node_ext::vis_eq; @@ -101,22 +101,6 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) // same as a `filter` op). .map(|tree| merge.is_tree_allowed(&tree).then_some(tree)) .collect::>()?; - // Preserves some positional formatting info before sorting. - let positional_formatting_map: FxHashMap<_, _> = use_trees - .iter() - .enumerate() - .filter_map(|(idx, tree)| { - formatting_whitespace(&SyntaxElement::from(tree.syntax().clone())) - .map(|trivia| (idx, trivia)) - }) - .collect(); - let closing_formatting_trivia = if use_trees.len() > 0 { - lhs.use_tree_list() - .and_then(|list| list.r_curly_token()) - .and_then(|token| formatting_whitespace(&SyntaxElement::from(token))) - } else { - None - }; // Sorts the use trees similar to rustfmt's algorithm for ordering imports // (see `use_tree_cmp` doc). use_trees.sort_unstable_by(|a, b| use_tree_cmp(a, b)); @@ -184,60 +168,59 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) // Creates a new use tree list with the item. None => lhs.get_or_create_use_tree_list().add_use_tree(rhs_t), // Recreates the use tree list with sorted items (see `use_tree_cmp` doc). - // Also attempts to preserve formatting (but only in terms of index-based - // "positions" of new lines and indents). - Some(old_list) => { - ted::remove(old_list.syntax()); + Some(use_tree_list) => { + if use_tree_list.l_curly_token().is_none() { + ted::insert_raw( + Position::first_child_of(use_tree_list.syntax()), + make::token(T!['{']), + ); + } + if use_tree_list.r_curly_token().is_none() { + ted::insert_raw( + Position::last_child_of(use_tree_list.syntax()), + make::token(T!['}']), + ); + } - let new_list = make::use_tree_list(empty()).clone_for_update(); let mut elements = Vec::new(); for (idx, tree) in use_trees.iter().enumerate() { if idx > 0 { elements.push(make::token(T![,]).into()); - } - match positional_formatting_map.get(&idx) { - None if idx > 0 => { - elements.push(make::tokens::single_space().into()) - } - Some(prev_trivia) => { - elements.extend(prev_trivia.clone().into_iter()) - } - _ => (), + elements.push(make::tokens::single_space().into()); } elements.push(tree.syntax().clone().into()); } - if let Some(ref trivia) = closing_formatting_trivia { - elements.extend(trivia.clone().into_iter()) - } - let trees_pos = match new_list.l_curly_token() { - Some(l_curly) => Position::after(l_curly), - None => Position::last_child_of(new_list.syntax()), - }; - ted::insert_all_raw(trees_pos, elements); - - let list_pos = Position::last_child_of(lhs.syntax()); - ted::insert_raw(list_pos, new_list.syntax()); + let start = use_tree_list + .l_curly_token() + .and_then(|l_curly| { + algo::non_trivia_sibling(l_curly.into(), Direction::Next) + }) + .filter(|it| it.kind() != T!['}']); + let end = use_tree_list + .r_curly_token() + .and_then(|r_curly| { + algo::non_trivia_sibling(r_curly.into(), Direction::Prev) + }) + .filter(|it| it.kind() != T!['{']); + if let Some((start, end)) = start.zip(end) { + // Attempt to insert elements while preserving preceding and trailing trivia. + ted::replace_all(start..=end, elements); + } else { + let new_use_tree_list = make::use_tree_list(empty()).clone_for_update(); + let trees_pos = match new_use_tree_list.l_curly_token() { + Some(l_curly) => Position::after(l_curly), + None => Position::last_child_of(new_use_tree_list.syntax()), + }; + ted::insert_all_raw(trees_pos, elements); + ted::replace(use_tree_list.syntax(), new_use_tree_list.syntax()); + } } } } } } - return Some(()); - - // Returns all trivia preceding a syntax element if it may be relevant to formatting - // (i.e. includes at least one new line character). - fn formatting_whitespace(elem: &SyntaxElement) -> Option> { - let succ = - |item: &SyntaxElement| item.prev_sibling_or_token().filter(|it| it.kind().is_trivia()); - let first = succ(elem); - let trivia_set: FxHashSet<_> = successors(first, succ).collect(); - let contains_formatting_whitespace = trivia_set.iter().any(|it| { - it.kind() == SyntaxKind::WHITESPACE - && it.as_token().is_some_and(|token| token.text().contains('\n')) - }); - contains_formatting_whitespace.then_some(trivia_set) - } + Some(()) } /// Traverses both paths until they differ, returning the common prefix of both. @@ -383,13 +366,13 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { } } -// Orders for use trees with equal paths (see `use_tree_cmp` for details about use tree ordering). -// -// If the `strict` parameter is set to true and both trees have tree lists, the tree lists are -// ordered by calling `use_tree_cmp` on their "sub-tree" pairs until either the tie is broken -// or tree list equality is confirmed, otherwise (i.e. if either `strict` is false or at least -// one of the trees does *not* have tree list), this potentially recursive step is skipped, -// and only the presence of a glob pattern or an alias is used to determine the ordering. +/// Orders for use trees with equal paths (see `use_tree_cmp` for details about use tree ordering). +/// +/// If the `strict` parameter is set to true and both trees have tree lists, the tree lists are +/// ordered by calling `use_tree_cmp` on their "sub-tree" pairs until either the tie is broken +/// or tree list equality is confirmed, otherwise (i.e. if either `strict` is false or at least +/// one of the trees does *not* have tree list), this potentially recursive step is skipped, +/// and only the presence of a glob pattern or an alias is used to determine the ordering. fn use_tree_cmp_by_tree_list_glob_or_alias( a: &ast::UseTree, b: &ast::UseTree, From 02d21f21cf7505c388c3140159b0da21cbff87fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 14 Jan 2024 10:54:20 -0800 Subject: [PATCH 014/118] Add a stable #visual-studio anchor to the Manual --- docs/user/manual.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index fa8413c19ae07..069a62ddbfe96 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -512,7 +512,8 @@ https://docs.helix-editor.com/[Helix] supports LSP by default. However, it won't install `rust-analyzer` automatically. You can follow instructions for installing <>. -=== Visual Studio 2022 +[#visual-studio] +=== [[visual-studio-2022]]Visual Studio 2022 There are multiple rust-analyzer extensions for Visual Studio 2022 on Windows: From c7eb52dd7b2a8222d4a09934bacc1b2b07deafb0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 Jan 2024 17:22:39 +0100 Subject: [PATCH 015/118] internal: Add unstable config for loading the sysroot sources via `cargo metadata` --- crates/base-db/src/input.rs | 42 ++- crates/project-model/src/cargo_workspace.rs | 2 + crates/project-model/src/sysroot.rs | 275 ++++++++++----- crates/project-model/src/tests.rs | 63 +++- crates/project-model/src/workspace.rs | 317 ++++++++++-------- ...rust_project_hello_world_project_model.txt | 2 +- crates/rust-analyzer/src/cli/rustc_tests.rs | 9 +- crates/rust-analyzer/src/config.rs | 9 + crates/rust-analyzer/src/reload.rs | 2 +- docs/user/generated_config.adoc | 10 + editors/code/package.json | 5 + 11 files changed, 497 insertions(+), 239 deletions(-) diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index e45a81238ac9b..8c43f97b91f54 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -9,7 +9,7 @@ use std::{fmt, mem, ops, str::FromStr}; use cfg::CfgOptions; -use la_arena::{Arena, Idx}; +use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; use syntax::SmolStr; @@ -157,6 +157,10 @@ impl CrateOrigin { pub fn is_lib(&self) -> bool { matches!(self, CrateOrigin::Library { .. }) } + + pub fn is_lang(&self) -> bool { + matches!(self, CrateOrigin::Lang { .. }) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -174,7 +178,7 @@ impl From<&str> for LangCrateOrigin { match s { "alloc" => LangCrateOrigin::Alloc, "core" => LangCrateOrigin::Core, - "proc-macro" => LangCrateOrigin::ProcMacro, + "proc-macro" | "proc_macro" => LangCrateOrigin::ProcMacro, "std" => LangCrateOrigin::Std, "test" => LangCrateOrigin::Test, _ => LangCrateOrigin::Other, @@ -522,7 +526,7 @@ impl CrateGraph { self.arena.iter().map(|(idx, _)| idx) } - // FIXME: used for `handle_hack_cargo_workspace`, should be removed later + // FIXME: used for fixing up the toolchain sysroot, should be removed and done differently #[doc(hidden)] pub fn iter_mut(&mut self) -> impl Iterator + '_ { self.arena.iter_mut() @@ -619,7 +623,12 @@ impl CrateGraph { /// This will deduplicate the crates of the graph where possible. /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id. /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted. - pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) { + pub fn extend( + &mut self, + mut other: CrateGraph, + proc_macros: &mut ProcMacroPaths, + on_finished: impl FnOnce(&FxHashMap), + ) { let topo = other.crates_in_topological_order(); let mut id_map: FxHashMap = FxHashMap::default(); for topo in topo { @@ -670,6 +679,8 @@ impl CrateGraph { *proc_macros = mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect(); + + on_finished(&id_map); } fn find_path( @@ -721,6 +732,29 @@ impl CrateGraph { fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator + 'a { self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name)) } + + /// Removes all crates from this crate graph except for the ones in `to_keep` and fixes up the dependencies. + /// Returns a mapping from old crate ids to new crate ids. + pub fn remove_crates_except(&mut self, to_keep: &[CrateId]) -> Vec> { + let mut id_map = vec![None; self.arena.len()]; + self.arena = std::mem::take(&mut self.arena) + .into_iter() + .filter_map(|(id, data)| if to_keep.contains(&id) { Some((id, data)) } else { None }) + .enumerate() + .map(|(new_id, (id, data))| { + id_map[id.into_raw().into_u32() as usize] = + Some(CrateId::from_raw(RawIdx::from_u32(new_id as u32))); + data + }) + .collect(); + for (_, data) in self.arena.iter_mut() { + data.dependencies.iter_mut().for_each(|dep| { + dep.crate_id = + id_map[dep.crate_id.into_raw().into_u32() as usize].expect("crate was filtered") + }); + } + id_map + } } impl ops::Index for CrateGraph { diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index bc1fcd08e20ce..dba9edab3c93c 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -82,6 +82,8 @@ pub struct CargoConfig { pub target: Option, /// Sysroot loading behavior pub sysroot: Option, + /// Whether to invoke `cargo metadata` on the sysroot crate. + pub sysroot_query_metadata: bool, pub sysroot_src: Option, /// rustc private crate source pub rustc_source: Option, diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index d52e448d74700..fb5e8c365a34a 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -8,6 +8,7 @@ use std::{env, fs, iter, ops, path::PathBuf, process::Command}; use anyhow::{format_err, Context, Result}; use base_db::CrateName; +use itertools::Itertools; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; @@ -18,42 +19,29 @@ use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath}; pub struct Sysroot { root: AbsPathBuf, src_root: AbsPathBuf, - crates: Arena, - /// Stores the result of `cargo metadata` of the `RA_UNSTABLE_SYSROOT_HACK` workspace. - pub hack_cargo_workspace: Option, + mode: SysrootMode, } -pub(crate) type SysrootCrate = Idx; +#[derive(Debug, Clone, Eq, PartialEq)] +pub(crate) enum SysrootMode { + Workspace(CargoWorkspace), + Stitched(Stitched), +} #[derive(Debug, Clone, Eq, PartialEq)] -pub struct SysrootCrateData { - pub name: String, - pub root: ManifestPath, - pub deps: Vec, +pub(crate) struct Stitched { + crates: Arena, } -impl ops::Index for Sysroot { +impl ops::Index for Stitched { type Output = SysrootCrateData; fn index(&self, index: SysrootCrate) -> &SysrootCrateData { &self.crates[index] } } -impl Sysroot { - /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/` - /// subfolder live, like: - /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu` - pub fn root(&self) -> &AbsPath { - &self.root - } - - /// Returns the sysroot "source" directory, where stdlib sources are located, like: - /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library` - pub fn src_root(&self) -> &AbsPath { - &self.src_root - } - - pub fn public_deps(&self) -> impl Iterator + '_ { +impl Stitched { + pub(crate) fn public_deps(&self) -> impl Iterator + '_ { // core is added as a dependency before std in order to // mimic rustcs dependency order ["core", "alloc", "std"] @@ -65,20 +53,56 @@ impl Sysroot { }) } - pub fn proc_macro(&self) -> Option { + pub(crate) fn proc_macro(&self) -> Option { self.by_name("proc_macro") } - pub fn crates(&self) -> impl Iterator + ExactSizeIterator + '_ { + pub(crate) fn crates(&self) -> impl Iterator + ExactSizeIterator + '_ { self.crates.iter().map(|(id, _data)| id) } + fn by_name(&self, name: &str) -> Option { + let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?; + Some(id) + } +} + +pub(crate) type SysrootCrate = Idx; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub(crate) struct SysrootCrateData { + pub(crate) name: String, + pub(crate) root: ManifestPath, + pub(crate) deps: Vec, +} + +impl Sysroot { + /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/` + /// subfolder live, like: + /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu` + pub fn root(&self) -> &AbsPath { + &self.root + } + + /// Returns the sysroot "source" directory, where stdlib sources are located, like: + /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library` + pub fn src_root(&self) -> &AbsPath { + &self.src_root + } + pub fn is_empty(&self) -> bool { - self.crates.is_empty() + match &self.mode { + SysrootMode::Workspace(ws) => ws.packages().next().is_none(), + SysrootMode::Stitched(stitched) => stitched.crates.is_empty(), + } } pub fn loading_warning(&self) -> Option { - if self.by_name("core").is_none() { + let has_core = match &self.mode { + SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(), + }; + if !has_core { let var_note = if env::var_os("RUST_SRC_PATH").is_some() { " (`RUST_SRC_PATH` might be incorrect, try unsetting it)" } else { @@ -92,27 +116,43 @@ impl Sysroot { None } } + + pub fn num_packages(&self) -> usize { + match &self.mode { + SysrootMode::Workspace(ws) => ws.packages().count(), + SysrootMode::Stitched(c) => c.crates().count(), + } + } + + pub(crate) fn mode(&self) -> &SysrootMode { + &self.mode + } } // FIXME: Expose a builder api as loading the sysroot got way too modular and complicated. impl Sysroot { /// Attempts to discover the toolchain's sysroot from the given `dir`. - pub fn discover(dir: &AbsPath, extra_env: &FxHashMap) -> Result { + pub fn discover( + dir: &AbsPath, + extra_env: &FxHashMap, + metadata: bool, + ) -> Result { tracing::debug!("discovering sysroot for {dir}"); let sysroot_dir = discover_sysroot_dir(dir, extra_env)?; let sysroot_src_dir = discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?; - Ok(Sysroot::load(sysroot_dir, sysroot_src_dir)) + Ok(Sysroot::load(sysroot_dir, sysroot_src_dir, metadata)) } pub fn discover_with_src_override( current_dir: &AbsPath, extra_env: &FxHashMap, src: AbsPathBuf, + metadata: bool, ) -> Result { tracing::debug!("discovering sysroot for {current_dir}"); let sysroot_dir = discover_sysroot_dir(current_dir, extra_env)?; - Ok(Sysroot::load(sysroot_dir, src)) + Ok(Sysroot::load(sysroot_dir, src, metadata)) } pub fn discover_rustc_src(&self) -> Option { @@ -131,49 +171,132 @@ impl Sysroot { } } - pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result { + pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf, metadata: bool) -> Result { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| { format_err!("can't load standard library from sysroot path {sysroot_dir}") })?; - Ok(Sysroot::load(sysroot_dir, sysroot_src_dir)) + Ok(Sysroot::load(sysroot_dir, sysroot_src_dir, metadata)) } - pub fn load(sysroot_dir: AbsPathBuf, mut sysroot_src_dir: AbsPathBuf) -> Sysroot { - // FIXME: Remove this `hack_cargo_workspace` field completely once we support sysroot dependencies - let hack_cargo_workspace = if let Ok(path) = std::env::var("RA_UNSTABLE_SYSROOT_HACK") { - let cargo_toml = ManifestPath::try_from( - AbsPathBuf::try_from(&*format!("{path}/Cargo.toml")).unwrap(), - ) - .unwrap(); - sysroot_src_dir = AbsPathBuf::try_from(&*path).unwrap().join("library"); - CargoWorkspace::fetch_metadata( - &cargo_toml, - &AbsPathBuf::try_from("/").unwrap(), - &CargoConfig::default(), - &|_| (), - ) - .map(CargoWorkspace::new) - .ok() - } else { - None - }; - let mut sysroot = Sysroot { - root: sysroot_dir, - src_root: sysroot_src_dir, - crates: Arena::default(), - hack_cargo_workspace, - }; + pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf, metadata: bool) -> Sysroot { + if metadata { + let sysroot: Option<_> = (|| { + let sysroot_cargo_toml = ManifestPath::try_from( + AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot/Cargo.toml")).ok()?, + ) + .ok()?; + let current_dir = + AbsPathBuf::try_from(&*format!("{sysroot_src_dir}/sysroot")).ok()?; + let res = CargoWorkspace::fetch_metadata( + &sysroot_cargo_toml, + ¤t_dir, + &CargoConfig::default(), + &|_| (), + ) + .map_err(|e| { + tracing::error!( + "failed to load sysroot `{sysroot_src_dir}/sysroot/Cargo.toml`: {}", + e + ); + e + }); + if let Err(e) = + std::fs::remove_file(&format!("{sysroot_src_dir}/sysroot/Cargo.lock")) + { + tracing::error!( + "failed to remove sysroot `{sysroot_src_dir}/sysroot/Cargo.lock`: {}", + e + ) + } + let mut res = res.ok()?; + + // Patch out `rustc-std-workspace-*` crates to point to the real crates. + // This is done prior to `CrateGraph` construction to avoid having duplicate `std` targets. + + let mut fake_core = None; + let mut fake_alloc = None; + let mut fake_std = None; + let mut real_core = None; + let mut real_alloc = None; + let mut real_std = None; + res.packages.iter().enumerate().for_each(|(idx, package)| { + match package.name.strip_prefix("rustc-std-workspace-") { + Some("core") => fake_core = Some((idx, package.id.clone())), + Some("alloc") => fake_alloc = Some((idx, package.id.clone())), + Some("std") => fake_std = Some((idx, package.id.clone())), + Some(_) => { + tracing::warn!("unknown rustc-std-workspace-* crate: {}", package.name) + } + None => match &*package.name { + "core" => real_core = Some(package.id.clone()), + "alloc" => real_alloc = Some(package.id.clone()), + "std" => real_std = Some(package.id.clone()), + _ => (), + }, + } + }); + + let patches = + [fake_core.zip(real_core), fake_alloc.zip(real_alloc), fake_std.zip(real_std)] + .into_iter() + .flatten(); + + let resolve = res.resolve.as_mut().expect("metadata executed with deps"); + let mut remove_nodes = vec![]; + for (idx, node) in resolve.nodes.iter_mut().enumerate() { + // Replace them in the dependency list + node.deps.iter_mut().for_each(|dep| { + if let Some((_, real)) = + patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg) + { + dep.pkg = real; + } + }); + if patches.clone().any(|((_, fake), _)| fake == node.id) { + remove_nodes.push(idx); + } + } + // Remove the fake ones from the resolve data + remove_nodes.into_iter().rev().for_each(|r| { + resolve.nodes.remove(r); + }); + // Remove the fake ones from the packages + patches.map(|((r, _), _)| r).sorted().rev().for_each(|r| { + res.packages.remove(r); + }); + + res.workspace_members = res + .packages + .iter() + .filter_map(|package| { + RELEVANT_SYSROOT_CRATES + .contains(&&*package.name) + .then(|| package.id.clone()) + }) + .collect(); + let cargo_workspace = CargoWorkspace::new(res); + Some(Sysroot { + root: sysroot_dir.clone(), + src_root: sysroot_src_dir.clone(), + mode: SysrootMode::Workspace(cargo_workspace), + }) + })(); + if let Some(sysroot) = sysroot { + return sysroot; + } + } + let mut stitched = Stitched { crates: Arena::default() }; for path in SYSROOT_CRATES.trim().lines() { let name = path.split('/').last().unwrap(); let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")] .into_iter() - .map(|it| sysroot.src_root.join(it)) + .map(|it| sysroot_src_dir.join(it)) .filter_map(|it| ManifestPath::try_from(it).ok()) .find(|it| fs::metadata(it).is_ok()); if let Some(root) = root { - sysroot.crates.alloc(SysrootCrateData { + stitched.crates.alloc(SysrootCrateData { name: name.into(), root, deps: Vec::new(), @@ -181,36 +304,34 @@ impl Sysroot { } } - if let Some(std) = sysroot.by_name("std") { + if let Some(std) = stitched.by_name("std") { for dep in STD_DEPS.trim().lines() { - if let Some(dep) = sysroot.by_name(dep) { - sysroot.crates[std].deps.push(dep) + if let Some(dep) = stitched.by_name(dep) { + stitched.crates[std].deps.push(dep) } } } - if let Some(alloc) = sysroot.by_name("alloc") { + if let Some(alloc) = stitched.by_name("alloc") { for dep in ALLOC_DEPS.trim().lines() { - if let Some(dep) = sysroot.by_name(dep) { - sysroot.crates[alloc].deps.push(dep) + if let Some(dep) = stitched.by_name(dep) { + stitched.crates[alloc].deps.push(dep) } } } - if let Some(proc_macro) = sysroot.by_name("proc_macro") { + if let Some(proc_macro) = stitched.by_name("proc_macro") { for dep in PROC_MACRO_DEPS.trim().lines() { - if let Some(dep) = sysroot.by_name(dep) { - sysroot.crates[proc_macro].deps.push(dep) + if let Some(dep) = stitched.by_name(dep) { + stitched.crates[proc_macro].deps.push(dep) } } } - - sysroot - } - - fn by_name(&self, name: &str) -> Option { - let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?; - Some(id) + Sysroot { + root: sysroot_dir, + src_root: sysroot_src_dir, + mode: SysrootMode::Stitched(stitched), + } } } @@ -318,3 +439,5 @@ test"; const PROC_MACRO_DEPS: &str = " std core"; + +const RELEVANT_SYSROOT_CRATES: &[&str] = &["core", "alloc", "std", "test", "proc_macro"]; diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 4887b29815ae2..9e6b00d93803e 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -38,7 +38,7 @@ fn load_cargo_with_overrides( to_crate_graph(project_workspace) } -fn load_cargo_with_sysroot( +fn load_cargo_with_fake_sysroot( file_map: &mut FxHashMap, file: &str, ) -> (CrateGraph, ProcMacroPaths) { @@ -125,7 +125,7 @@ fn get_fake_sysroot() -> Sysroot { // fake sysroot, so we give them both the same path: let sysroot_dir = AbsPathBuf::assert(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(sysroot_dir, sysroot_src_dir) + Sysroot::load(sysroot_dir, sysroot_src_dir, false) } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { @@ -225,12 +225,12 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { #[test] fn crate_graph_dedup_identical() { let (mut crate_graph, proc_macros) = - load_cargo_with_sysroot(&mut Default::default(), "regex-metadata.json"); + load_cargo_with_fake_sysroot(&mut Default::default(), "regex-metadata.json"); crate_graph.sort_deps(); let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone()); - crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros); + crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros, |_| ()); assert!(crate_graph.iter().eq(d_crate_graph.iter())); assert_eq!(proc_macros, d_proc_macros); } @@ -239,14 +239,14 @@ fn crate_graph_dedup_identical() { fn crate_graph_dedup() { let path_map = &mut Default::default(); let (mut crate_graph, _proc_macros) = - load_cargo_with_sysroot(path_map, "ripgrep-metadata.json"); + load_cargo_with_fake_sysroot(path_map, "ripgrep-metadata.json"); assert_eq!(crate_graph.iter().count(), 81); crate_graph.sort_deps(); let (regex_crate_graph, mut regex_proc_macros) = - load_cargo_with_sysroot(path_map, "regex-metadata.json"); + load_cargo_with_fake_sysroot(path_map, "regex-metadata.json"); assert_eq!(regex_crate_graph.iter().count(), 60); - crate_graph.extend(regex_crate_graph, &mut regex_proc_macros); + crate_graph.extend(regex_crate_graph, &mut regex_proc_macros, |_| ()); assert_eq!(crate_graph.iter().count(), 118); } @@ -254,12 +254,12 @@ fn crate_graph_dedup() { fn test_deduplicate_origin_dev() { let path_map = &mut Default::default(); let (mut crate_graph, _proc_macros) = - load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json"); + load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_A.json"); crate_graph.sort_deps(); let (crate_graph_1, mut _proc_macros_2) = - load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json"); + load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_B.json"); - crate_graph.extend(crate_graph_1, &mut _proc_macros_2); + crate_graph.extend(crate_graph_1, &mut _proc_macros_2, |_| ()); let mut crates_named_p2 = vec![]; for id in crate_graph.iter() { @@ -280,12 +280,12 @@ fn test_deduplicate_origin_dev() { fn test_deduplicate_origin_dev_rev() { let path_map = &mut Default::default(); let (mut crate_graph, _proc_macros) = - load_cargo_with_sysroot(path_map, "deduplication_crate_graph_B.json"); + load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_B.json"); crate_graph.sort_deps(); let (crate_graph_1, mut _proc_macros_2) = - load_cargo_with_sysroot(path_map, "deduplication_crate_graph_A.json"); + load_cargo_with_fake_sysroot(path_map, "deduplication_crate_graph_A.json"); - crate_graph.extend(crate_graph_1, &mut _proc_macros_2); + crate_graph.extend(crate_graph_1, &mut _proc_macros_2, |_| ()); let mut crates_named_p2 = vec![]; for id in crate_graph.iter() { @@ -301,3 +301,40 @@ fn test_deduplicate_origin_dev_rev() { let p2 = crates_named_p2[0]; assert!(p2.origin.is_local()); } + +#[test] +fn smoke_test_real_sysroot_cargo() { + if std::env::var("SYSROOT_CARGO_METADATA").is_err() { + return; + } + let file_map = &mut FxHashMap::::default(); + let meta = get_test_json_file("hello-world-metadata.json"); + + let cargo_workspace = CargoWorkspace::new(meta); + let sysroot = Ok(Sysroot::discover( + AbsPath::assert(Path::new(env!("CARGO_MANIFEST_DIR"))), + &Default::default(), + true, + ) + .unwrap()); + + let project_workspace = ProjectWorkspace::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + sysroot, + rustc: Err(None), + rustc_cfg: Vec::new(), + cfg_overrides: Default::default(), + toolchain: None, + target_layout: Err("target_data_layout not loaded".into()), + }; + project_workspace.to_crate_graph( + &mut { + |path| { + let len = file_map.len(); + Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) + } + }, + &Default::default(), + ); +} diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index c04eddc56fb7f..679f219dcc4f0 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -9,7 +9,7 @@ use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, DependencyKind, Edition, Env, FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, }; -use cfg::{CfgDiff, CfgOptions}; +use cfg::{CfgAtom, CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; @@ -22,7 +22,7 @@ use crate::{ cfg_flag::CfgFlag, project_json::Crate, rustc_cfg::{self, RustcCfgConfig}, - sysroot::SysrootCrate, + sysroot::{SysrootCrate, SysrootMode}, target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, }; @@ -130,7 +130,7 @@ impl fmt::Debug for ProjectWorkspace { let mut debug_struct = f.debug_struct("Json"); debug_struct.field("n_crates", &project.n_crates()); if let Ok(sysroot) = sysroot { - debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); + debug_struct.field("n_sysroot_crates", &sysroot.num_packages()); } debug_struct.field("toolchain", &toolchain); debug_struct.field("n_rustc_cfg", &rustc_cfg.len()); @@ -208,23 +208,23 @@ impl ProjectWorkspace { let sysroot = match (&config.sysroot, &config.sysroot_src) { (Some(RustLibSource::Path(path)), None) => { - Sysroot::with_sysroot_dir(path.clone()).map_err(|e| { + Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata).map_err(|e| { Some(format!("Failed to find sysroot at {path}:{e}")) }) } (Some(RustLibSource::Discover), None) => { - Sysroot::discover(cargo_toml.parent(), &config.extra_env).map_err(|e| { + Sysroot::discover(cargo_toml.parent(), &config.extra_env, config.sysroot_query_metadata).map_err(|e| { Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}")) }) } (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => { - Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone())) + Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone(), config.sysroot_query_metadata)) } (Some(RustLibSource::Discover), Some(sysroot_src)) => { Sysroot::discover_with_src_override( cargo_toml.parent(), &config.extra_env, - sysroot_src.clone(), + sysroot_src.clone(), config.sysroot_query_metadata, ).map_err(|e| { Some(format!("Failed to find sysroot for Cargo.toml file {cargo_toml}. Is rust-src installed? {e}")) }) @@ -317,12 +317,12 @@ impl ProjectWorkspace { toolchain: Option, ) -> ProjectWorkspace { let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { - (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)), + (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src, false)), (Some(sysroot), None) => { // assume sysroot is structured like rustup's and guess `sysroot_src` let sysroot_src = sysroot.join("lib").join("rustlib").join("src").join("rust").join("library"); - Ok(Sysroot::load(sysroot, sysroot_src)) + Ok(Sysroot::load(sysroot, sysroot_src, false)) } (None, Some(sysroot_src)) => { // assume sysroot is structured like rustup's and guess `sysroot` @@ -330,7 +330,7 @@ impl ProjectWorkspace { for _ in 0..5 { sysroot.pop(); } - Ok(Sysroot::load(sysroot, sysroot_src)) + Ok(Sysroot::load(sysroot, sysroot_src, false)) } (None, None) => Err(None), }; @@ -354,16 +354,22 @@ impl ProjectWorkspace { config: &CargoConfig, ) -> anyhow::Result { let sysroot = match &config.sysroot { - Some(RustLibSource::Path(path)) => Sysroot::with_sysroot_dir(path.clone()) - .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))), + Some(RustLibSource::Path(path)) => { + Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata) + .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))) + } Some(RustLibSource::Discover) => { let dir = &detached_files .first() .and_then(|it| it.parent()) .ok_or_else(|| format_err!("No detached files to load"))?; - Sysroot::discover(dir, &config.extra_env).map_err(|e| { - Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}")) - }) + Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata).map_err( + |e| { + Some(format!( + "Failed to find sysroot for {dir}. Is rust-src installed? {e}" + )) + }, + ) } None => Err(None), }; @@ -494,13 +500,43 @@ impl ProjectWorkspace { /// The return type contains the path and whether or not /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec { - let mk_sysroot = |sysroot: Result<&Sysroot, _>, project_root: Option<&AbsPath>| { - sysroot.map(|sysroot| PackageRoot { - // mark the sysroot as mutable if it is located inside of the project - is_local: project_root - .map_or(false, |project_root| sysroot.src_root().starts_with(project_root)), - include: vec![sysroot.src_root().to_path_buf()], - exclude: Vec::new(), + let mk_sysroot = |sysroot: Result<_, _>, project_root: Option<&AbsPath>| { + let project_root = project_root.map(ToOwned::to_owned); + sysroot.into_iter().flat_map(move |sysroot: &Sysroot| { + let mut r = match sysroot.mode() { + SysrootMode::Workspace(ws) => ws + .packages() + .filter_map(|pkg| { + if ws[pkg].is_local { + // the local ones are included in the main `PackageRoot`` below + return None; + } + let pkg_root = ws[pkg].manifest.parent().to_path_buf(); + + let include = vec![pkg_root.clone()]; + + let exclude = vec![ + pkg_root.join(".git"), + pkg_root.join("target"), + pkg_root.join("tests"), + pkg_root.join("examples"), + pkg_root.join("benches"), + ]; + Some(PackageRoot { is_local: false, include, exclude }) + }) + .collect(), + SysrootMode::Stitched(_) => vec![], + }; + + r.push(PackageRoot { + // mark the sysroot as mutable if it is located inside of the project + is_local: project_root + .as_ref() + .map_or(false, |project_root| sysroot.src_root().starts_with(project_root)), + include: vec![sysroot.src_root().to_path_buf()], + exclude: Vec::new(), + }); + r }) }; match self { @@ -588,16 +624,16 @@ impl ProjectWorkspace { pub fn n_packages(&self) -> usize { match self { ProjectWorkspace::Json { project, sysroot, .. } => { - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len()); + let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); sysroot_package_len + project.n_crates() } ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => { let rustc_package_len = rustc.as_ref().map_or(0, |(it, _)| it.packages().len()); - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len()); + let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); cargo.packages().len() + sysroot_package_len + rustc_package_len } ProjectWorkspace::DetachedFiles { sysroot, files, .. } => { - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len()); + let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); sysroot_package_len + files.len() } } @@ -638,7 +674,6 @@ impl ProjectWorkspace { sysroot.as_ref().ok(), rustc_cfg.clone(), cfg_overrides, - None, build_scripts, match target_layout.as_ref() { Ok(it) => Ok(Arc::from(it.as_str())), @@ -849,8 +884,6 @@ fn cargo_to_crate_graph( sysroot: Option<&Sysroot>, rustc_cfg: Vec, override_cfg: &CfgOverrides, - // Don't compute cfg and use this if present, only used for the sysroot experiment hack - forced_cfg: Option, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, toolchain: Option<&Version>, @@ -883,7 +916,7 @@ fn cargo_to_crate_graph( for pkg in cargo.packages() { has_private |= cargo[pkg].metadata.rustc_private; - let cfg_options = forced_cfg.clone().unwrap_or_else(|| { + let cfg_options = { let mut cfg_options = cfg_options.clone(); // Add test cfg for local crates @@ -908,7 +941,7 @@ fn cargo_to_crate_graph( cfg_options.apply_diff(diff.clone()); }; cfg_options - }); + }; let mut lib_tgt = None; for &tgt in cargo[pkg].targets.iter() { @@ -1349,124 +1382,126 @@ fn sysroot_to_crate_graph( toolchain: Option<&Version>, ) -> (SysrootPublicDeps, Option) { let _p = profile::span("sysroot_to_crate_graph"); - let cfg_options = create_cfg_options(rustc_cfg.clone()); - let sysroot_crates: FxHashMap = match &sysroot.hack_cargo_workspace { - Some(cargo) => handle_hack_cargo_workspace( - load, - cargo, - rustc_cfg, - cfg_options, - target_layout, - toolchain, - crate_graph, - sysroot, - ), - None => sysroot - .crates() - .filter_map(|krate| { - let file_id = load(&sysroot[krate].root)?; - - let env = Env::default(); - let display_name = - CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); - let crate_id = crate_graph.add_crate_root( - file_id, - Edition::CURRENT, - Some(display_name), - None, - cfg_options.clone(), - None, - env, - false, - CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), - target_layout.clone(), - toolchain.cloned(), - ); - Some((krate, crate_id)) - }) - .collect(), - }; - for from in sysroot.crates() { - for &to in sysroot[from].deps.iter() { - let name = CrateName::new(&sysroot[to].name).unwrap(); - if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { - add_dep(crate_graph, from, name, to, DependencyKind::Normal); + match sysroot.mode() { + SysrootMode::Workspace(cargo) => { + let (mut cg, mut pm) = cargo_to_crate_graph( + load, + None, + cargo, + None, + rustc_cfg, + &CfgOverrides::default(), + &WorkspaceBuildScripts::default(), + target_layout, + toolchain, + ); + + let mut pub_deps = vec![]; + let mut libproc_macro = None; + let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(); + for (cid, c) in cg.iter_mut() { + // uninject `test` flag so `core` keeps working. + c.cfg_options.apply_diff(diff.clone()); + // patch the origin + if c.origin.is_local() { + let lang_crate = LangCrateOrigin::from( + c.display_name.as_ref().map_or("", |it| it.canonical_name()), + ); + c.origin = CrateOrigin::Lang(lang_crate); + match lang_crate { + LangCrateOrigin::Test + | LangCrateOrigin::Alloc + | LangCrateOrigin::Core + | LangCrateOrigin::Std => pub_deps.push(( + CrateName::normalize_dashes(&lang_crate.to_string()), + cid, + !matches!(lang_crate, LangCrateOrigin::Test), + )), + LangCrateOrigin::ProcMacro => libproc_macro = Some(cid), + LangCrateOrigin::Other => (), + } + } + } + + let mut marker_set = vec![]; + for &(_, cid, _) in pub_deps.iter() { + marker_set.extend(cg.transitive_deps(cid)); } + if let Some(cid) = libproc_macro { + marker_set.extend(cg.transitive_deps(cid)); + } + + marker_set.sort(); + marker_set.dedup(); + + // Remove all crates except the ones we are interested in to keep the sysroot graph small. + let removed_mapping = cg.remove_crates_except(&marker_set); + + crate_graph.extend(cg, &mut pm, |mapping| { + // Map the id through the removal mapping first, then through the crate graph extension mapping. + pub_deps.iter_mut().for_each(|(_, cid, _)| { + *cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()] + }); + if let Some(libproc_macro) = &mut libproc_macro { + *libproc_macro = mapping + [&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()]; + } + }); + + (SysrootPublicDeps { deps: pub_deps }, libproc_macro) } - } + SysrootMode::Stitched(stitched) => { + let cfg_options = create_cfg_options(rustc_cfg.clone()); + let sysroot_crates: FxHashMap = stitched + .crates() + .filter_map(|krate| { + let file_id = load(&stitched[krate].root)?; - let public_deps = SysrootPublicDeps { - deps: sysroot - .public_deps() - .filter_map(|(name, idx, prelude)| Some((name, *sysroot_crates.get(&idx)?, prelude))) - .collect::>(), - }; + let env = Env::default(); + let display_name = + CrateDisplayName::from_canonical_name(stitched[krate].name.clone()); + let crate_id = crate_graph.add_crate_root( + file_id, + Edition::CURRENT, + Some(display_name), + None, + cfg_options.clone(), + None, + env, + false, + CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)), + target_layout.clone(), + toolchain.cloned(), + ); + Some((krate, crate_id)) + }) + .collect(); + + for from in stitched.crates() { + for &to in stitched[from].deps.iter() { + let name = CrateName::new(&stitched[to].name).unwrap(); + if let (Some(&from), Some(&to)) = + (sysroot_crates.get(&from), sysroot_crates.get(&to)) + { + add_dep(crate_graph, from, name, to, DependencyKind::Normal); + } + } + } - let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); - (public_deps, libproc_macro) -} + let public_deps = SysrootPublicDeps { + deps: stitched + .public_deps() + .filter_map(|(name, idx, prelude)| { + Some((name, *sysroot_crates.get(&idx)?, prelude)) + }) + .collect::>(), + }; -fn handle_hack_cargo_workspace( - load: &mut dyn FnMut(&AbsPath) -> Option, - cargo: &CargoWorkspace, - rustc_cfg: Vec, - cfg_options: CfgOptions, - target_layout: Result, Arc>, - toolchain: Option<&Version>, - crate_graph: &mut CrateGraph, - sysroot: &Sysroot, -) -> FxHashMap { - let (cg, mut pm) = cargo_to_crate_graph( - load, - None, - cargo, - None, - rustc_cfg, - &CfgOverrides::default(), - Some(cfg_options), - &WorkspaceBuildScripts::default(), - target_layout, - toolchain, - ); - crate_graph.extend(cg, &mut pm); - for crate_name in ["std", "alloc", "core"] { - let original = crate_graph - .iter() - .find(|x| { - crate_graph[*x] - .display_name - .as_ref() - .map(|x| x.canonical_name() == crate_name) - .unwrap_or(false) - }) - .unwrap(); - let fake_crate_name = format!("rustc-std-workspace-{}", crate_name); - let fake = crate_graph - .iter() - .find(|x| { - crate_graph[*x] - .display_name - .as_ref() - .map(|x| x.canonical_name() == fake_crate_name) - .unwrap_or(false) - }) - .unwrap(); - crate_graph.remove_and_replace(fake, original).unwrap(); - } - for (_, c) in crate_graph.iter_mut() { - if c.origin.is_local() { - // LangCrateOrigin::Other is good enough for a hack. - c.origin = CrateOrigin::Lang(LangCrateOrigin::Other); + let libproc_macro = + stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); + (public_deps, libproc_macro) } } - sysroot - .crates() - .filter_map(|krate| { - let file_id = load(&sysroot[krate].root)?; - let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; - Some((krate, crate_id)) - }) - .collect() } fn add_dep( diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index e35f0fc7327e2..0df99534c5bd9 100644 --- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -182,7 +182,7 @@ }, ], origin: Lang( - Other, + ProcMacro, ), is_proc_macro: false, target_layout: Err( diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index b8f6138161e5c..87bb3cbd3410d 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -61,9 +61,12 @@ impl Tester { cargo_config.sysroot = Some(RustLibSource::Discover); let workspace = ProjectWorkspace::DetachedFiles { files: vec![tmp_file.clone()], - sysroot: Ok( - Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env).unwrap() - ), + sysroot: Ok(Sysroot::discover( + tmp_file.parent().unwrap(), + &cargo_config.extra_env, + false, + ) + .unwrap()), rustc_cfg: vec![], }; let load_cargo_config = LoadCargoConfig { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index fe009f82a71fd..3ec5d86966154 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -135,6 +135,13 @@ config_data! { /// /// This option does not take effect until rust-analyzer is restarted. cargo_sysroot: Option = "\"discover\"", + /// Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze + /// third-party dependencies of the standard libraries. + /// + /// This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer + /// will attempt to clean up afterwards, but nevertheless requires the location to be + /// writable to. + cargo_sysrootQueryMetadata: bool = "false", /// Relative path to the sysroot library sources. If left unset, this will default to /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. /// @@ -1233,6 +1240,7 @@ impl Config { }); let sysroot_src = self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot)); + let sysroot_query_metadata = self.data.cargo_sysrootQueryMetadata; CargoConfig { features: match &self.data.cargo_features { @@ -1244,6 +1252,7 @@ impl Config { }, target: self.data.cargo_target.clone(), sysroot, + sysroot_query_metadata, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 8e3fa7fa4dace..24af5fb49c042 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -516,7 +516,7 @@ impl GlobalState { for ws in &**self.workspaces { let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, &self.config.extra_env()); - crate_graph.extend(other, &mut crate_proc_macros); + crate_graph.extend(other, &mut crate_proc_macros, |_| {}); proc_macros.push(crate_proc_macros); } (crate_graph, proc_macros, crate_graph_file_dependencies) diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index ecc90abff1353..1a2791954e629 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -121,6 +121,16 @@ Unsetting this disables sysroot loading. This option does not take effect until rust-analyzer is restarted. -- +[[rust-analyzer.cargo.sysrootQueryMetadata]]rust-analyzer.cargo.sysrootQueryMetadata (default: `false`):: ++ +-- +Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze +third-party dependencies of the standard libraries. + +This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer +will attempt to clean up afterwards, but nevertheless requires the location to be +writable to. +-- [[rust-analyzer.cargo.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 8307f6833e6fb..e7ceee165cdcb 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -648,6 +648,11 @@ "string" ] }, + "rust-analyzer.cargo.sysrootQueryMetadata": { + "markdownDescription": "Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze\nthird-party dependencies of the standard libraries.\n\nThis will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer\nwill attempt to clean up afterwards, but nevertheless requires the location to be\nwritable to.", + "default": false, + "type": "boolean" + }, "rust-analyzer.cargo.sysrootSrc": { "markdownDescription": "Relative path to the sysroot library sources. If left unset, this will default to\n`{cargo.sysroot}/lib/rustlib/src/rust/library`.\n\nThis option does not take effect until rust-analyzer is restarted.", "default": null, From d80d2fcae0fb66901d88980d74520992fd5882ff Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jan 2024 10:24:14 +0100 Subject: [PATCH 016/118] Eagerly lower enum variants in CrateDefMap construction --- crates/hir-def/src/attr.rs | 82 +------- crates/hir-def/src/body.rs | 7 +- crates/hir-def/src/body/pretty.rs | 14 +- crates/hir-def/src/child_by_source.rs | 22 ++- crates/hir-def/src/data/adt.rs | 185 +++++------------- crates/hir-def/src/db.rs | 35 ++-- crates/hir-def/src/find_path.rs | 12 +- crates/hir-def/src/item_scope.rs | 7 +- crates/hir-def/src/item_tree.rs | 54 +++-- crates/hir-def/src/item_tree/lower.rs | 10 +- crates/hir-def/src/item_tree/pretty.rs | 2 +- crates/hir-def/src/lang_item.rs | 8 +- crates/hir-def/src/lib.rs | 35 ++-- crates/hir-def/src/nameres/collector.rs | 100 ++++++---- crates/hir-def/src/nameres/path_resolution.rs | 66 +++++-- crates/hir-def/src/pretty.rs | 8 +- crates/hir-def/src/resolver.rs | 8 +- crates/hir-def/src/src.rs | 17 +- crates/hir-def/src/trace.rs | 6 +- crates/hir-def/src/visibility.rs | 4 +- crates/hir-expand/src/db.rs | 3 + crates/hir-ty/src/consteval.rs | 21 +- crates/hir-ty/src/db.rs | 2 +- crates/hir-ty/src/diagnostics/decl_check.rs | 8 +- crates/hir-ty/src/diagnostics/match_check.rs | 3 +- .../match_check/deconstruct_pat.rs | 2 +- crates/hir-ty/src/display.rs | 16 +- crates/hir-ty/src/infer.rs | 58 +++--- crates/hir-ty/src/infer/closure.rs | 2 +- crates/hir-ty/src/infer/path.rs | 5 +- crates/hir-ty/src/inhabitedness.rs | 17 +- crates/hir-ty/src/layout.rs | 8 +- crates/hir-ty/src/layout/adt.rs | 17 +- crates/hir-ty/src/lower.rs | 27 +-- crates/hir-ty/src/mir/eval.rs | 84 ++++---- crates/hir-ty/src/mir/lower.rs | 15 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 24 +-- crates/hir-ty/src/mir/pretty.rs | 15 +- crates/hir-ty/src/tests.rs | 22 +-- crates/hir-ty/src/tls.rs | 5 +- crates/hir-ty/src/utils.rs | 26 +-- crates/hir/src/attrs.rs | 2 +- crates/hir/src/db.rs | 6 +- crates/hir/src/from_id.rs | 4 +- crates/hir/src/has_source.rs | 2 +- crates/hir/src/lib.rs | 54 ++--- crates/hir/src/symbols.rs | 4 +- crates/ide-db/src/apply_change.rs | 5 +- crates/ide-db/src/lib.rs | 5 +- 49 files changed, 552 insertions(+), 592 deletions(-) diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 30452e34aac36..be69d6f1351ac 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -28,8 +28,8 @@ use crate::{ lang_item::LangItem, nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource}, - AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId, - LocalFieldId, Lookup, MacroId, VariantId, + AdtId, AssocItemLoc, AttrDefId, GenericParamId, ItemLoc, LocalFieldId, Lookup, MacroId, + VariantId, }; #[derive(Default, Debug, Clone, PartialEq, Eq)] @@ -70,33 +70,6 @@ impl ops::Deref for AttrsWithOwner { impl Attrs { pub const EMPTY: Self = Self(RawAttrs::EMPTY); - pub(crate) fn variants_attrs_query( - db: &dyn DefDatabase, - e: EnumId, - ) -> Arc> { - let _p = profile::span("variants_attrs_query"); - // FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids - let mut res = ArenaMap::default(); - - let loc = e.lookup(db); - let krate = loc.container.krate; - let item_tree = loc.id.item_tree(db); - let enum_ = &item_tree[loc.id.value]; - let crate_graph = db.crate_graph(); - let cfg_options = &crate_graph[krate].cfg_options; - - let mut idx = 0; - for variant in enum_.variants.clone() { - let attrs = item_tree.attrs(db, krate, variant.into()); - if attrs.is_cfg_enabled(cfg_options) { - res.insert(Idx::from_raw(RawIdx::from(idx)), attrs); - idx += 1; - } - } - - Arc::new(res) - } - pub(crate) fn fields_attrs_query( db: &dyn DefDatabase, v: VariantId, @@ -108,29 +81,11 @@ impl Attrs { let crate_graph = db.crate_graph(); let (fields, item_tree, krate) = match v { VariantId::EnumVariantId(it) => { - let e = it.parent; - let loc = e.lookup(db); + let loc = it.lookup(db); let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); - let enum_ = &item_tree[loc.id.value]; - - let cfg_options = &crate_graph[krate].cfg_options; - - let Some(variant) = enum_ - .variants - .clone() - .filter(|variant| { - let attrs = item_tree.attrs(db, krate, (*variant).into()); - attrs.is_cfg_enabled(cfg_options) - }) - .zip(0u32..) - .find(|(_variant, idx)| it.local_id == Idx::from_raw(RawIdx::from(*idx))) - .map(|(variant, _idx)| variant) - else { - return Arc::new(res); - }; - - (item_tree[variant].fields.clone(), item_tree, krate) + let variant = &item_tree[loc.id.value]; + (variant.fields.clone(), item_tree, krate) } VariantId::StructId(it) => { let loc = it.lookup(db); @@ -401,10 +356,12 @@ impl AttrsWithOwner { AttrDefId::FieldId(it) => { return db.fields_attrs(it.parent)[it.local_id].clone(); } + // FIXME: DRY this up AttrDefId::EnumVariantId(it) => { - return db.variants_attrs(it.parent)[it.local_id].clone(); + let id = it.lookup(db).id; + let tree = id.item_tree(db); + tree.raw_attrs(id.value.into()).clone() } - // FIXME: DRY this up AttrDefId::AdtId(it) => match it { AdtId::StructId(it) => attrs_from_item_tree_loc(db, it), AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it), @@ -503,12 +460,7 @@ impl AttrsWithOwner { AdtId::EnumId(id) => any_has_attrs(db, id), }, AttrDefId::FunctionId(id) => any_has_attrs(db, id), - AttrDefId::EnumVariantId(id) => { - let map = db.variants_attrs_source_map(id.parent); - let file_id = id.parent.lookup(db).id.file_id(); - let root = db.parse_or_expand(file_id); - InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root))) - } + AttrDefId::EnumVariantId(id) => any_has_attrs(db, id), AttrDefId::StaticId(id) => any_has_attrs(db, id), AttrDefId::ConstId(id) => any_has_attrs(db, id), AttrDefId::TraitId(id) => any_has_attrs(db, id), @@ -676,20 +628,6 @@ fn attrs_from_item_tree_assoc<'db, N: ItemTreeNode>( attrs_from_item_tree(db, id) } -pub(crate) fn variants_attrs_source_map( - db: &dyn DefDatabase, - def: EnumId, -) -> Arc>> { - let mut res = ArenaMap::default(); - let child_source = def.child_source(db); - - for (idx, variant) in child_source.value.iter() { - res.insert(idx, AstPtr::new(variant)); - } - - Arc::new(res) -} - pub(crate) fn fields_attrs_source_map( db: &dyn DefDatabase, def: VariantId, diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index db28c6731ece1..a9c71f649a7b5 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -26,7 +26,7 @@ use crate::{ }, nameres::DefMap, path::{ModPath, Path}, - src::{HasChildSource, HasSource}, + src::HasSource, BlockId, DefWithBodyId, HasModule, Lookup, }; @@ -160,8 +160,9 @@ impl Body { src.map(|it| it.body()) } DefWithBodyId::VariantId(v) => { - let src = v.parent.child_source(db); - src.map(|it| it[v.local_id].expr()) + let s = v.lookup(db); + let src = s.source(db); + src.map(|it| it.expr()) } DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()), } diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 02b19ade44ba9..601878e8e79ca 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -3,7 +3,6 @@ use std::fmt::{self, Write}; use itertools::Itertools; -use syntax::ast::HasName; use crate::{ hir::{ @@ -42,12 +41,13 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo } DefWithBodyId::InTypeConstId(_) => format!("In type const = "), DefWithBodyId::VariantId(it) => { - let src = it.parent.child_source(db); - let variant = &src.value[it.local_id]; - match &variant.name() { - Some(name) => name.to_string(), - None => "_".to_string(), - } + let loc = it.lookup(db); + let enum_loc = loc.parent.lookup(db); + format!( + "enum {}::{}", + enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast()), + loc.id.item_tree(db)[loc.id.value].name.display(db.upcast()), + ) } }; diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs index 32c53cb950313..e8a771141effb 100644 --- a/crates/hir-def/src/child_by_source.rs +++ b/crates/hir-def/src/child_by_source.rs @@ -13,8 +13,8 @@ use crate::{ item_scope::ItemScope, nameres::DefMap, src::{HasChildSource, HasSource}, - AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FieldId, ImplId, - Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId, VariantId, + AdtId, AssocItemId, DefWithBodyId, EnumId, ExternCrateId, FieldId, ImplId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, UseId, VariantId, }; pub trait ChildBySource { @@ -205,12 +205,18 @@ impl ChildBySource for VariantId { impl ChildBySource for EnumId { fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) { - let arena_map = self.child_source(db); - let arena_map = arena_map.as_ref(); - for (local_id, source) in arena_map.value.iter() { - let id = EnumVariantId { parent: *self, local_id }; - res[keys::VARIANT].insert(source.clone(), id) - } + let loc = &self.lookup(db); + + let tree = loc.id.item_tree(db); + let ast_id_map = db.ast_id_map(loc.id.file_id()); + let root = db.parse_or_expand(loc.id.file_id()); + + db.enum_data(*self).variants.iter().for_each(|&(variant, _)| { + res[keys::VARIANT].insert( + ast_id_map.get(tree[variant.lookup(db).id.value].ast_id).to_node(&root), + variant, + ); + }); } } diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 8772c34f02f1a..93a64dc6ec6fb 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -28,8 +28,7 @@ use crate::{ tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, type_ref::TypeRef, visibility::RawVisibility, - EnumId, EnumLoc, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, - UnionId, VariantId, + EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId, }; /// Note that we use `StructData` for unions as well! @@ -43,7 +42,7 @@ pub struct StructData { } bitflags! { -#[derive(Debug, Clone, PartialEq, Eq)] + #[derive(Debug, Clone, PartialEq, Eq)] pub struct StructFlags: u8 { const NO_FLAGS = 0; /// Indicates whether the struct is `PhantomData`. @@ -65,7 +64,7 @@ bitflags! { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumData { pub name: Name, - pub variants: Arena, + pub variants: Box<[(EnumVariantId, Name)]>, pub repr: Option, pub visibility: RawVisibility, pub rustc_has_incoherent_inherent_impls: bool, @@ -75,7 +74,6 @@ pub struct EnumData { pub struct EnumVariantData { pub name: Name, pub variant_data: Arc, - pub tree_id: la_arena::Idx, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -287,17 +285,9 @@ impl StructData { impl EnumData { pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc { - db.enum_data_with_diagnostics(e).0 - } - - pub(crate) fn enum_data_with_diagnostics_query( - db: &dyn DefDatabase, - e: EnumId, - ) -> (Arc, Arc<[DefDiagnostic]>) { let loc = e.lookup(db); let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); - let cfg_options = db.crate_graph()[krate].cfg_options.clone(); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let rustc_has_incoherent_inherent_impls = item_tree .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()) @@ -305,53 +295,21 @@ impl EnumData { .exists(); let enum_ = &item_tree[loc.id.value]; - let mut variants = Arena::new(); - let mut diagnostics = Vec::new(); - for tree_id in enum_.variants.clone() { - let attrs = item_tree.attrs(db, krate, tree_id.into()); - let var = &item_tree[tree_id]; - if attrs.is_cfg_enabled(&cfg_options) { - let (var_data, field_diagnostics) = lower_fields( - db, - krate, - loc.id.file_id(), - loc.container.local_id, - &item_tree, - &cfg_options, - &var.fields, - Some(enum_.visibility), - ); - diagnostics.extend(field_diagnostics); - - variants.alloc(EnumVariantData { - name: var.name.clone(), - variant_data: Arc::new(var_data), - tree_id, - }); - } else { - diagnostics.push(DefDiagnostic::unconfigured_code( - loc.container.local_id, - InFile::new(loc.id.file_id(), var.ast_id.erase()), - attrs.cfg().unwrap(), - cfg_options.clone(), - )) - } - } - ( - Arc::new(EnumData { - name: enum_.name.clone(), - variants, - repr, - visibility: item_tree[enum_.visibility].clone(), - rustc_has_incoherent_inherent_impls, - }), - diagnostics.into(), - ) + Arc::new(EnumData { + name: enum_.name.clone(), + variants: loc.container.def_map(db)[loc.container.local_id].scope.enums[&e] + .iter() + .map(|&id| (id, item_tree[id.lookup(db).id.value].name.clone())) + .collect(), + repr, + visibility: item_tree[enum_.visibility].clone(), + rustc_has_incoherent_inherent_impls, + }) } - pub fn variant(&self, name: &Name) -> Option { - let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?; + pub fn variant(&self, name: &Name) -> Option { + let &(id, _) = self.variants.iter().find(|(_id, n)| n == name)?; Some(id) } @@ -363,82 +321,46 @@ impl EnumData { } } -impl HasChildSource for EnumId { - type Value = ast::Variant; - fn child_source( - &self, +impl EnumVariantData { + pub(crate) fn enum_variant_data_query( db: &dyn DefDatabase, - ) -> InFile> { - let loc = &self.lookup(db); - let src = loc.source(db); - let mut trace = Trace::new_for_map(); - lower_enum(db, &mut trace, &src, loc); - src.with_value(trace.into_map()) + e: EnumVariantId, + ) -> Arc { + db.enum_variant_data_with_diagnostics(e).0 } -} -fn lower_enum( - db: &dyn DefDatabase, - trace: &mut Trace, - ast: &InFile, - loc: &EnumLoc, -) { - let item_tree = loc.id.item_tree(db); - let krate = loc.container.krate; - - let item_tree_variants = item_tree[loc.id.value].variants.clone(); - - let cfg_options = &db.crate_graph()[krate].cfg_options; - let variants = ast - .value - .variant_list() - .into_iter() - .flat_map(|it| it.variants()) - .zip(item_tree_variants) - .filter(|&(_, item_tree_id)| { - item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) - }); - for (var, item_tree_id) in variants { - trace.alloc( - || var.clone(), - || EnumVariantData { - name: var.name().map_or_else(Name::missing, |it| it.as_name()), - variant_data: Arc::new(VariantData::new( - db, - ast.with_value(var.kind()), - loc.container, - &item_tree, - item_tree_id, - )), - tree_id: item_tree_id, - }, + pub(crate) fn enum_variant_data_with_diagnostics_query( + db: &dyn DefDatabase, + e: EnumVariantId, + ) -> (Arc, Arc<[DefDiagnostic]>) { + let loc = e.lookup(db); + let krate = loc.container.krate; + let item_tree = loc.id.item_tree(db); + let cfg_options = db.crate_graph()[krate].cfg_options.clone(); + let variant = &item_tree[loc.id.value]; + + let (var_data, field_diagnostics) = lower_fields( + db, + krate, + loc.id.file_id(), + loc.container.local_id, + &item_tree, + &cfg_options, + &variant.fields, + Some(item_tree[loc.parent.lookup(db).id.value].visibility), ); + + ( + Arc::new(EnumVariantData { + name: variant.name.clone(), + variant_data: Arc::new(var_data), + }), + field_diagnostics.into(), + ) } } impl VariantData { - fn new( - db: &dyn DefDatabase, - flavor: InFile, - module_id: ModuleId, - item_tree: &ItemTree, - variant: la_arena::Idx, - ) -> Self { - let mut trace = Trace::new_for_arena(); - match lower_struct( - db, - &mut trace, - &flavor, - module_id.krate, - item_tree, - &item_tree[variant].fields, - ) { - StructKind::Tuple => VariantData::Tuple(trace.into_arena()), - StructKind::Record => VariantData::Record(trace.into_arena()), - StructKind::Unit => VariantData::Unit, - } - } - pub fn fields(&self) -> &Arena { const EMPTY: &Arena = &Arena::new(); match &self { @@ -468,14 +390,13 @@ impl HasChildSource for VariantId { let item_tree; let (src, fields, container) = match *self { VariantId::EnumVariantId(it) => { - // I don't really like the fact that we call into parent source - // here, this might add to more queries then necessary. - let lookup = it.parent.lookup(db); + let lookup = it.lookup(db); item_tree = lookup.id.item_tree(db); - let src = it.parent.child_source(db); - let tree_id = db.enum_data(it.parent).variants[it.local_id].tree_id; - let fields = &item_tree[tree_id].fields; - (src.map(|map| map[it.local_id].kind()), fields, lookup.container) + ( + lookup.source(db).map(|it| it.kind()), + &item_tree[lookup.id.value].fields, + lookup.container, + ) } VariantId::StructId(it) => { let lookup = it.lookup(db); diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 70c0d5193d4bf..4201b1dd17435 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -11,7 +11,7 @@ use crate::{ attr::{Attrs, AttrsWithOwner}, body::{scope::ExprScopes, Body, BodySourceMap}, data::{ - adt::{EnumData, StructData}, + adt::{EnumData, EnumVariantData, StructData}, ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData, }, @@ -22,12 +22,12 @@ use crate::{ nameres::{diagnostics::DefDiagnostic, DefMap}, visibility::{self, Visibility}, AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, - EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, - FunctionLoc, GenericDefId, ImplId, ImplLoc, InTypeConstId, InTypeConstLoc, LocalEnumVariantId, - LocalFieldId, Macro2Id, Macro2Loc, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, - ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, - TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, - VariantId, + EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, + ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, InTypeConstId, + InTypeConstLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroId, MacroRulesId, MacroRulesLoc, + MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, + TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, + UseId, UseLoc, VariantId, }; #[salsa::query_group(InternDatabaseStorage)] @@ -46,6 +46,8 @@ pub trait InternDatabase: SourceDatabase { #[salsa::interned] fn intern_enum(&self, loc: EnumLoc) -> EnumId; #[salsa::interned] + fn intern_enum_variant(&self, loc: EnumVariantLoc) -> EnumVariantId; + #[salsa::interned] fn intern_const(&self, loc: ConstLoc) -> ConstId; #[salsa::interned] fn intern_static(&self, loc: StaticLoc) -> StaticId; @@ -131,8 +133,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; - #[salsa::invoke(EnumData::enum_data_with_diagnostics_query)] - fn enum_data_with_diagnostics(&self, e: EnumId) -> (Arc, Arc<[DefDiagnostic]>); + #[salsa::invoke(EnumVariantData::enum_variant_data_query)] + fn enum_variant_data(&self, id: EnumVariantId) -> Arc; + + #[salsa::invoke(EnumVariantData::enum_variant_data_with_diagnostics_query)] + fn enum_variant_data_with_diagnostics( + &self, + id: EnumVariantId, + ) -> (Arc, Arc<[DefDiagnostic]>); #[salsa::invoke(ImplData::impl_data_query)] fn impl_data(&self, e: ImplId) -> Arc; @@ -189,18 +197,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc>; - #[salsa::invoke(Attrs::fields_attrs_query)] fn fields_attrs(&self, def: VariantId) -> Arc>; - #[salsa::invoke(crate::attr::variants_attrs_source_map)] - fn variants_attrs_source_map( - &self, - def: EnumId, - ) -> Arc>>; - #[salsa::invoke(crate::attr::fields_attrs_source_map)] fn fields_attrs_source_map( &self, diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 67e43f15cd3f3..3ff51b9845cef 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -2,7 +2,10 @@ use std::{cmp::Ordering, iter}; -use hir_expand::name::{known, AsName, Name}; +use hir_expand::{ + name::{known, AsName, Name}, + Lookup, +}; use rustc_hash::FxHashSet; use crate::{ @@ -139,9 +142,10 @@ fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Opti if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { // - if the item is an enum variant, refer to it via the enum - if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(variant.parent.into()), from) { - let data = ctx.db.enum_data(variant.parent); - path.push_segment(data.variants[variant.local_id].name.clone()); + if let Some(mut path) = + find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), from) + { + path.push_segment(ctx.db.enum_variant_data(variant).name.clone()); return Some(path); } // If this doesn't work, it seems we have no way of referring to the diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 168ee4acffbef..436bc0c98c2ad 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -18,8 +18,8 @@ use crate::{ db::DefDatabase, per_ns::PerNs, visibility::{Visibility, VisibilityExplicity}, - AdtId, BuiltinType, ConstId, ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, - ModuleDefId, ModuleId, TraitId, UseId, + AdtId, BuiltinType, ConstId, EnumId, EnumVariantId, ExternCrateId, HasModule, ImplId, + LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId, }; #[derive(Debug, Default)] @@ -79,6 +79,7 @@ pub struct ItemScope { /// declared. declarations: Vec, + pub enums: FxHashMap>, impls: Vec, unnamed_consts: Vec, /// Traits imported via `use Trait as _;`. @@ -718,6 +719,7 @@ impl ItemScope { use_imports_types, use_imports_macros, macro_invocations, + enums, } = self; types.shrink_to_fit(); values.shrink_to_fit(); @@ -736,6 +738,7 @@ impl ItemScope { extern_crate_decls.shrink_to_fit(); use_decls.shrink_to_fit(); macro_invocations.shrink_to_fit(); + enums.shrink_to_fit(); } } diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 82ea5ffeba17c..0f81bef500643 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -41,7 +41,7 @@ mod tests; use std::{ fmt::{self, Debug}, hash::{Hash, Hasher}, - ops::Index, + ops::{Index, Range}, }; use ast::{AstNode, HasName, StructKind}; @@ -308,7 +308,7 @@ pub enum AttrOwner { /// Inner attributes of the source file. TopLevel, - Variant(Idx), + Variant(FileItemTreeId), Field(Idx), Param(Idx), TypeOrConstParamData(Idx), @@ -329,7 +329,7 @@ macro_rules! from_attrs { from_attrs!( ModItem(ModItem), - Variant(Idx), + Variant(FileItemTreeId), Field(Idx), Param(Idx), TypeOrConstParamData(Idx), @@ -352,35 +352,44 @@ pub trait ItemTreeNode: Clone { fn id_to_mod_item(id: FileItemTreeId) -> ModItem; } -pub struct FileItemTreeId(Idx); +pub struct FileItemTreeId(Idx); -impl FileItemTreeId { +impl FileItemTreeId { + pub fn range_iter(range: Range) -> impl Iterator { + (range.start.index().into_raw().into_u32()..range.end.index().into_raw().into_u32()) + .map(RawIdx::from_u32) + .map(Idx::from_raw) + .map(Self) + } +} + +impl FileItemTreeId { pub fn index(&self) -> Idx { self.0 } } -impl Clone for FileItemTreeId { +impl Clone for FileItemTreeId { fn clone(&self) -> Self { Self(self.0) } } -impl Copy for FileItemTreeId {} +impl Copy for FileItemTreeId {} -impl PartialEq for FileItemTreeId { +impl PartialEq for FileItemTreeId { fn eq(&self, other: &FileItemTreeId) -> bool { self.0 == other.0 } } -impl Eq for FileItemTreeId {} +impl Eq for FileItemTreeId {} -impl Hash for FileItemTreeId { +impl Hash for FileItemTreeId { fn hash(&self, state: &mut H) { self.0.hash(state) } } -impl fmt::Debug for FileItemTreeId { +impl fmt::Debug for FileItemTreeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } @@ -415,12 +424,12 @@ impl TreeId { } #[derive(Debug)] -pub struct ItemTreeId { +pub struct ItemTreeId { tree: TreeId, pub value: FileItemTreeId, } -impl ItemTreeId { +impl ItemTreeId { pub fn new(tree: TreeId, idx: FileItemTreeId) -> Self { Self { tree, value: idx } } @@ -438,22 +447,22 @@ impl ItemTreeId { } } -impl Copy for ItemTreeId {} -impl Clone for ItemTreeId { +impl Copy for ItemTreeId {} +impl Clone for ItemTreeId { fn clone(&self) -> Self { *self } } -impl PartialEq for ItemTreeId { +impl PartialEq for ItemTreeId { fn eq(&self, other: &Self) -> bool { self.tree == other.tree && self.value == other.value } } -impl Eq for ItemTreeId {} +impl Eq for ItemTreeId {} -impl Hash for ItemTreeId { +impl Hash for ItemTreeId { fn hash(&self, state: &mut H) { self.tree.hash(state); self.value.hash(state); @@ -568,6 +577,13 @@ impl Index> for ItemTree { } } +impl Index> for ItemTree { + type Output = Variant; + fn index(&self, id: FileItemTreeId) -> &Variant { + &self[id.index()] + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct Use { pub visibility: RawVisibilityId, @@ -678,7 +694,7 @@ pub struct Enum { pub name: Name, pub visibility: RawVisibilityId, pub generic_params: Interned, - pub variants: IdxRange, + pub variants: Range>, pub ast_id: FileAstId, } diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 6343b43a016b2..8d19b5000cf4d 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -253,25 +253,27 @@ impl<'a> Ctx<'a> { let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_); let variants = match &enum_.variant_list() { Some(variant_list) => self.lower_variants(variant_list), - None => IdxRange::new(self.next_variant_idx()..self.next_variant_idx()), + None => { + FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx()) + } }; let res = Enum { name, visibility, generic_params, variants, ast_id }; Some(id(self.data().enums.alloc(res))) } - fn lower_variants(&mut self, variants: &ast::VariantList) -> IdxRange { + fn lower_variants(&mut self, variants: &ast::VariantList) -> Range> { let start = self.next_variant_idx(); for variant in variants.variants() { if let Some(data) = self.lower_variant(&variant) { let idx = self.data().variants.alloc(data); self.add_attrs( - idx.into(), + FileItemTreeId(idx).into(), RawAttrs::new(self.db.upcast(), &variant, self.span_map()), ); } } let end = self.next_variant_idx(); - IdxRange::new(start..end) + FileItemTreeId(start)..FileItemTreeId(end) } fn lower_variant(&mut self, variant: &ast::Variant) -> Option { diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 8693b9a98c9d9..d8a0cb569839e 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -318,7 +318,7 @@ impl Printer<'_> { self.print_generic_params(generic_params); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { - for variant in variants.clone() { + for variant in FileItemTreeId::range_iter(variants.clone()) { let Variant { name, fields, ast_id: _ } = &this.tree[variant]; this.print_attrs_of(variant, "\n"); w!(this, "{}", name.display(self.db.upcast())); diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 66e0d2cc346bc..fc2edce93da71 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -125,12 +125,8 @@ impl LangItems { } ModuleDefId::AdtId(AdtId::EnumId(e)) => { lang_items.collect_lang_item(db, e, LangItemTarget::EnumId); - db.enum_data(e).variants.iter().for_each(|(local_id, _)| { - lang_items.collect_lang_item( - db, - EnumVariantId { parent: e, local_id }, - LangItemTarget::EnumVariant, - ); + module_data.scope.enums[&e].iter().for_each(|&id| { + lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant); }); } ModuleDefId::AdtId(AdtId::StructId(s)) => { diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index aa84ccaee6eee..1712eaede0920 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -100,7 +100,7 @@ use crate::{ db::DefDatabase, item_tree::{ Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules, - Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, + Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant, }, }; @@ -299,12 +299,15 @@ impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum); // FIXME: rename to `VariantId`, only enums can ave variants #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct EnumVariantId { +pub struct EnumVariantId(salsa::InternId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct EnumVariantLoc { + pub container: ModuleId, + pub id: ItemTreeId, pub parent: EnumId, - pub local_id: LocalEnumVariantId, } - -pub type LocalEnumVariantId = Idx; +impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct FieldId { @@ -953,23 +956,21 @@ impl VariantId { match self { VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), - VariantId::EnumVariantId(it) => { - db.enum_data(it.parent).variants[it.local_id].variant_data.clone() - } + VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(), } } pub fn file_id(self, db: &dyn DefDatabase) -> HirFileId { match self { - VariantId::EnumVariantId(it) => it.parent.lookup(db).id.file_id(), + VariantId::EnumVariantId(it) => it.lookup(db).id.file_id(), VariantId::StructId(it) => it.lookup(db).id.file_id(), VariantId::UnionId(it) => it.lookup(db).id.file_id(), } } - pub fn adt_id(self) -> AdtId { + pub fn adt_id(self, db: &dyn DefDatabase) -> AdtId { match self { - VariantId::EnumVariantId(it) => it.parent.into(), + VariantId::EnumVariantId(it) => it.lookup(db).parent.into(), VariantId::StructId(it) => it.into(), VariantId::UnionId(it) => it.into(), } @@ -1016,7 +1017,7 @@ impl HasModule for ExternCrateId { impl HasModule for VariantId { fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { - VariantId::EnumVariantId(it) => it.parent.lookup(db).container, + VariantId::EnumVariantId(it) => it.lookup(db).container, VariantId::StructId(it) => it.lookup(db).container, VariantId::UnionId(it) => it.lookup(db).container, } @@ -1045,7 +1046,7 @@ impl HasModule for TypeOwnerId { TypeOwnerId::TraitAliasId(it) => it.lookup(db).container, TypeOwnerId::TypeAliasId(it) => it.lookup(db).module(db), TypeOwnerId::ImplId(it) => it.lookup(db).container, - TypeOwnerId::EnumVariantId(it) => it.parent.lookup(db).container, + TypeOwnerId::EnumVariantId(it) => it.lookup(db).container, } } } @@ -1056,7 +1057,7 @@ impl HasModule for DefWithBodyId { DefWithBodyId::FunctionId(it) => it.lookup(db).module(db), DefWithBodyId::StaticId(it) => it.lookup(db).module(db), DefWithBodyId::ConstId(it) => it.lookup(db).module(db), - DefWithBodyId::VariantId(it) => it.parent.lookup(db).container, + DefWithBodyId::VariantId(it) => it.lookup(db).container, DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db), } } @@ -1071,7 +1072,7 @@ impl HasModule for GenericDefId { GenericDefId::TraitAliasId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), GenericDefId::ImplId(it) => it.lookup(db).container, - GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container, + GenericDefId::EnumVariantId(it) => it.lookup(db).container, GenericDefId::ConstId(it) => it.lookup(db).module(db), } } @@ -1098,7 +1099,7 @@ impl ModuleDefId { ModuleDefId::ModuleId(id) => *id, ModuleDefId::FunctionId(id) => id.lookup(db).module(db), ModuleDefId::AdtId(id) => id.module(db), - ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container, + ModuleDefId::EnumVariantId(id) => id.lookup(db).container, ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), ModuleDefId::StaticId(id) => id.lookup(db).module(db), ModuleDefId::TraitId(id) => id.lookup(db).container, @@ -1117,7 +1118,7 @@ impl AttrDefId { AttrDefId::FieldId(it) => it.parent.module(db).krate, AttrDefId::AdtId(it) => it.module(db).krate, AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, - AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.krate, + AttrDefId::EnumVariantId(it) => it.lookup(db).container.krate, AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, AttrDefId::TraitId(it) => it.lookup(db).container.krate, diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index a18ac4b28c4ea..fc15a77e8c349 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -23,7 +23,7 @@ use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{Span, SyntaxContextId}; +use span::{ErasedFileAstId, Span, SyntaxContextId}; use stdx::always; use syntax::{ast, SmolStr}; use triomphe::Arc; @@ -51,7 +51,7 @@ use crate::{ per_ns::PerNs, tt, visibility::{RawVisibility, Visibility}, - AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId, + AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantLoc, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, @@ -980,24 +980,35 @@ impl DefCollector<'_> { cov_mark::hit!(glob_enum); // glob import from enum => just import all the variants - // XXX: urgh, so this works by accident! Here, we look at - // the enum data, and, in theory, this might require us to - // look back at the crate_def_map, creating a cycle. For - // example, `enum E { crate::some_macro!(); }`. Luckily, the - // only kind of macro that is allowed inside enum is a - // `cfg_macro`, and we don't need to run name resolution for - // it, but this is sheer luck! - let enum_data = self.db.enum_data(e); - let resolutions = enum_data - .variants - .iter() - .map(|(local_id, variant_data)| { - let name = variant_data.name.clone(); - let variant = EnumVariantId { parent: e, local_id }; - let res = PerNs::both(variant.into(), variant.into(), vis, None); - (Some(name), res) - }) - .collect::>(); + // We need to check if the def map the enum is from is us, then we can't + // call the def-map query since we are currently constructing it! + let loc = e.lookup(self.db); + let tree = loc.id.item_tree(self.db); + let current_def_map = self.def_map.krate == loc.container.krate + && self.def_map.block_id() == loc.container.block; + let resolutions = if current_def_map { + self.def_map.modules[loc.container.local_id].scope.enums[&e] + .iter() + .map(|&variant| { + let name = tree[variant.lookup(self.db).id.value].name.clone(); + let res = + PerNs::both(variant.into(), variant.into(), vis, None); + (Some(name), res) + }) + .collect::>() + } else { + loc.container.def_map(self.db).modules[loc.container.local_id] + .scope + .enums[&e] + .iter() + .map(|&variant| { + let name = tree[variant.lookup(self.db).id.value].name.clone(); + let res = + PerNs::both(variant.into(), variant.into(), vis, None); + (Some(name), res) + }) + .collect::>() + }; self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); } Some(d) => { @@ -1577,7 +1588,7 @@ impl ModCollector<'_, '_> { let attrs = self.item_tree.attrs(db, krate, item.into()); if let Some(cfg) = attrs.cfg() { if !self.is_cfg_enabled(&cfg) { - self.emit_unconfigured_diagnostic(item, &cfg); + self.emit_unconfigured_diagnostic(item.ast_id(self.item_tree).erase(), &cfg); return; } } @@ -1708,17 +1719,40 @@ impl ModCollector<'_, '_> { } ModItem::Enum(id) => { let it = &self.item_tree[id]; + let enum_ = + EnumLoc { container: module, id: ItemTreeId::new(self.tree_id, id) } + .intern(db); let vis = resolve_vis(def_map, &self.item_tree[it.visibility]); - update_def( - self.def_collector, - EnumLoc { container: module, id: ItemTreeId::new(self.tree_id, id) } - .intern(db) - .into(), - &it.name, - vis, - false, - ); + update_def(self.def_collector, enum_.into(), &it.name, vis, false); + + let variants = FileItemTreeId::range_iter(it.variants.clone()) + .filter_map(|variant| { + let attrs = self.item_tree.attrs(db, krate, variant.into()); + if let Some(cfg) = attrs.cfg() { + if !self.is_cfg_enabled(&cfg) { + self.emit_unconfigured_diagnostic( + self.item_tree[variant.index()].ast_id.erase(), + &cfg, + ); + return None; + } + } + + Some( + EnumVariantLoc { + container: module, + id: ItemTreeId::new(self.tree_id, variant), + parent: enum_, + } + .intern(db), + ) + }) + .collect(); + self.def_collector.def_map.modules[module_id] + .scope + .enums + .insert(enum_, variants); } ModItem::Const(id) => { let it = &self.item_tree[id]; @@ -2360,10 +2394,8 @@ impl ModCollector<'_, '_> { self.def_collector.cfg_options.check(cfg) != Some(false) } - fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) { - let ast_id = item.ast_id(self.item_tree); - - let ast_id = InFile::new(self.file_id(), ast_id.erase()); + fn emit_unconfigured_diagnostic(&mut self, ast_id: ErasedFileAstId, cfg: &CfgExpr) { + let ast_id = InFile::new(self.file_id(), ast_id); self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id, ast_id, diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 389dabdbc8678..372d148021841 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -11,18 +11,18 @@ //! `ReachedFixedPoint` signals about this. use base_db::Edition; -use hir_expand::name::Name; +use hir_expand::{name::Name, Lookup}; use triomphe::Arc; use crate::{ - data::adt::VariantData, db::DefDatabase, item_scope::{ImportOrExternCrate, BUILTIN_SCOPE}, + item_tree::Fields, nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs}, path::{ModPath, PathKind}, per_ns::PerNs, visibility::{RawVisibility, Visibility}, - AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, + AdtId, CrateId, LocalModuleId, ModuleDefId, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -355,29 +355,55 @@ impl DefMap { ModuleDefId::AdtId(AdtId::EnumId(e)) => { // enum variant cov_mark::hit!(can_import_enum_variant); - let enum_data = db.enum_data(e); - match enum_data.variant(segment) { - Some(local_id) => { - let variant = EnumVariantId { parent: e, local_id }; - match &*enum_data.variants[local_id].variant_data { - VariantData::Record(_) => { - PerNs::types(variant.into(), Visibility::Public, None) - } - VariantData::Tuple(_) | VariantData::Unit => PerNs::both( - variant.into(), - variant.into(), - Visibility::Public, - None, - ), - } - } + + let loc = e.lookup(db); + let tree = loc.id.item_tree(db); + let current_def_map = + self.krate == loc.container.krate && self.block_id() == loc.container.block; + let res = if current_def_map { + self.modules[loc.container.local_id].scope.enums[&e].iter().find_map( + |&variant| { + let variant_data = &tree[variant.lookup(db).id.value]; + (variant_data.name == *segment).then(|| match variant_data.fields { + Fields::Record(_) => { + PerNs::types(variant.into(), Visibility::Public, None) + } + Fields::Tuple(_) | Fields::Unit => PerNs::both( + variant.into(), + variant.into(), + Visibility::Public, + None, + ), + }) + }, + ) + } else { + loc.container.def_map(db).modules[loc.container.local_id].scope.enums[&e] + .iter() + .find_map(|&variant| { + let variant_data = &tree[variant.lookup(db).id.value]; + (variant_data.name == *segment).then(|| match variant_data.fields { + Fields::Record(_) => { + PerNs::types(variant.into(), Visibility::Public, None) + } + Fields::Tuple(_) | Fields::Unit => PerNs::both( + variant.into(), + variant.into(), + Visibility::Public, + None, + ), + }) + }) + }; + match res { + Some(res) => res, None => { return ResolvePathResult::with( PerNs::types(e.into(), vis, imp), ReachedFixedPoint::Yes, Some(i), Some(self.krate), - ); + ) } } } diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs index f4f5541e3733d..8e0df46970180 100644 --- a/crates/hir-def/src/pretty.rs +++ b/crates/hir-def/src/pretty.rs @@ -39,11 +39,9 @@ pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) LangItemTarget::Trait(it) => { write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))? } - LangItemTarget::EnumVariant(it) => write!( - buf, - "{}", - db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()) - )?, + LangItemTarget::EnumVariant(it) => { + write!(buf, "{}", db.enum_variant_data(it).name.display(db.upcast()))? + } } if let Some(s) = s { diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 1d850f721c1f8..bb530259ac585 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -1111,7 +1111,7 @@ impl HasResolver for DefWithBodyId { DefWithBodyId::ConstId(c) => c.resolver(db), DefWithBodyId::FunctionId(f) => f.resolver(db), DefWithBodyId::StaticId(s) => s.resolver(db), - DefWithBodyId::VariantId(v) => v.parent.resolver(db), + DefWithBodyId::VariantId(v) => v.resolver(db), DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db), } } @@ -1137,7 +1137,7 @@ impl HasResolver for GenericDefId { GenericDefId::TraitAliasId(inner) => inner.resolver(db), GenericDefId::TypeAliasId(inner) => inner.resolver(db), GenericDefId::ImplId(inner) => inner.resolver(db), - GenericDefId::EnumVariantId(inner) => inner.parent.resolver(db), + GenericDefId::EnumVariantId(inner) => inner.resolver(db), GenericDefId::ConstId(inner) => inner.resolver(db), } } @@ -1145,14 +1145,14 @@ impl HasResolver for GenericDefId { impl HasResolver for EnumVariantId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { - self.parent.resolver(db) + self.lookup(db).parent.resolver(db) } } impl HasResolver for VariantId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { - VariantId::EnumVariantId(it) => it.parent.resolver(db), + VariantId::EnumVariantId(it) => it.resolver(db), VariantId::StructId(it) => it.resolver(db), VariantId::UnionId(it) => it.resolver(db), } diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs index 3770103cda53e..4698e967eb7d9 100644 --- a/crates/hir-def/src/src.rs +++ b/crates/hir-def/src/src.rs @@ -5,8 +5,8 @@ use la_arena::ArenaMap; use syntax::ast; use crate::{ - db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Lookup, Macro2Loc, - MacroRulesLoc, ProcMacroLoc, UseId, + db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, EnumVariantLoc, ItemLoc, Lookup, + Macro2Loc, MacroRulesLoc, ProcMacroLoc, UseId, }; pub trait HasSource { @@ -40,6 +40,19 @@ impl HasSource for ItemLoc { } } +impl HasSource for EnumVariantLoc { + type Value = ast::Variant; + + fn source(&self, db: &dyn DefDatabase) -> InFile { + let tree = self.id.item_tree(db); + let ast_id_map = db.ast_id_map(self.id.file_id()); + let root = db.parse_or_expand(self.id.file_id()); + let node = &tree[self.id.value]; + + InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id).to_node(&root)) + } +} + impl HasSource for Macro2Loc { type Value = ast::MacroDef; diff --git a/crates/hir-def/src/trace.rs b/crates/hir-def/src/trace.rs index 6e6ceb8e47495..01654f04ccf40 100644 --- a/crates/hir-def/src/trace.rs +++ b/crates/hir-def/src/trace.rs @@ -18,10 +18,7 @@ pub(crate) struct Trace { } impl Trace { - pub(crate) fn new_for_arena() -> Trace { - Trace { arena: Some(Arena::default()), map: None, len: 0 } - } - + #[allow(dead_code)] pub(crate) fn new_for_map() -> Trace { Trace { arena: None, map: Some(ArenaMap::default()), len: 0 } } @@ -41,6 +38,7 @@ impl Trace { id } + #[allow(dead_code)] pub(crate) fn into_arena(mut self) -> Arena { self.arena.take().unwrap() } diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index cd8023f5d7d7d..c73ffa334c3de 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -225,9 +225,7 @@ pub(crate) fn field_visibilities_query( let var_data = match variant_id { VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), - VariantId::EnumVariantId(it) => { - db.enum_data(it.parent).variants[it.local_id].variant_data.clone() - } + VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(), }; let resolver = variant_id.module(db).resolver(db); let mut res = ArenaMap::default(); diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index ed04582cb0abe..035664c644607 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -218,6 +218,9 @@ pub fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc { - let prev_variant = EnumVariantId { local_id, parent: variant_id.parent }; - 1 + db.const_eval_discriminant(prev_variant)? + Some(prev_idx) => { + 1 + db.const_eval_discriminant( + db.enum_data(loc.parent).variants[prev_idx as usize].0, + )? } _ => 0, }; diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index ad790fa094d82..481992aa0b312 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -292,7 +292,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc .display(db.upcast()) .to_string(), DefWithBodyId::VariantId(it) => { - db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string() + db.enum_variant_data(it).name.display(db.upcast()).to_string() } DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"), }); diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 51a044d8ef562..7ea1b9e4213e3 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -582,11 +582,11 @@ impl<'a> DeclValidator<'a> { // Check the field names. let enum_fields_replacements = data .variants - .values() - .filter_map(|variant| { + .iter() + .filter_map(|(_, name)| { Some(Replacement { - current_name: variant.name.clone(), - suggested_text: to_camel_case(&variant.name.to_smol_str())?, + current_name: name.clone(), + suggested_text: to_camel_case(&name.to_smol_str())?, expected_case: CaseType::UpperCamelCase, }) }) diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index 2e04bbfee83b7..e4d4536fc9321 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -318,8 +318,7 @@ impl HirDisplay for Pat { if let Some(variant) = variant { match variant { VariantId::EnumVariantId(v) => { - let data = f.db.enum_data(v.parent); - write!(f, "{}", data.variants[v.local_id].name.display(f.db.upcast()))?; + write!(f, "{}", f.db.enum_variant_data(v).name.display(f.db.upcast()))?; } VariantId::StructId(s) => { write!(f, "{}", f.db.struct_data(s).name.display(f.db.upcast()))? diff --git a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs index a0f6b9368ee01..f066f8b798d6c 100644 --- a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -594,7 +594,7 @@ impl SplitWildcard { let mut ctors: SmallVec<[_; 1]> = enum_data .variants .iter() - .map(|(local_id, _)| EnumVariantId { parent: *enum_id, local_id }) + .map(|&(variant, _)| variant) .filter(|&variant| { // If `exhaustive_patterns` is enabled, we exclude variants known to be // uninhabited. diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index d63a64a70de2a..18f2cdaae5f11 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -20,8 +20,7 @@ use hir_def::{ path::{Path, PathKind}, type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, - TraitId, + HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, }; use hir_expand::name::Name; use intern::{Internable, Interned}; @@ -613,10 +612,9 @@ fn render_const_scalar( else { return f.write_str(""); }; - let data = &f.db.enum_data(e).variants[var_id]; + let data = f.db.enum_variant_data(var_id); write!(f, "{}", data.name.display(f.db.upcast()))?; - let field_types = - f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into()); + let field_types = f.db.field_types(var_id.into()); render_variant_after_name( &data.variant_data, f, @@ -892,11 +890,9 @@ impl HirDisplay for Ty { CallableDefId::StructId(s) => { write!(f, "{}", db.struct_data(s).name.display(f.db.upcast()))? } - CallableDefId::EnumVariantId(e) => write!( - f, - "{}", - db.enum_data(e.parent).variants[e.local_id].name.display(f.db.upcast()) - )?, + CallableDefId::EnumVariantId(e) => { + write!(f, "{}", db.enum_variant_data(e).name.display(f.db.upcast()))? + } }; f.end_location_link(); if parameters.len(Interner) > 0 { diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index a78e3e7dc2515..9dd8e4da22f67 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -40,8 +40,8 @@ use hir_def::{ path::{ModPath, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::TypeRef, - AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup, - TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, + TupleFieldId, TupleId, TypeAliasId, VariantId, }; use hir_expand::name::{name, Name}; use indexmap::IndexSet; @@ -87,28 +87,30 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc ctx.collect_const(&db.const_data(c)), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), DefWithBodyId::VariantId(v) => { - ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() { - hir_def::layout::IntegerType::Pointer(signed) => match signed { - true => BuiltinType::Int(BuiltinInt::Isize), - false => BuiltinType::Uint(BuiltinUint::Usize), + ctx.return_ty = TyBuilder::builtin( + match db.enum_data(v.lookup(db.upcast()).parent.into()).variant_body_type() { + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => BuiltinType::Int(BuiltinInt::Isize), + false => BuiltinType::Uint(BuiltinUint::Usize), + }, + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => BuiltinType::Int(match size { + Integer::I8 => BuiltinInt::I8, + Integer::I16 => BuiltinInt::I16, + Integer::I32 => BuiltinInt::I32, + Integer::I64 => BuiltinInt::I64, + Integer::I128 => BuiltinInt::I128, + }), + false => BuiltinType::Uint(match size { + Integer::I8 => BuiltinUint::U8, + Integer::I16 => BuiltinUint::U16, + Integer::I32 => BuiltinUint::U32, + Integer::I64 => BuiltinUint::U64, + Integer::I128 => BuiltinUint::U128, + }), + }, }, - hir_def::layout::IntegerType::Fixed(size, signed) => match signed { - true => BuiltinType::Int(match size { - Integer::I8 => BuiltinInt::I8, - Integer::I16 => BuiltinInt::I16, - Integer::I32 => BuiltinInt::I32, - Integer::I64 => BuiltinInt::I64, - Integer::I128 => BuiltinInt::I128, - }), - false => BuiltinType::Uint(match size { - Integer::I8 => BuiltinUint::U8, - Integer::I16 => BuiltinUint::U16, - Integer::I32 => BuiltinUint::U32, - Integer::I64 => BuiltinUint::U64, - Integer::I128 => BuiltinUint::U128, - }), - }, - }); + ); } DefWithBodyId::InTypeConstId(c) => { // FIXME(const-generic-body): We should not get the return type in this way. @@ -1062,7 +1064,7 @@ impl<'a> InferenceContext<'a> { Some(ResolveValueResult::ValueNs(value, _)) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); - let ty = self.db.ty(var.parent.into()); + let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(var.into())); } @@ -1105,7 +1107,7 @@ impl<'a> InferenceContext<'a> { } TypeNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); - let ty = self.db.ty(var.parent.into()); + let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(var.into())), unresolved) } @@ -1131,8 +1133,7 @@ impl<'a> InferenceContext<'a> { if let Some((AdtId::EnumId(id), _)) = ty.as_adt() { let enum_data = self.db.enum_data(id); let name = current_segment.first().unwrap().name; - if let Some(local_id) = enum_data.variant(name) { - let variant = EnumVariantId { parent: id, local_id }; + if let Some(variant) = enum_data.variant(name) { return if remaining_segments.len() == 1 { (ty, Some(variant.into())) } else { @@ -1247,8 +1248,7 @@ impl<'a> InferenceContext<'a> { // this could be an enum variant or associated type if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { let enum_data = self.db.enum_data(enum_id); - if let Some(local_id) = enum_data.variant(segment) { - let variant = EnumVariantId { parent: enum_id, local_id }; + if let Some(variant) = enum_data.variant(segment) { return (ty, Some(variant.into())); } } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 118b9c0149f6f..14b0efabffa3e 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -666,7 +666,7 @@ impl InferenceContext<'_> { | Pat::Or(_) => (), Pat::TupleStruct { .. } | Pat::Record { .. } => { if let Some(variant) = self.result.variant_resolution_for_pat(p) { - let adt = variant.adt_id(); + let adt = variant.adt_id(self.db.upcast()); let is_multivariant = match adt { hir_def::AdtId::EnumId(e) => self.db.enum_data(e).variants.len() != 1, _ => false, diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index e61a070265a4b..a09bb15147f65 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -4,7 +4,7 @@ use chalk_ir::cast::Cast; use hir_def::{ path::{Path, PathSegment}, resolver::{ResolveValueResult, TypeNs, ValueNs}, - AdtId, AssocItemId, EnumVariantId, GenericDefId, ItemContainerId, Lookup, + AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup, }; use hir_expand::name::Name; use stdx::never; @@ -395,8 +395,7 @@ impl InferenceContext<'_> { _ => return None, }; let enum_data = self.db.enum_data(enum_id); - let local_id = enum_data.variant(name)?; - let variant = EnumVariantId { parent: enum_id, local_id }; + let variant = enum_data.variant(name)?; self.write_variant_resolution(id, variant.into()); Some((ValueNs::EnumVariantId(variant), subst.clone())) } diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs index e5038543b68e6..dda5ddd0bae85 100644 --- a/crates/hir-ty/src/inhabitedness.rs +++ b/crates/hir-ty/src/inhabitedness.rs @@ -30,17 +30,15 @@ pub(crate) fn is_enum_variant_uninhabited_from( target_mod: ModuleId, db: &dyn HirDatabase, ) -> bool { - let enum_data = db.enum_data(variant.parent); - let vars_attrs = db.variants_attrs(variant.parent); - let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate(); + let is_local = variant.lookup(db.upcast()).container.krate() == target_mod.krate(); let mut uninhabited_from = UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; let inhabitedness = uninhabited_from.visit_variant( variant.into(), - &enum_data.variants[variant.local_id].variant_data, + &db.enum_variant_data(variant).variant_data, subst, - &vars_attrs[variant.local_id], + &db.attrs(variant.into()), is_local, ); inhabitedness == BREAK_VISIBLY_UNINHABITED @@ -117,15 +115,14 @@ impl UninhabitedFrom<'_> { self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local) } AdtId::EnumId(e) => { - let vars_attrs = self.db.variants_attrs(e); let enum_data = self.db.enum_data(e); - for (local_id, enum_var) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.iter() { let variant_inhabitedness = self.visit_variant( - EnumVariantId { parent: e, local_id }.into(), - &enum_var.variant_data, + variant.into(), + &self.db.enum_variant_data(variant).variant_data, subst, - &vars_attrs[local_id], + &self.db.attrs(variant.into()), is_local, ); match variant_inhabitedness { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index b7bfaf2931b6b..539276f42ee0b 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -9,7 +9,7 @@ use hir_def::{ Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, }, - LocalEnumVariantId, LocalFieldId, StructId, + LocalFieldId, StructId, }; use la_arena::{Idx, RawIdx}; use rustc_abi::AddressSpace; @@ -32,15 +32,15 @@ mod adt; mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct RustcEnumVariantIdx(pub LocalEnumVariantId); +pub struct RustcEnumVariantIdx(pub usize); impl rustc_index::Idx for RustcEnumVariantIdx { fn new(idx: usize) -> Self { - RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32))) + RustcEnumVariantIdx(idx) } fn index(self) -> usize { - u32::from(self.0.into_raw()) as usize + self.0 } } diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 8a7715ce8729f..9fcc251356f6f 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -6,9 +6,8 @@ use base_db::salsa::Cycle; use hir_def::{ data::adt::VariantData, layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, - AdtId, EnumVariantId, LocalEnumVariantId, VariantId, + AdtId, VariantId, }; -use la_arena::RawIdx; use rustc_index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; @@ -22,8 +21,8 @@ use crate::{ use super::LayoutCx; -pub(crate) const fn struct_variant_idx() -> RustcEnumVariantIdx { - RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from_u32(0))) +pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx { + RustcEnumVariantIdx(0) } pub fn layout_of_adt_query( @@ -62,12 +61,7 @@ pub fn layout_of_adt_query( let r = data .variants .iter() - .map(|(idx, v)| { - handle_variant( - EnumVariantId { parent: e, local_id: idx }.into(), - &v.variant_data, - ) - }) + .map(|&(v, _)| handle_variant(v.into(), &db.enum_variant_data(v).variant_data)) .collect::, _>>()?; (r, data.repr.unwrap_or_default()) } @@ -89,8 +83,7 @@ pub fn layout_of_adt_query( |min, max| repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)), variants.iter_enumerated().filter_map(|(id, _)| { let AdtId::EnumId(e) = def else { return None }; - let d = - db.const_eval_discriminant(EnumVariantId { parent: e, local_id: id.0 }).ok()?; + let d = db.const_eval_discriminant(db.enum_data(e).variants[id.0].0).ok()?; Some((id, d)) }), // FIXME: The current code for niche-filling relies on variant indices diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index e371e42761560..e160f05b01383 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -762,7 +762,7 @@ impl<'a> TyLoweringContext<'a> { Some(segment) if segment.args_and_bindings.is_some() => Some(segment), _ => last, }; - (segment, Some(var.parent.into())) + (segment, Some(var.lookup(self.db.upcast()).parent.into())) } }; if let Some(segment) = segment { @@ -1375,11 +1375,13 @@ pub(crate) fn field_types_query( let (resolver, def): (_, GenericDefId) = match variant_id { VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()), VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()), - VariantId::EnumVariantId(it) => (it.parent.resolver(db.upcast()), it.parent.into()), + VariantId::EnumVariantId(it) => { + (it.resolver(db.upcast()), it.lookup(db.upcast()).parent.into()) + } }; let generics = generics(db.upcast(), def); let mut res = ArenaMap::default(); - let ctx = TyLoweringContext::new(db, &resolver, GenericDefId::from(variant_id.adt_id()).into()) + let ctx = TyLoweringContext::new(db, &resolver, def.into()) .with_type_param_mode(ParamLoweringMode::Variable); for (field_id, field_data) in var_data.fields().iter() { res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref))); @@ -1740,25 +1742,24 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders PolyFnSig { - let enum_data = db.enum_data(def.parent); - let var_data = &enum_data.variants[def.local_id]; + let var_data = db.enum_variant_data(def); let fields = var_data.variant_data.fields(); - let resolver = def.parent.resolver(db.upcast()); + let resolver = def.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, DefWithBodyId::VariantId(def).into()) .with_type_param_mode(ParamLoweringMode::Variable); let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::>(); - let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders(); + let (ret, binders) = + type_for_adt(db, def.lookup(db.upcast()).parent.into()).into_value_and_skipped_binders(); Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe)) } /// Build the type of a tuple enum variant constructor. fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> Binders { - let enum_data = db.enum_data(def.parent); - let var_data = &enum_data.variants[def.local_id].variant_data; - if let StructKind::Unit = var_data.kind() { - return type_for_adt(db, def.parent.into()); + let e = def.lookup(db.upcast()).parent; + if let StructKind::Unit = db.enum_variant_data(def).variant_data.kind() { + return type_for_adt(db, e.into()); } - let generics = generics(db.upcast(), def.parent.into()); + let generics = generics(db.upcast(), e.into()); let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); make_binders( db, @@ -1812,7 +1813,7 @@ impl CallableDefId { match self { CallableDefId::FunctionId(f) => f.lookup(db).module(db), CallableDefId::StructId(s) => s.lookup(db).container, - CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container, + CallableDefId::EnumVariantId(e) => e.lookup(db).container, } .krate() } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 16075d9073438..431d09c5650f4 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -751,9 +751,19 @@ impl Evaluator<'_> { Variants::Single { .. } => &layout, Variants::Multiple { variants, .. } => { &variants[match f.parent { - hir_def::VariantId::EnumVariantId(it) => { - RustcEnumVariantIdx(it.local_id) - } + hir_def::VariantId::EnumVariantId(it) => RustcEnumVariantIdx({ + let lookup = it.lookup(self.db.upcast()); + let rustc_enum_variant_idx = + lookup.id.value.index().into_raw().into_u32() + - lookup.id.item_tree(self.db.upcast()) + [lookup.parent.lookup(self.db.upcast()).id.value] + .variants + .start + .index() + .into_raw() + .into_u32(); + rustc_enum_variant_idx as usize + }), _ => { return Err(MirEvalError::TypeError( "Multivariant layout only happens for enums", @@ -1412,22 +1422,12 @@ impl Evaluator<'_> { fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result { let layout = self.layout(&ty)?; - let enum_id = 'b: { - match ty.kind(Interner) { - TyKind::Adt(e, _) => match e.0 { - AdtId::EnumId(e) => break 'b e, - _ => (), - }, - _ => (), - } + let &TyKind::Adt(chalk_ir::AdtId(AdtId::EnumId(e)), _) = ty.kind(Interner) else { return Ok(0); }; match &layout.variants { Variants::Single { index } => { - let r = self.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id: index.0, - })?; + let r = self.const_eval_discriminant(self.db.enum_data(e).variants[index.0].0)?; Ok(r) } Variants::Multiple { tag, tag_encoding, variants, .. } => { @@ -1446,17 +1446,15 @@ impl Evaluator<'_> { let candidate_tag = i128::from_le_bytes(pad16(tag, false)) .wrapping_sub(*niche_start as i128) as usize; - let variant = variants + let idx = variants .iter_enumerated() .map(|(it, _)| it) .filter(|it| it != untagged_variant) .nth(candidate_tag) .unwrap_or(*untagged_variant) .0; - let result = self.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id: variant, - })?; + let result = + self.const_eval_discriminant(self.db.enum_data(e).variants[idx].0)?; Ok(result) } } @@ -1579,14 +1577,16 @@ impl Evaluator<'_> { subst: Substitution, locals: &Locals, ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { - let adt = it.adt_id(); + let adt = it.adt_id(self.db.upcast()); if let DefWithBodyId::VariantId(f) = locals.body.owner { if let VariantId::EnumVariantId(it) = it { - if AdtId::from(f.parent) == adt { - // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and - // infinite sized type errors) we use a dummy layout - let i = self.const_eval_discriminant(it)?; - return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); + if let AdtId::EnumId(e) = adt { + if f.lookup(self.db.upcast()).parent == e { + // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and + // infinite sized type errors) we use a dummy layout + let i = self.const_eval_discriminant(it)?; + return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); + } } } } @@ -1602,8 +1602,17 @@ impl Evaluator<'_> { VariantId::EnumVariantId(it) => it, _ => not_supported!("multi variant layout for non-enums"), }; - let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id); let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; + let lookup = enum_variant_id.lookup(self.db.upcast()); + let rustc_enum_variant_idx = lookup.id.value.index().into_raw().into_u32() + - lookup.id.item_tree(self.db.upcast()) + [lookup.parent.lookup(self.db.upcast()).id.value] + .variants + .start + .index() + .into_raw() + .into_u32(); + let rustc_enum_variant_idx = RustcEnumVariantIdx(rustc_enum_variant_idx as usize); let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, @@ -1847,8 +1856,8 @@ impl Evaluator<'_> { .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))); } if let DefWithBodyId::VariantId(f) = locals.body.owner { - if let Some((adt, _)) = ty.as_adt() { - if AdtId::from(f.parent) == adt { + if let Some((AdtId::EnumId(e), _)) = ty.as_adt() { + if f.lookup(self.db.upcast()).parent == e { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy size return Ok(Some((16, 16))); @@ -2019,10 +2028,8 @@ impl Evaluator<'_> { bytes, e, ) { - let data = &this.db.enum_data(e).variants[v].variant_data; - let field_types = this - .db - .field_types(EnumVariantId { parent: e, local_id: v }.into()); + let data = &this.db.enum_variant_data(v).variant_data; + let field_types = this.db.field_types(v.into()); for (f, _) in data.fields().iter() { let offset = l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); @@ -2093,14 +2100,13 @@ impl Evaluator<'_> { } AdtId::UnionId(_) => (), AdtId::EnumId(e) => { - if let Some((variant, layout)) = detect_variant_from_bytes( + if let Some((ev, layout)) = detect_variant_from_bytes( &layout, self.db, self.trait_env.clone(), self.read_memory(addr, layout.size.bytes_usize())?, e, ) { - let ev = EnumVariantId { parent: e, local_id: variant }; for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); let ty = ty.clone().substitute(Interner, subst); @@ -2540,11 +2546,13 @@ impl Evaluator<'_> { match r { Ok(r) => Ok(r), Err(e) => { - let data = self.db.enum_data(variant.parent); + let db = self.db.upcast(); + let loc = variant.lookup(db); + let enum_loc = loc.parent.lookup(db); let name = format!( "{}::{}", - data.name.display(self.db.upcast()), - data.variants[variant.local_id].name.display(self.db.upcast()) + enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast()), + loc.id.item_tree(db)[loc.id.value].name.display(db.upcast()), ); Err(MirEvalError::ConstEvalError(name, Box::new(e))) } diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index c02c5ef8767f9..69d8046524b43 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -456,9 +456,8 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(Some(current)) } ValueNs::EnumVariantId(variant_id) => { - let variant_data = - &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; - if variant_data.variant_data.kind() == StructKind::Unit { + let variant_data = &self.db.enum_variant_data(variant_id).variant_data; + if variant_data.kind() == StructKind::Unit { let ty = self.infer.type_of_expr[expr_id].clone(); current = self.lower_enum_variant( variant_id, @@ -1874,11 +1873,13 @@ impl<'ctx> MirLowerCtx<'ctx> { match r { Ok(r) => Ok(r), Err(e) => { - let data = self.db.enum_data(variant.parent); + let db = self.db.upcast(); + let loc = variant.lookup(db); + let enum_loc = loc.parent.lookup(db); let name = format!( "{}::{}", - data.name.display(self.db.upcast()), - data.variants[variant.local_id].name.display(self.db.upcast()) + enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast()), + loc.id.item_tree(db)[loc.id.value].name.display(db.upcast()), ); Err(MirLowerError::ConstEvalError(name.into(), Box::new(e))) } @@ -2104,7 +2105,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result { - db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string() + db.enum_variant_data(it).name.display(db.upcast()).to_string() } DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"), }); diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 98c2e7c63bc17..bbb513b24cb9d 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -524,22 +524,9 @@ impl MirLowerCtx<'_> { ); current = next; } - let enum_data = self.db.enum_data(v.parent); self.pattern_matching_variant_fields( shape, - &enum_data.variants[v.local_id].variant_data, - variant, - current, - current_else, - &cond_place, - mode, - )? - } - VariantId::StructId(s) => { - let struct_data = self.db.struct_data(s); - self.pattern_matching_variant_fields( - shape, - &struct_data.variant_data, + &self.db.enum_variant_data(v).variant_data, variant, current, current_else, @@ -547,6 +534,15 @@ impl MirLowerCtx<'_> { mode, )? } + VariantId::StructId(s) => self.pattern_matching_variant_fields( + shape, + &self.db.struct_data(s).variant_data, + variant, + current, + current_else, + &cond_place, + mode, + )?, VariantId::UnionId(_) => { return Err(MirLowerError::TypeError("pattern matching on union")) } diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 366c2f662b54b..65fb58848255b 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -7,7 +7,7 @@ use std::{ use either::Either; use hir_def::{body::Body, hir::BindingId}; -use hir_expand::name::Name; +use hir_expand::{name::Name, Lookup}; use la_arena::ArenaMap; use crate::{ @@ -58,8 +58,14 @@ impl MirBody { ); } hir_def::DefWithBodyId::VariantId(id) => { - let data = db.enum_data(id.parent); - w!(this, "enum {} = ", data.name.display(db.upcast())); + let loc = id.lookup(db.upcast()); + let enum_loc = loc.parent.lookup(db.upcast()); + w!( + this, + "enum {}::{} = ", + enum_loc.id.item_tree(db.upcast())[enum_loc.id.value].name.display(db.upcast()), + loc.id.item_tree(db.upcast())[loc.id.value].name.display(db.upcast()), + ) } hir_def::DefWithBodyId::InTypeConstId(id) => { w!(this, "in type const {id:?} = "); @@ -306,8 +312,7 @@ impl<'a> MirPrettyCtx<'a> { hir_def::VariantId::EnumVariantId(e) => { w!(this, "("); f(this, local, head); - let variant_name = - &this.db.enum_data(e.parent).variants[e.local_id].name; + let variant_name = &this.db.enum_variant_data(e).name; w!( this, " as {}).{}", diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index c8cc61cc21b48..671fd9ec3a451 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -16,7 +16,7 @@ use base_db::{FileRange, SourceDatabaseExt}; use expect_test::Expect; use hir_def::{ body::{Body, BodySourceMap, SyntheticSyntax}, - db::{DefDatabase, InternDatabase}, + db::DefDatabase, hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, @@ -145,7 +145,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::VariantId(it) => { - let loc = db.lookup_intern_enum(it.parent); + let loc = it.lookup(&db); loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), @@ -383,7 +383,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::VariantId(it) => { - let loc = db.lookup_intern_enum(it.parent); + let loc = it.lookup(&db); loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), @@ -453,16 +453,12 @@ fn visit_module( visit_body(db, &body, cb); } ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => { - db.enum_data(it) - .variants - .iter() - .map(|(id, _)| hir_def::EnumVariantId { parent: it, local_id: id }) - .for_each(|it| { - let def = it.into(); - cb(def); - let body = db.body(def); - visit_body(db, &body, cb); - }); + db.enum_data(it).variants.iter().for_each(|&(it, _)| { + let def = it.into(); + cb(def); + let body = db.body(def); + visit_body(db, &body, cb); + }); } ModuleDefId::TraitId(it) => { let trait_data = db.trait_data(it); diff --git a/crates/hir-ty/src/tls.rs b/crates/hir-ty/src/tls.rs index 83814ed0ec1f1..db5fa3205778d 100644 --- a/crates/hir-ty/src/tls.rs +++ b/crates/hir-ty/src/tls.rs @@ -107,10 +107,7 @@ impl DebugContext<'_> { let name = match def { CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(), CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(), - CallableDefId::EnumVariantId(e) => { - let enum_data = self.0.enum_data(e.parent); - enum_data.variants[e.local_id].name.clone() - } + CallableDefId::EnumVariantId(e) => self.0.enum_variant_data(e).name.clone(), }; match def { CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name.display(self.0.upcast())), diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index 75b8b9afa7085..e80eab87009ab 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -19,9 +19,8 @@ use hir_def::{ lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, - ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, - LocalEnumVariantId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, - TypeParamId, + ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, Lookup, + OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, }; use hir_expand::name::Name; use intern::Interned; @@ -355,7 +354,7 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, GenericDefId::ConstId(it) => it.lookup(db).container, - GenericDefId::EnumVariantId(it) => return Some(it.parent.into()), + GenericDefId::EnumVariantId(it) => return Some(it.lookup(db).parent.into()), GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) @@ -435,10 +434,12 @@ pub(crate) fn detect_variant_from_bytes<'a>( trait_env: Arc, b: &[u8], e: EnumId, -) -> Option<(LocalEnumVariantId, &'a Layout)> { +) -> Option<(EnumVariantId, &'a Layout)> { let krate = trait_env.krate; let (var_id, var_layout) = match &layout.variants { - hir_def::layout::Variants::Single { index } => (index.0, &*layout), + hir_def::layout::Variants::Single { index } => { + (db.enum_data(e).variants[index.0].0, &*layout) + } hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => { let target_data_layout = db.target_data_layout(krate)?; let size = tag.size(&*target_data_layout).bytes_usize(); @@ -446,11 +447,12 @@ pub(crate) fn detect_variant_from_bytes<'a>( let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false)); match tag_encoding { TagEncoding::Direct => { - let x = variants.iter_enumerated().find(|x| { - db.const_eval_discriminant(EnumVariantId { parent: e, local_id: x.0 .0 }) - == Ok(tag) - })?; - (x.0 .0, x.1) + let (var_idx, layout) = + variants.iter_enumerated().find_map(|(var_idx, v)| { + let def = db.enum_data(e).variants[var_idx.0].0; + (db.const_eval_discriminant(def) == Ok(tag)).then_some((def, v)) + })?; + (var_idx, layout) } TagEncoding::Niche { untagged_variant, niche_start, .. } => { let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize; @@ -460,7 +462,7 @@ pub(crate) fn detect_variant_from_bytes<'a>( .filter(|x| x != untagged_variant) .nth(candidate_tag) .unwrap_or(*untagged_variant); - (variant.0, &variants[variant]) + (db.enum_data(e).variants[variant.0].0, &variants[variant]) } } } diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 5a21f41dca84a..7b9f895bc73eb 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -115,7 +115,7 @@ fn resolve_doc_path_on_( AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), AttrDefId::AdtId(it) => it.resolver(db.upcast()), AttrDefId::FunctionId(it) => it.resolver(db.upcast()), - AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()), + AttrDefId::EnumVariantId(it) => it.resolver(db.upcast()), AttrDefId::StaticId(it) => it.resolver(db.upcast()), AttrDefId::ConstId(it) => it.resolver(db.upcast()), AttrDefId::TraitId(it) => it.resolver(db.upcast()), diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index 403a6c88ab0f5..a2492840cb75c 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs @@ -6,8 +6,8 @@ pub use hir_def::db::{ AttrsQuery, BlockDefMapQuery, BlockItemTreeQueryQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, ConstVisibilityQuery, CrateDefMapQueryQuery, CrateLangItemsQuery, - CrateSupportsNoStdQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, - EnumDataWithDiagnosticsQuery, ExprScopesQuery, ExternCrateDeclDataQuery, + CrateSupportsNoStdQuery, DefDatabase, DefDatabaseStorage, EnumDataQuery, EnumVariantDataQuery, + EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery, ExternCrateDeclDataQuery, FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery, FileItemTreeQuery, FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery, ImplDataQuery, ImplDataWithDiagnosticsQuery, ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery, @@ -19,7 +19,7 @@ pub use hir_def::db::{ MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataQuery, StructDataWithDiagnosticsQuery, TraitAliasDataQuery, TraitDataQuery, TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, UnionDataQuery, - UnionDataWithDiagnosticsQuery, VariantsAttrsQuery, VariantsAttrsSourceMapQuery, + UnionDataWithDiagnosticsQuery, }; pub use hir_expand::db::{ AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage, diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index fc4bbffdb8361..887227bf4d071 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs @@ -93,13 +93,13 @@ impl From for GenericParamId { impl From for Variant { fn from(id: EnumVariantId) -> Self { - Variant { parent: id.parent.into(), id: id.local_id } + Variant { id } } } impl From for EnumVariantId { fn from(def: Variant) -> Self { - EnumVariantId { parent: def.parent.id, local_id: def.id } + def.id } } diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 31cf8ba336434..d10884517f92d 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -116,7 +116,7 @@ impl HasSource for Enum { impl HasSource for Variant { type Ast = ast::Variant; fn source(self, db: &dyn HirDatabase) -> Option> { - Some(self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone())) + Some(self.id.lookup(db.upcast()).source(db.upcast())) } } impl HasSource for Function { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 3180a2b713a89..246fc231b47f5 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -54,9 +54,9 @@ use hir_def::{ src::HasSource as _, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, - ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, - Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, - TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, + ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, + MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, + TypeOrConstParamId, TypeParamId, UnionId, }; use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; use hir_ty::{ @@ -375,9 +375,7 @@ impl ModuleDef { ModuleDef::Module(it) => it.id.into(), ModuleDef::Const(it) => it.id.into(), ModuleDef::Static(it) => it.id.into(), - ModuleDef::Variant(it) => { - EnumVariantId { parent: it.parent.into(), local_id: it.id }.into() - } + ModuleDef::Variant(it) => it.id.into(), ModuleDef::BuiltinType(_) | ModuleDef::Macro(_) => return Vec::new(), }; @@ -586,10 +584,9 @@ impl Module { Adt::Enum(e) => { for v in e.variants(db) { acc.extend(ModuleDef::Variant(v).diagnostics(db)); - } - - for diag in db.enum_data_with_diagnostics(e.id).1.iter() { - emit_def_diagnostic(db, acc, diag); + for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() { + emit_def_diagnostic(db, acc, diag); + } } } } @@ -1084,7 +1081,7 @@ impl Field { let generic_def_id: GenericDefId = match self.parent { VariantDef::Struct(it) => it.id.into(), VariantDef::Union(it) => it.id.into(), - VariantDef::Variant(it) => it.parent.id.into(), + VariantDef::Variant(it) => it.id.into(), }; let substs = TyBuilder::placeholder_subst(db, generic_def_id); let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); @@ -1224,7 +1221,7 @@ impl Enum { } pub fn variants(self, db: &dyn HirDatabase) -> Vec { - db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() + db.enum_data(self.id).variants.iter().map(|&(id, _)| Variant { id }).collect() } pub fn repr(self, db: &dyn HirDatabase) -> Option { @@ -1292,25 +1289,24 @@ impl From<&Variant> for DefWithBodyId { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Variant { - pub(crate) parent: Enum, - pub(crate) id: LocalEnumVariantId, + pub(crate) id: EnumVariantId, } impl Variant { pub fn module(self, db: &dyn HirDatabase) -> Module { - self.parent.module(db) + Module { id: self.id.lookup(db.upcast()).container } } - pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum { - self.parent + pub fn parent_enum(self, db: &dyn HirDatabase) -> Enum { + self.id.lookup(db.upcast()).parent.into() } pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { - Type::from_value_def(db, EnumVariantId { parent: self.parent.id, local_id: self.id }) + Type::from_value_def(db, self.id) } pub fn name(self, db: &dyn HirDatabase) -> Name { - db.enum_data(self.parent.id).variants[self.id].name.clone() + db.enum_variant_data(self.id).name.clone() } pub fn fields(self, db: &dyn HirDatabase) -> Vec { @@ -1326,7 +1322,7 @@ impl Variant { } pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc { - db.enum_data(self.parent.id).variants[self.id].variant_data.clone() + db.enum_variant_data(self.id).variant_data.clone() } pub fn value(self, db: &dyn HirDatabase) -> Option { @@ -1342,7 +1338,21 @@ impl Variant { let parent_layout = parent_enum.layout(db)?; Ok(match &parent_layout.0.variants { layout::Variants::Multiple { variants, .. } => Layout( - Arc::new(variants[RustcEnumVariantIdx(self.id)].clone()), + { + let lookup = self.id.lookup(db.upcast()); + let rustc_enum_variant_idx = lookup.id.value.index().into_raw().into_u32() + - lookup.id.item_tree(db.upcast()) + [lookup.parent.lookup(db.upcast()).id.value] + .variants + .start + .index() + .into_raw() + .into_u32(); + let rustc_enum_variant_idx = + RustcEnumVariantIdx(rustc_enum_variant_idx as usize); + + Arc::new(variants[rustc_enum_variant_idx].clone()) + }, db.target_data_layout(parent_enum.krate(db).into()).unwrap(), ), _ => parent_layout, @@ -1547,7 +1557,7 @@ impl DefWithBody { DefWithBody::Function(it) => it.ret_type(db), DefWithBody::Static(it) => it.ty(db), DefWithBody::Const(it) => it.ty(db), - DefWithBody::Variant(it) => it.parent.variant_body_ty(db), + DefWithBody::Variant(it) => it.parent_enum(db).variant_body_ty(db), DefWithBody::InTypeConst(it) => Type::new_with_resolver_inner( db, &DefWithBodyId::from(it.id).resolver(db.upcast()), diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index e1101dd8236e6..28ac5940e6922 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -262,9 +262,7 @@ impl<'a> SymbolCollector<'a> { DefWithBodyId::FunctionId(id) => Some(self.db.function_data(id).name.to_smol_str()), DefWithBodyId::StaticId(id) => Some(self.db.static_data(id).name.to_smol_str()), DefWithBodyId::ConstId(id) => Some(self.db.const_data(id).name.as_ref()?.to_smol_str()), - DefWithBodyId::VariantId(id) => { - Some(self.db.enum_data(id.parent).variants[id.local_id].name.to_smol_str()) - } + DefWithBodyId::VariantId(id) => Some(self.db.enum_variant_data(id).name.to_smol_str()), DefWithBodyId::InTypeConstId(_) => Some("in type const".into()), } } diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index 259d141404d3f..9871fb5c54090 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -141,7 +141,8 @@ impl RootDatabase { hir::db::UnionDataQuery hir::db::UnionDataWithDiagnosticsQuery hir::db::EnumDataQuery - hir::db::EnumDataWithDiagnosticsQuery + hir::db::EnumVariantDataWithDiagnosticsQuery + hir::db::EnumVariantDataQuery hir::db::ImplDataQuery hir::db::ImplDataWithDiagnosticsQuery hir::db::TraitDataQuery @@ -158,9 +159,7 @@ impl RootDatabase { hir::db::BodyQuery hir::db::ExprScopesQuery hir::db::GenericParamsQuery - hir::db::VariantsAttrsQuery hir::db::FieldsAttrsQuery - hir::db::VariantsAttrsSourceMapQuery hir::db::FieldsAttrsSourceMapQuery hir::db::AttrsQuery hir::db::CrateLangItemsQuery diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index eae23e9548240..05abc9d815183 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -222,7 +222,8 @@ impl RootDatabase { hir_db::UnionDataQuery hir_db::UnionDataWithDiagnosticsQuery hir_db::EnumDataQuery - hir_db::EnumDataWithDiagnosticsQuery + hir_db::EnumDataQuery + hir_db::EnumVariantDataWithDiagnosticsQuery hir_db::ImplDataQuery hir_db::ImplDataWithDiagnosticsQuery hir_db::TraitDataQuery @@ -239,9 +240,7 @@ impl RootDatabase { hir_db::BodyQuery hir_db::ExprScopesQuery hir_db::GenericParamsQuery - hir_db::VariantsAttrsQuery hir_db::FieldsAttrsQuery - hir_db::VariantsAttrsSourceMapQuery hir_db::FieldsAttrsSourceMapQuery hir_db::AttrsQuery hir_db::CrateLangItemsQuery From 6bbd106c70370ca6a78de0676f103810f9eea22a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 15 Jan 2024 11:40:09 +0200 Subject: [PATCH 017/118] Merge commit '9d8889cdfcc3aa0302353fc988ed21ff9bc9925c' into sync-from-ra --- Cargo.lock | 44 ++-- Cargo.toml | 8 +- crates/base-db/src/lib.rs | 18 +- crates/hir-def/Cargo.toml | 7 +- crates/hir-def/src/attr.rs | 9 +- crates/hir-def/src/body/lower.rs | 5 +- crates/hir-def/src/child_by_source.rs | 2 +- crates/hir-def/src/data/adt.rs | 2 +- crates/hir-def/src/db.rs | 7 +- crates/hir-def/src/find_path.rs | 204 ++++++++------- crates/hir-def/src/generics.rs | 241 +++++++++-------- crates/hir-def/src/hir/format_args.rs | 2 +- crates/hir-def/src/hir/type_ref.rs | 3 +- crates/hir-def/src/item_scope.rs | 91 ++++--- crates/hir-def/src/item_tree.rs | 37 ++- crates/hir-def/src/item_tree/lower.rs | 12 +- crates/hir-def/src/item_tree/pretty.rs | 4 +- crates/hir-def/src/lang_item.rs | 17 +- crates/hir-def/src/lib.rs | 17 +- .../builtin_derive_macro.rs | 83 +++--- .../macro_expansion_tests/builtin_fn_macro.rs | 2 +- crates/hir-def/src/nameres.rs | 7 +- crates/hir-def/src/nameres/path_resolution.rs | 8 +- crates/hir-def/src/nameres/tests/macros.rs | 48 ++++ crates/hir-def/src/resolver.rs | 2 +- crates/hir-def/src/test_db.rs | 3 +- crates/hir-def/src/visibility.rs | 52 ++-- crates/hir-expand/src/builtin_derive_macro.rs | 114 ++------ crates/hir-expand/src/builtin_fn_macro.rs | 24 +- crates/hir-expand/src/lib.rs | 4 + crates/hir-expand/src/quote.rs | 4 + crates/hir-ty/Cargo.toml | 6 +- crates/hir-ty/src/chalk_db.rs | 8 +- crates/hir-ty/src/db.rs | 36 +-- crates/hir-ty/src/display.rs | 2 +- crates/hir-ty/src/layout.rs | 11 +- crates/hir-ty/src/layout/adt.rs | 2 +- crates/hir-ty/src/layout/tests.rs | 4 +- crates/hir-ty/src/lib.rs | 15 +- crates/hir-ty/src/lower.rs | 2 +- crates/hir-ty/src/method_resolution.rs | 125 ++++----- crates/hir-ty/src/test_db.rs | 3 +- crates/hir-ty/src/tests/macros.rs | 30 ++- crates/hir/src/attrs.rs | 2 +- crates/hir/src/db.rs | 4 +- crates/hir/src/lib.rs | 2 +- crates/hir/src/symbols.rs | 3 +- .../ide-assists/src/handlers/auto_import.rs | 35 +++ .../src/handlers/extract_function.rs | 52 +++- .../src/handlers/generate_constant.rs | 16 ++ .../src/handlers/generate_delegate_methods.rs | 45 ++-- .../src/handlers/merge_nested_if.rs | 246 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 17 ++ crates/ide-completion/src/context.rs | 5 + crates/ide-completion/src/item.rs | 6 + crates/ide-completion/src/render.rs | 81 ++++++ crates/ide-completion/src/render/function.rs | 14 +- crates/ide-db/src/apply_change.rs | 144 +++++----- crates/ide-db/src/imports/import_assets.rs | 5 +- crates/ide-db/src/lib.rs | 13 +- crates/ide-db/src/path_transform.rs | 21 +- crates/ide-db/src/search.rs | 2 +- .../src/handlers/unresolved_assoc_item.rs | 1 + crates/ide/src/expand_macro.rs | 8 +- crates/ide/src/lib.rs | 2 +- crates/load-cargo/src/lib.rs | 7 +- crates/parser/Cargo.toml | 7 +- crates/parser/src/grammar/expressions.rs | 10 +- crates/parser/src/lexed_str.rs | 2 - crates/parser/src/lib.rs | 5 + .../ok/0208_closure_range_method_call.rast | 49 ++++ .../ok/0208_closure_range_method_call.rs | 4 + crates/proc-macro-api/src/lib.rs | 10 +- crates/proc-macro-api/src/process.rs | 66 ++++- crates/proc-macro-srv-cli/src/main.rs | 3 +- crates/proc-macro-srv/Cargo.toml | 2 +- .../proc-macro-srv/proc-macro-test/Cargo.toml | 3 - .../proc-macro-srv/proc-macro-test/build.rs | 28 +- .../proc-macro-test/imp/Cargo.toml | 3 - .../proc-macro-test/imp/src/lib.rs | 3 - crates/proc-macro-srv/src/lib.rs | 4 +- crates/project-model/src/workspace.rs | 2 +- crates/rust-analyzer/Cargo.toml | 4 +- crates/rust-analyzer/src/global_state.rs | 85 +++--- .../src/handlers/notification.rs | 27 +- crates/rust-analyzer/src/handlers/request.rs | 1 - crates/rust-analyzer/src/lsp/utils.rs | 35 ++- crates/rust-analyzer/src/main_loop.rs | 9 +- crates/rust-analyzer/src/mem_docs.rs | 5 +- crates/rust-analyzer/src/reload.rs | 7 +- crates/rustc-dependencies/Cargo.toml | 23 -- crates/rustc-dependencies/src/lib.rs | 48 ---- crates/syntax/Cargo.toml | 6 +- crates/syntax/src/ast/node_ext.rs | 20 +- crates/syntax/src/ast/token_ext.rs | 2 - crates/syntax/src/lib.rs | 5 + crates/syntax/src/validation.rs | 2 +- crates/test-utils/src/minicore.rs | 10 + crates/vfs-notify/src/lib.rs | 2 +- crates/vfs/src/lib.rs | 110 ++++---- crates/vfs/src/loader.rs | 5 + docs/dev/syntax.md | 3 +- editors/code/package-lock.json | 6 +- 104 files changed, 1650 insertions(+), 1024 deletions(-) create mode 100644 crates/ide-assists/src/handlers/merge_nested_if.rs create mode 100644 crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast create mode 100644 crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs delete mode 100644 crates/rustc-dependencies/Cargo.toml delete mode 100644 crates/rustc-dependencies/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 6670e92f51bc1..15d06222eb42d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -513,7 +513,8 @@ dependencies = [ "mbe", "once_cell", "profile", - "rustc-dependencies", + "ra-ap-rustc_abi", + "ra-ap-rustc_parse_format", "rustc-hash", "smallvec", "span", @@ -579,7 +580,8 @@ dependencies = [ "oorandom", "profile", "project-model", - "rustc-dependencies", + "ra-ap-rustc_abi", + "ra-ap-rustc_index", "rustc-hash", "scoped-tls", "smallvec", @@ -1196,7 +1198,7 @@ dependencies = [ "drop_bomb", "expect-test", "limit", - "rustc-dependencies", + "ra-ap-rustc_lexer", "sourcegen", "stdx", ] @@ -1540,7 +1542,6 @@ dependencies = [ "profile", "project-model", "rayon", - "rustc-dependencies", "rustc-hash", "scip", "serde", @@ -1567,9 +1568,9 @@ dependencies = [ [[package]] name = "rust-analyzer-salsa" -version = "0.17.0-pre.4" +version = "0.17.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c42b8737c320578b441a82daf7cdf8d897468de64e8a774fa54b53a50b6cc0" +checksum = "ca9d387a9801f4fb9b366789ad1bfc08448cafc49cf148d907cfcd88ab665d7f" dependencies = [ "indexmap", "lock_api", @@ -1579,13 +1580,14 @@ dependencies = [ "rust-analyzer-salsa-macros", "rustc-hash", "smallvec", + "triomphe", ] [[package]] name = "rust-analyzer-salsa-macros" -version = "0.17.0-pre.4" +version = "0.17.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db72b0883f3592ade2be15a10583c75e0b269ec26e1190800fda2e2ce5ae6634" +checksum = "a2035f385d7fae31e9b086f40b272ee1d79c484472f31c9a10348a406e841eaf" dependencies = [ "heck", "proc-macro2", @@ -1599,16 +1601,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc-dependencies" -version = "0.0.0" -dependencies = [ - "ra-ap-rustc_abi", - "ra-ap-rustc_index", - "ra-ap-rustc_lexer", - "ra-ap-rustc_parse_format", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -1751,6 +1743,12 @@ dependencies = [ "vfs", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1808,9 +1806,9 @@ dependencies = [ "proc-macro2", "profile", "quote", + "ra-ap-rustc_lexer", "rayon", "rowan", - "rustc-dependencies", "rustc-hash", "smol_str", "sourcegen", @@ -2028,9 +2026,13 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c5a71827ac326072b6405552093e2ad2accd25a32fd78d4edc82d98c7f2409" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" +dependencies = [ + "serde", + "stable_deref_trait", +] [[package]] name = "tt" diff --git a/Cargo.toml b/Cargo.toml index 4ee8064b5e347..35bef151196f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,11 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -rustc-dependencies = { path = "./crates/rustc-dependencies", version = "0.0.0" } + +ra-ap-rustc_lexer = { version = "0.21.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.21.0", default-features = false } +ra-ap-rustc_index = { version = "0.21.0", default-features = false } +ra-ap-rustc_abi = { version = "0.21.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. sourcegen = { path = "./crates/sourcegen" } @@ -108,7 +112,7 @@ itertools = "0.12.0" libc = "0.2.150" nohash-hasher = "0.2.0" rayon = "1.8.0" -rust-analyzer-salsa = "0.17.0-pre.4" +rust-analyzer-salsa = "0.17.0-pre.5" rustc-hash = "1.1.0" semver = "1.0.14" serde = { version = "1.0.192", features = ["derive"] } diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index a0a55df5f99af..92d2b9c3f57c9 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -7,7 +7,6 @@ mod change; use std::panic; -use rustc_hash::FxHashSet; use syntax::{ast, Parse, SourceFile}; use triomphe::Arc; @@ -44,12 +43,13 @@ pub trait Upcast { } pub const DEFAULT_PARSE_LRU_CAP: usize = 128; +pub const DEFAULT_BORROWCK_LRU_CAP: usize = 256; pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc; fn resolve_path(&self, path: AnchoredPath<'_>) -> Option; - fn relevant_crates(&self, file_id: FileId) -> Arc>; + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>; } /// Database which stores all significant input facts: source code and project @@ -84,19 +84,21 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn source_root(&self, id: SourceRootId) -> Arc; - fn source_root_crates(&self, id: SourceRootId) -> Arc>; + fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } -fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc> { +fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> { let graph = db.crate_graph(); - let res = graph + let mut crates = graph .iter() .filter(|&krate| { let root_file = graph[krate].root_file_id; db.file_source_root(root_file) == id }) - .collect(); - Arc::new(res) + .collect::>(); + crates.sort(); + crates.dedup(); + crates.into_iter().collect() } /// Silly workaround for cyclic deps between the traits @@ -113,7 +115,7 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { source_root.resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { let _p = profile::span("relevant_crates"); let source_root = self.0.file_source_root(file_id); self.0.source_root_crates(source_root) diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 5933d30040fad..523ff6fc404e8 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -29,7 +29,8 @@ smallvec.workspace = true hashbrown.workspace = true triomphe.workspace = true -rustc-dependencies.workspace = true +ra-ap-rustc_parse_format.workspace = true +ra-ap-rustc_abi.workspace = true # local deps stdx.workspace = true @@ -53,7 +54,7 @@ test-utils.workspace = true test-fixture.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 26f76afb1f09f..30452e34aac36 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -207,6 +207,13 @@ impl Attrs { }) } + pub fn has_doc_notable_trait(&self) -> bool { + self.by_key("doc").tt_values().any(|tt| { + tt.delimiter.kind == DelimiterKind::Parenthesis && + matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "notable_trait") + }) + } + pub fn doc_exprs(&self) -> impl Iterator + '_ { self.by_key("doc").tt_values().map(DocExpr::parse) } @@ -355,7 +362,7 @@ fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { } impl AttrsWithOwner { - pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self { + pub fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self { Self { attrs: db.attrs(owner), owner } } diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index c728570d9866a..fc0a4eb43dc2a 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -965,11 +965,10 @@ impl ExprCollector<'_> { let res = match self.def_map.modules[module] .scope - .macro_invocations - .get(&InFile::new(outer_file, self.ast_id_map.ast_id_for_ptr(syntax_ptr))) + .macro_invoc(InFile::new(outer_file, self.ast_id_map.ast_id_for_ptr(syntax_ptr))) { // fast path, macro call is in a block module - Some(&call) => Ok(self.expander.enter_expand_id(self.db, call)), + Some(call) => Ok(self.expander.enter_expand_id(self.db, call)), None => self.expander.enter_expand(self.db, mcall, |path| { self.def_map .resolve_path( diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs index c82d2347de5c6..32c53cb950313 100644 --- a/crates/hir-def/src/child_by_source.rs +++ b/crates/hir-def/src/child_by_source.rs @@ -92,7 +92,7 @@ impl ChildBySource for ItemScope { self.impls().for_each(|imp| add_impl(db, res, file_id, imp)); self.extern_crate_decls().for_each(|ext| add_extern_crate(db, res, file_id, ext)); self.use_decls().for_each(|ext| add_use(db, res, file_id, ext)); - self.unnamed_consts().for_each(|konst| { + self.unnamed_consts(db).for_each(|konst| { let loc = konst.lookup(db); if loc.id.file_id() == file_id { res[keys::CONST].insert(loc.source(db).value, konst); diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index a95b78614e822..8772c34f02f1a 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -11,7 +11,7 @@ use hir_expand::{ }; use intern::Interned; use la_arena::{Arena, ArenaMap}; -use rustc_dependencies::abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; +use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use syntax::ast::{self, HasName, HasVisibility}; use triomphe::Arc; diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index d5831022f28f7..70c0d5193d4bf 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -210,13 +210,10 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Attrs; + #[salsa::transparent] #[salsa::invoke(lang_item::lang_attr_query)] fn lang_attr(&self, def: AttrDefId) -> Option; - #[salsa::transparent] - #[salsa::invoke(AttrsWithOwner::attrs_with_owner)] - fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner; - // endregion:attrs #[salsa::invoke(LangItems::lang_item_query)] @@ -240,7 +237,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; + fn crate_lang_items(&self, krate: CrateId) -> Option>; fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; } diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 4737b48703db3..67e43f15cd3f3 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -10,7 +10,7 @@ use crate::{ item_scope::ItemInNs, nameres::DefMap, path::{ModPath, PathKind}, - visibility::Visibility, + visibility::{Visibility, VisibilityExplicity}, CrateRootModuleId, ModuleDefId, ModuleId, }; @@ -24,7 +24,7 @@ pub fn find_path( prefer_prelude: bool, ) -> Option { let _p = profile::span("find_path"); - find_path_inner(db, item, from, None, prefer_no_std, prefer_prelude) + find_path_inner(FindPathCtx { db, prefixed: None, prefer_no_std, prefer_prelude }, item, from) } pub fn find_path_prefixed( @@ -36,7 +36,11 @@ pub fn find_path_prefixed( prefer_prelude: bool, ) -> Option { let _p = profile::span("find_path_prefixed"); - find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std, prefer_prelude) + find_path_inner( + FindPathCtx { db, prefixed: Some(prefix_kind), prefer_no_std, prefer_prelude }, + item, + from, + ) } #[derive(Copy, Clone, Debug)] @@ -83,64 +87,60 @@ impl PrefixKind { } } -/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId -fn find_path_inner( - db: &dyn DefDatabase, - item: ItemInNs, - from: ModuleId, +#[derive(Copy, Clone)] +struct FindPathCtx<'db> { + db: &'db dyn DefDatabase, prefixed: Option, prefer_no_std: bool, prefer_prelude: bool, -) -> Option { +} + +/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId +fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Option { // - if the item is a builtin, it's in scope if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { return Some(ModPath::from_segments(PathKind::Plain, Some(builtin.as_name()))); } - let def_map = from.def_map(db); + let def_map = from.def_map(ctx.db); let crate_root = def_map.crate_root(); // - if the item is a module, jump straight to module search if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { let mut visited_modules = FxHashSet::default(); return find_path_for_module( - db, + FindPathCtx { + prefer_no_std: ctx.prefer_no_std || ctx.db.crate_supports_no_std(crate_root.krate), + ..ctx + }, &def_map, &mut visited_modules, crate_root, from, module_id, MAX_PATH_LEN, - prefixed, - prefer_no_std || db.crate_supports_no_std(crate_root.krate), - prefer_prelude, ) .map(|(item, _)| item); } // - if the item is already in scope, return the name under which it is - let scope_name = find_in_scope(db, &def_map, from, item); - if prefixed.is_none() { + let scope_name = find_in_scope(ctx.db, &def_map, from, item); + if ctx.prefixed.is_none() { if let Some(scope_name) = scope_name { return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name))); } } // - if the item is in the prelude, return the name from there - if let value @ Some(_) = find_in_prelude(db, &crate_root.def_map(db), &def_map, item, from) { + if let value @ Some(_) = + find_in_prelude(ctx.db, &crate_root.def_map(ctx.db), &def_map, item, from) + { return value; } if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { // - if the item is an enum variant, refer to it via the enum - if let Some(mut path) = find_path_inner( - db, - ItemInNs::Types(variant.parent.into()), - from, - prefixed, - prefer_no_std, - prefer_prelude, - ) { - let data = db.enum_data(variant.parent); + if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(variant.parent.into()), from) { + let data = ctx.db.enum_data(variant.parent); path.push_segment(data.variants[variant.local_id].name.clone()); return Some(path); } @@ -152,32 +152,29 @@ fn find_path_inner( let mut visited_modules = FxHashSet::default(); calculate_best_path( - db, + FindPathCtx { + prefer_no_std: ctx.prefer_no_std || ctx.db.crate_supports_no_std(crate_root.krate), + ..ctx + }, &def_map, &mut visited_modules, crate_root, MAX_PATH_LEN, item, from, - prefixed, - prefer_no_std || db.crate_supports_no_std(crate_root.krate), - prefer_prelude, scope_name, ) .map(|(item, _)| item) } fn find_path_for_module( - db: &dyn DefDatabase, + ctx: FindPathCtx<'_>, def_map: &DefMap, visited_modules: &mut FxHashSet, crate_root: CrateRootModuleId, from: ModuleId, module_id: ModuleId, max_len: usize, - prefixed: Option, - prefer_no_std: bool, - prefer_prelude: bool, ) -> Option<(ModPath, Stability)> { if max_len == 0 { return None; @@ -185,8 +182,8 @@ fn find_path_for_module( // Base cases: // - if the item is already in scope, return the name under which it is - let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into())); - if prefixed.is_none() { + let scope_name = find_in_scope(ctx.db, def_map, from, ItemInNs::Types(module_id.into())); + if ctx.prefixed.is_none() { if let Some(scope_name) = scope_name { return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable)); } @@ -198,20 +195,20 @@ fn find_path_for_module( } // - if relative paths are fine, check if we are searching for a parent - if prefixed.filter(PrefixKind::is_absolute).is_none() { + if ctx.prefixed.filter(PrefixKind::is_absolute).is_none() { if let modpath @ Some(_) = find_self_super(def_map, module_id, from) { return modpath.zip(Some(Stable)); } } // - if the item is the crate root of a dependency crate, return the name from the extern prelude - let root_def_map = crate_root.def_map(db); + let root_def_map = crate_root.def_map(ctx.db); for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() { if module_id == def_id { let name = scope_name.unwrap_or_else(|| name.clone()); let name_already_occupied_in_type_ns = def_map - .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { + .with_ancestor_maps(ctx.db, from.local_id, &mut |def_map, local_id| { def_map[local_id] .scope .type_(&name) @@ -229,21 +226,18 @@ fn find_path_for_module( } if let value @ Some(_) = - find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from) + find_in_prelude(ctx.db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from) { return value.zip(Some(Stable)); } calculate_best_path( - db, + ctx, def_map, visited_modules, crate_root, max_len, ItemInNs::Types(module_id.into()), from, - prefixed, - prefer_no_std, - prefer_prelude, scope_name, ) } @@ -256,7 +250,7 @@ fn find_in_scope( item: ItemInNs, ) -> Option { def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { - def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone()) + def_map[local_id].scope.name_of(item).map(|(name, _, _)| name.clone()) }) } @@ -273,7 +267,7 @@ fn find_in_prelude( // Preludes in block DefMaps are ignored, only the crate DefMap is searched let prelude_def_map = prelude_module.def_map(db); let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; - let (name, vis) = prelude_scope.name_of(item)?; + let (name, vis, _declared) = prelude_scope.name_of(item)?; if !vis.is_visible_from(db, from) { return None; } @@ -315,16 +309,13 @@ fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option, def_map: &DefMap, visited_modules: &mut FxHashSet, crate_root: CrateRootModuleId, max_len: usize, item: ItemInNs, from: ModuleId, - mut prefixed: Option, - prefer_no_std: bool, - prefer_prelude: bool, scope_name: Option, ) -> Option<(ModPath, Stability)> { if max_len <= 1 { @@ -341,32 +332,29 @@ fn calculate_best_path( }; // Recursive case: // - otherwise, look for modules containing (reexporting) it and import it from one of those - if item.krate(db) == Some(from.krate) { + if item.krate(ctx.db) == Some(from.krate) { let mut best_path_len = max_len; // Item was defined in the same crate that wants to import it. It cannot be found in any // dependency in this case. - for (module_id, name) in find_local_import_locations(db, item, from) { + for (module_id, name) in find_local_import_locations(ctx.db, item, from) { if !visited_modules.insert(module_id) { cov_mark::hit!(recursive_imports); continue; } if let Some(mut path) = find_path_for_module( - db, + ctx, def_map, visited_modules, crate_root, from, module_id, best_path_len - 1, - prefixed, - prefer_no_std, - prefer_prelude, ) { path.0.push_segment(name); let new_path = match best_path.take() { Some(best_path) => { - select_best_path(best_path, path, prefer_no_std, prefer_prelude) + select_best_path(best_path, path, ctx.prefer_no_std, ctx.prefer_prelude) } None => path, }; @@ -379,8 +367,8 @@ fn calculate_best_path( // too (unless we can't name it at all). It could *also* be (re)exported by the same crate // that wants to import it here, but we always prefer to use the external path here. - for dep in &db.crate_graph()[from.krate].dependencies { - let import_map = db.import_map(dep.crate_id); + for dep in &ctx.db.crate_graph()[from.krate].dependencies { + let import_map = ctx.db.import_map(dep.crate_id); let Some(import_info_for) = import_map.import_info_for(item) else { continue }; for info in import_info_for { if info.is_doc_hidden { @@ -391,16 +379,13 @@ fn calculate_best_path( // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? let Some((mut path, path_stability)) = find_path_for_module( - db, + ctx, def_map, visited_modules, crate_root, from, info.container, max_len - 1, - prefixed, - prefer_no_std, - prefer_prelude, ) else { continue; }; @@ -413,17 +398,21 @@ fn calculate_best_path( ); let new_path_with_stab = match best_path.take() { - Some(best_path) => { - select_best_path(best_path, path_with_stab, prefer_no_std, prefer_prelude) - } + Some(best_path) => select_best_path( + best_path, + path_with_stab, + ctx.prefer_no_std, + ctx.prefer_prelude, + ), None => path_with_stab, }; update_best_path(&mut best_path, new_path_with_stab); } } } - if let Some(module) = item.module(db) { - if module.containing_block().is_some() && prefixed.is_some() { + let mut prefixed = ctx.prefixed; + if let Some(module) = item.module(ctx.db) { + if module.containing_block().is_some() && ctx.prefixed.is_some() { cov_mark::hit!(prefixed_in_block_expression); prefixed = Some(PrefixKind::Plain); } @@ -548,34 +537,35 @@ fn find_local_import_locations( &ext_def_map[module.local_id] }; - if let Some((name, vis)) = data.scope.name_of(item) { + if let Some((name, vis, declared)) = data.scope.name_of(item) { if vis.is_visible_from(db, from) { - let is_private = match vis { - Visibility::Module(private_to) => private_to.local_id == module.local_id, - Visibility::Public => false, - }; - let is_original_def = match item.as_module_def_id() { - Some(module_def_id) => data.scope.declarations().any(|it| it == module_def_id), - None => false, + let is_pub_or_explicit = match vis { + Visibility::Module(_, VisibilityExplicity::Explicit) => { + cov_mark::hit!(explicit_private_imports); + true + } + Visibility::Module(_, VisibilityExplicity::Implicit) => { + cov_mark::hit!(discount_private_imports); + false + } + Visibility::Public => true, }; - // Ignore private imports. these could be used if we are + // Ignore private imports unless they are explicit. these could be used if we are // in a submodule of this module, but that's usually not // what the user wants; and if this module can import // the item and we're a submodule of it, so can we. // Also this keeps the cached data smaller. - if !is_private || is_original_def { + if is_pub_or_explicit || declared { locations.push((module, name.clone())); } } } // Descend into all modules visible from `from`. - for (ty, vis) in data.scope.types() { - if let ModuleDefId::ModuleId(module) = ty { - if vis.is_visible_from(db, from) { - worklist.push(module); - } + for (module, vis) in data.scope.modules_in_scope() { + if vis.is_visible_from(db, from) { + worklist.push(module); } } } @@ -625,16 +615,14 @@ mod tests { .expect("path does not resolve to a type"); let found_path = find_path_inner( - &db, + FindPathCtx { prefer_no_std: false, db: &db, prefixed: prefix_kind, prefer_prelude }, ItemInNs::Types(resolved), module, - prefix_kind, - false, - prefer_prelude, ); assert_eq!(found_path, Some(mod_path), "on kind: {prefix_kind:?}"); } + #[track_caller] fn check_found_path( ra_fixture: &str, unprefixed: &str, @@ -1004,6 +992,7 @@ pub use crate::foo::bar::S; #[test] fn discount_private_imports() { + cov_mark::check!(discount_private_imports); check_found_path( r#" //- /main.rs @@ -1021,6 +1010,47 @@ $0 ); } + #[test] + fn explicit_private_imports_crate() { + cov_mark::check!(explicit_private_imports); + check_found_path( + r#" +//- /main.rs +mod foo; +pub mod bar { pub struct S; } +pub(crate) use bar::S; +//- /foo.rs +$0 + "#, + "crate::S", + "crate::S", + "crate::S", + "crate::S", + ); + } + + #[test] + fn explicit_private_imports() { + cov_mark::check!(explicit_private_imports); + check_found_path( + r#" +//- /main.rs +pub mod bar { + mod foo; + pub mod baz { pub struct S; } + pub(self) use baz::S; +} + +//- /bar/foo.rs +$0 + "#, + "super::S", + "super::S", + "crate::bar::S", + "super::S", + ); + } + #[test] fn import_cycle() { check_found_path( diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index f5324f052e592..6cb9b8448d14f 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -107,11 +107,11 @@ impl TypeOrConstParamData { impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData); /// Data about the generic parameters of a function, struct, impl, etc. -#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct GenericParams { pub type_or_consts: Arena, pub lifetimes: Arena, - pub where_predicates: Vec, + pub where_predicates: Box<[WherePredicate]>, } /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined @@ -142,109 +142,14 @@ pub enum WherePredicateTypeTarget { TypeOrConstParam(LocalTypeOrConstParamId), } -impl GenericParams { - /// Iterator of type_or_consts field - pub fn iter( - &self, - ) -> impl DoubleEndedIterator, &TypeOrConstParamData)> { - self.type_or_consts.iter() - } - - pub(crate) fn generic_params_query( - db: &dyn DefDatabase, - def: GenericDefId, - ) -> Interned { - let _p = profile::span("generic_params_query"); - - let krate = def.module(db).krate; - let cfg_options = db.crate_graph(); - let cfg_options = &cfg_options[krate].cfg_options; - - // Returns the generic parameters that are enabled under the current `#[cfg]` options - let enabled_params = |params: &Interned, item_tree: &ItemTree| { - let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); - - // In the common case, no parameters will by disabled by `#[cfg]` attributes. - // Therefore, make a first pass to check if all parameters are enabled and, if so, - // clone the `Interned` instead of recreating an identical copy. - let all_type_or_consts_enabled = - params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); - let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); - - if all_type_or_consts_enabled && all_lifetimes_enabled { - params.clone() - } else { - Interned::new(GenericParams { - type_or_consts: all_type_or_consts_enabled - .then(|| params.type_or_consts.clone()) - .unwrap_or_else(|| { - params - .type_or_consts - .iter() - .filter_map(|(idx, param)| { - enabled(idx.into()).then(|| param.clone()) - }) - .collect() - }), - lifetimes: all_lifetimes_enabled - .then(|| params.lifetimes.clone()) - .unwrap_or_else(|| { - params - .lifetimes - .iter() - .filter_map(|(idx, param)| { - enabled(idx.into()).then(|| param.clone()) - }) - .collect() - }), - where_predicates: params.where_predicates.clone(), - }) - } - }; - macro_rules! id_to_generics { - ($id:ident) => {{ - let id = $id.lookup(db).id; - let tree = id.item_tree(db); - let item = &tree[id.value]; - enabled_params(&item.generic_params, &tree) - }}; - } - - match def { - GenericDefId::FunctionId(id) => { - let loc = id.lookup(db); - let tree = loc.id.item_tree(db); - let item = &tree[loc.id.value]; - - let enabled_params = enabled_params(&item.explicit_generic_params, &tree); - let mut generic_params = GenericParams::clone(&enabled_params); - - let module = loc.container.module(db); - let func_data = db.function_data(id); - - // Don't create an `Expander` if not needed since this - // could cause a reparse after the `ItemTree` has been created due to the spanmap. - let mut expander = - Lazy::new(|| (module.def_map(db), Expander::new(db, loc.id.file_id(), module))); - for param in func_data.params.iter() { - generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); - } - - Interned::new(generic_params) - } - GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics!(id), - GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id), - GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id), - GenericDefId::TraitId(id) => id_to_generics!(id), - GenericDefId::TraitAliasId(id) => id_to_generics!(id), - GenericDefId::TypeAliasId(id) => id_to_generics!(id), - GenericDefId::ImplId(id) => id_to_generics!(id), - GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => { - Interned::new(GenericParams::default()) - } - } - } +#[derive(Clone, Default)] +pub(crate) struct GenericParamsCollector { + pub(crate) type_or_consts: Arena, + lifetimes: Arena, + where_predicates: Vec, +} +impl GenericParamsCollector { pub(crate) fn fill( &mut self, lower_ctx: &LowerCtx<'_>, @@ -444,11 +349,131 @@ impl GenericParams { }); } - pub(crate) fn shrink_to_fit(&mut self) { - let Self { lifetimes, type_or_consts: types, where_predicates } = self; + pub(crate) fn finish(self) -> GenericParams { + let Self { mut lifetimes, mut type_or_consts, where_predicates } = self; lifetimes.shrink_to_fit(); - types.shrink_to_fit(); - where_predicates.shrink_to_fit(); + type_or_consts.shrink_to_fit(); + GenericParams { + type_or_consts, + lifetimes, + where_predicates: where_predicates.into_boxed_slice(), + } + } +} + +impl GenericParams { + /// Iterator of type_or_consts field + pub fn iter( + &self, + ) -> impl DoubleEndedIterator, &TypeOrConstParamData)> { + self.type_or_consts.iter() + } + + pub(crate) fn generic_params_query( + db: &dyn DefDatabase, + def: GenericDefId, + ) -> Interned { + let _p = profile::span("generic_params_query"); + + let krate = def.module(db).krate; + let cfg_options = db.crate_graph(); + let cfg_options = &cfg_options[krate].cfg_options; + + // Returns the generic parameters that are enabled under the current `#[cfg]` options + let enabled_params = |params: &Interned, item_tree: &ItemTree| { + let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); + + // In the common case, no parameters will by disabled by `#[cfg]` attributes. + // Therefore, make a first pass to check if all parameters are enabled and, if so, + // clone the `Interned` instead of recreating an identical copy. + let all_type_or_consts_enabled = + params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); + let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); + + if all_type_or_consts_enabled && all_lifetimes_enabled { + params.clone() + } else { + Interned::new(GenericParams { + type_or_consts: all_type_or_consts_enabled + .then(|| params.type_or_consts.clone()) + .unwrap_or_else(|| { + params + .type_or_consts + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + lifetimes: all_lifetimes_enabled + .then(|| params.lifetimes.clone()) + .unwrap_or_else(|| { + params + .lifetimes + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + where_predicates: params.where_predicates.clone(), + }) + } + }; + macro_rules! id_to_generics { + ($id:ident) => {{ + let id = $id.lookup(db).id; + let tree = id.item_tree(db); + let item = &tree[id.value]; + enabled_params(&item.generic_params, &tree) + }}; + } + + match def { + GenericDefId::FunctionId(id) => { + let loc = id.lookup(db); + let tree = loc.id.item_tree(db); + let item = &tree[loc.id.value]; + + let enabled_params = enabled_params(&item.explicit_generic_params, &tree); + + let module = loc.container.module(db); + let func_data = db.function_data(id); + if func_data.params.is_empty() { + enabled_params + } else { + let mut generic_params = GenericParamsCollector { + type_or_consts: enabled_params.type_or_consts.clone(), + lifetimes: enabled_params.lifetimes.clone(), + where_predicates: enabled_params.where_predicates.clone().into(), + }; + + // Don't create an `Expander` if not needed since this + // could cause a reparse after the `ItemTree` has been created due to the spanmap. + let mut expander = Lazy::new(|| { + (module.def_map(db), Expander::new(db, loc.id.file_id(), module)) + }); + for param in func_data.params.iter() { + generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); + } + Interned::new(generic_params.finish()) + } + } + GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics!(id), + GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id), + GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id), + GenericDefId::TraitId(id) => id_to_generics!(id), + GenericDefId::TraitAliasId(id) => id_to_generics!(id), + GenericDefId::TypeAliasId(id) => id_to_generics!(id), + GenericDefId::ImplId(id) => id_to_generics!(id), + GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => { + Interned::new(GenericParams { + type_or_consts: Default::default(), + lifetimes: Default::default(), + where_predicates: Default::default(), + }) + } + } } pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option { diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs index 7fc33abc7c9a1..c0d1738b5048e 100644 --- a/crates/hir-def/src/hir/format_args.rs +++ b/crates/hir-def/src/hir/format_args.rs @@ -2,7 +2,7 @@ use std::mem; use hir_expand::name::Name; -use rustc_dependencies::parse_format as parse; +use rustc_parse_format as parse; use stdx::TupleExt; use syntax::{ ast::{self, IsString}, diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 75adf21abdcb2..935a8ebad165b 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -116,8 +116,7 @@ pub enum TypeRef { Path(Path), RawPtr(Box, Mutability), Reference(Box, Option, Mutability), - // FIXME: for full const generics, the latter element (length) here is going to have to be an - // expression that is further lowered later in hir_ty. + // FIXME: This should be Array(Box, Ast), Array(Box, ConstRef), Slice(Box), /// A fn pointer. Last element of the vector is the return type. diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 4902f24e2e3ab..168ee4acffbef 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -15,9 +15,11 @@ use stdx::format_to; use syntax::ast; use crate::{ - db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, - ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, - TraitId, UseId, + db::DefDatabase, + per_ns::PerNs, + visibility::{Visibility, VisibilityExplicity}, + AdtId, BuiltinType, ConstId, ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, UseId, }; #[derive(Debug, Default)] @@ -105,7 +107,7 @@ pub struct ItemScope { /// The attribute macro invocations in this scope. attr_macros: FxHashMap, MacroCallId>, /// The macro invocations in this scope. - pub macro_invocations: FxHashMap, MacroCallId>, + macro_invocations: FxHashMap, MacroCallId>, /// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes /// paired with the derive macro invocations for the specific attribute. derive_macros: FxHashMap, SmallVec<[DeriveMacroInvocation; 1]>>, @@ -145,8 +147,8 @@ impl ItemScope { .chain(self.values.keys()) .chain(self.macros.keys()) .chain(self.unresolved.iter()) - .unique() .sorted() + .dedup() .map(move |name| (name, self.get(name))) } @@ -157,8 +159,8 @@ impl ItemScope { .filter_map(ImportOrExternCrate::into_import) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) - .unique() .sorted() + .dedup() } pub fn fully_resolve_import(&self, db: &dyn DefDatabase, mut import: ImportId) -> PerNs { @@ -234,20 +236,37 @@ impl ItemScope { self.impls.iter().copied() } - pub fn values( - &self, - ) -> impl Iterator + ExactSizeIterator + '_ { - self.values.values().copied().map(|(a, b, _)| (a, b)) + pub(crate) fn modules_in_scope(&self) -> impl Iterator + '_ { + self.types.values().copied().filter_map(|(def, vis, _)| match def { + ModuleDefId::ModuleId(module) => Some((module, vis)), + _ => None, + }) } - pub(crate) fn types( - &self, - ) -> impl Iterator + ExactSizeIterator + '_ { - self.types.values().copied().map(|(def, vis, _)| (def, vis)) - } + pub fn unnamed_consts<'a>( + &'a self, + db: &'a dyn DefDatabase, + ) -> impl Iterator + 'a { + // FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those. + // Should be removed once synstructure stops doing that. + let synstructure_hack_consts = self.values.values().filter_map(|(item, _, _)| match item { + &ModuleDefId::ConstId(id) => { + let loc = id.lookup(db); + let item_tree = loc.id.item_tree(db); + if item_tree[loc.id.value] + .name + .as_ref() + .map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_")) + { + Some(id) + } else { + None + } + } + _ => None, + }); - pub fn unnamed_consts(&self) -> impl Iterator + '_ { - self.unnamed_consts.iter().copied() + self.unnamed_consts.iter().copied().chain(synstructure_hack_consts) } /// Iterate over all module scoped macros @@ -274,21 +293,18 @@ impl ItemScope { } /// XXX: this is O(N) rather than O(1), try to not introduce new usages. - pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { + pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility, /*declared*/ bool)> { match item { - ItemInNs::Macros(def) => self - .macros - .iter() - .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), - ItemInNs::Types(def) => self - .types - .iter() - .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), - - ItemInNs::Values(def) => self - .values - .iter() - .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + ItemInNs::Macros(def) => self.macros.iter().find_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }), + ItemInNs::Types(def) => self.types.iter().find_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }), + + ItemInNs::Values(def) => self.values.iter().find_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }), } } @@ -316,6 +332,10 @@ impl ItemScope { }), ) } + + pub(crate) fn macro_invoc(&self, call: AstId) -> Option { + self.macro_invocations.get(&call).copied() + } } impl ItemScope { @@ -624,18 +644,17 @@ impl ItemScope { pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { self.types .values_mut() - .map(|(def, vis, _)| (def, vis)) - .chain(self.values.values_mut().map(|(def, vis, _)| (def, vis))) - .map(|(_, v)| v) + .map(|(_, vis, _)| vis) + .chain(self.values.values_mut().map(|(_, vis, _)| vis)) .chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis)) - .for_each(|vis| *vis = Visibility::Module(this_module)); + .for_each(|vis| *vis = Visibility::Module(this_module, VisibilityExplicity::Implicit)); for (mac, vis, import) in self.macros.values_mut() { if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) { continue; } - *vis = Visibility::Module(this_module); + *vis = Visibility::Module(this_module, VisibilityExplicity::Implicit); } } diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 20e4e44339e9a..82ea5ffeba17c 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -69,7 +69,7 @@ use crate::{ generics::{GenericParams, LifetimeParamData, TypeOrConstParamData}, path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, - visibility::RawVisibility, + visibility::{RawVisibility, VisibilityExplicity}, BlockId, Lookup, }; @@ -78,8 +78,9 @@ pub struct RawVisibilityId(u32); impl RawVisibilityId { pub const PUB: Self = RawVisibilityId(u32::max_value()); - pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1); - pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2); + pub const PRIV_IMPLICIT: Self = RawVisibilityId(u32::max_value() - 1); + pub const PRIV_EXPLICIT: Self = RawVisibilityId(u32::max_value() - 2); + pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 3); } impl fmt::Debug for RawVisibilityId { @@ -87,7 +88,7 @@ impl fmt::Debug for RawVisibilityId { let mut f = f.debug_tuple("RawVisibilityId"); match *self { Self::PUB => f.field(&"pub"), - Self::PRIV => f.field(&"pub(self)"), + Self::PRIV_IMPLICIT | Self::PRIV_EXPLICIT => f.field(&"pub(self)"), Self::PUB_CRATE => f.field(&"pub(crate)"), _ => f.field(&self.0), }; @@ -249,19 +250,30 @@ impl ItemVisibilities { fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId { match &vis { RawVisibility::Public => RawVisibilityId::PUB, - RawVisibility::Module(path) if path.segments().is_empty() => match &path.kind { - PathKind::Super(0) => RawVisibilityId::PRIV, - PathKind::Crate => RawVisibilityId::PUB_CRATE, - _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), - }, + RawVisibility::Module(path, explicitiy) if path.segments().is_empty() => { + match (&path.kind, explicitiy) { + (PathKind::Super(0), VisibilityExplicity::Explicit) => { + RawVisibilityId::PRIV_EXPLICIT + } + (PathKind::Super(0), VisibilityExplicity::Implicit) => { + RawVisibilityId::PRIV_IMPLICIT + } + (PathKind::Crate, _) => RawVisibilityId::PUB_CRATE, + _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), + } + } _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), } } } static VIS_PUB: RawVisibility = RawVisibility::Public; -static VIS_PRIV: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))); -static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate)); +static VIS_PRIV_IMPLICIT: RawVisibility = + RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)), VisibilityExplicity::Implicit); +static VIS_PRIV_EXPLICIT: RawVisibility = + RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)), VisibilityExplicity::Explicit); +static VIS_PUB_CRATE: RawVisibility = + RawVisibility::Module(ModPath::from_kind(PathKind::Crate), VisibilityExplicity::Explicit); #[derive(Default, Debug, Eq, PartialEq)] struct ItemTreeData { @@ -540,7 +552,8 @@ impl Index for ItemTree { type Output = RawVisibility; fn index(&self, index: RawVisibilityId) -> &Self::Output { match index { - RawVisibilityId::PRIV => &VIS_PRIV, + RawVisibilityId::PRIV_IMPLICIT => &VIS_PRIV_IMPLICIT, + RawVisibilityId::PRIV_EXPLICIT => &VIS_PRIV_EXPLICIT, RawVisibilityId::PUB => &VIS_PUB, RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE, _ => &self.data().vis.arena[Idx::from_raw(index.0.into())], diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 8e2fafe81b50f..6343b43a016b2 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -6,7 +6,7 @@ use hir_expand::{ast_id_map::AstIdMap, span_map::SpanMapRef, HirFileId}; use syntax::ast::{self, HasModuleItem, HasTypeBounds}; use crate::{ - generics::{GenericParams, TypeParamData, TypeParamProvenance}, + generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance}, type_ref::{LifetimeRef, TraitBoundModifier, TraitRef}, LocalLifetimeParamId, LocalTypeOrConstParamId, }; @@ -386,17 +386,16 @@ impl<'a> Ctx<'a> { flags |= FnFlags::HAS_UNSAFE_KW; } - let mut res = Function { + let res = Function { name, visibility, - explicit_generic_params: Interned::new(GenericParams::default()), + explicit_generic_params: self.lower_generic_params(HasImplicitSelf::No, func), abi, params, ret_type: Interned::new(ret_type), ast_id, flags, }; - res.explicit_generic_params = self.lower_generic_params(HasImplicitSelf::No, func); Some(id(self.data().functions.alloc(res))) } @@ -604,7 +603,7 @@ impl<'a> Ctx<'a> { has_implicit_self: HasImplicitSelf, node: &dyn ast::HasGenericParams, ) -> Interned { - let mut generics = GenericParams::default(); + let mut generics = GenericParamsCollector::default(); if let HasImplicitSelf::Yes(bounds) = has_implicit_self { // Traits and trait aliases get the Self type as an implicit first type parameter. @@ -642,8 +641,7 @@ impl<'a> Ctx<'a> { }; generics.fill(&self.body_ctx, node, add_param_attrs); - generics.shrink_to_fit(); - Interned::new(generics) + Interned::new(generics.finish()) } fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Box<[Interned]> { diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 6d92fce07272f..8693b9a98c9d9 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -104,7 +104,9 @@ impl Printer<'_> { fn print_visibility(&mut self, vis: RawVisibilityId) { match &self.tree[vis] { - RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())), + RawVisibility::Module(path, _expl) => { + w!(self, "pub({}) ", path.display(self.db.upcast())) + } RawVisibility::Public => w!(self, "pub "), }; } diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 1ae6bd4c91949..66e0d2cc346bc 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -87,7 +87,10 @@ impl LangItems { } /// Salsa query. This will look for lang items in a specific crate. - pub(crate) fn crate_lang_items_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { + pub(crate) fn crate_lang_items_query( + db: &dyn DefDatabase, + krate: CrateId, + ) -> Option> { let _p = profile::span("crate_lang_items_query"); let mut lang_items = LangItems::default(); @@ -150,7 +153,11 @@ impl LangItems { } } - Arc::new(lang_items) + if lang_items.items.is_empty() { + None + } else { + Some(Arc::new(lang_items)) + } } /// Salsa query. Look for a lang item, starting from the specified crate and recursively @@ -161,9 +168,9 @@ impl LangItems { item: LangItem, ) -> Option { let _p = profile::span("lang_item_query"); - let lang_items = db.crate_lang_items(start_crate); - let start_crate_target = lang_items.items.get(&item); - if let Some(&target) = start_crate_target { + if let Some(target) = + db.crate_lang_items(start_crate).and_then(|it| it.items.get(&item).copied()) + { return Some(target); } db.crate_graph()[start_crate] diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 250d7b677b5dd..aa84ccaee6eee 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -10,10 +10,17 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#[allow(unused)] -macro_rules! eprintln { - ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; -} +#[cfg(feature = "in-rust-tree")] +extern crate rustc_parse_format; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_parse_format as rustc_parse_format; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_abi; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_abi as rustc_abi; pub mod db; @@ -49,7 +56,7 @@ pub mod visibility; pub mod find_path; pub mod import_map; -pub use rustc_dependencies::abi as layout; +pub use rustc_abi as layout; use triomphe::Arc; #[cfg(test)] diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index abd84c6a46de5..553c0b795336d 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -16,13 +16,12 @@ struct Foo; #[derive(Copy)] struct Foo; -impl < > core::marker::Copy for Foo< > where {}"#]], +impl < > $crate::marker::Copy for Foo< > where {}"#]], ); } #[test] fn test_copy_expand_in_core() { - cov_mark::check!(test_copy_expand_in_core); check( r#" //- /lib.rs crate:core @@ -41,7 +40,7 @@ macro Copy {} #[derive(Copy)] struct Foo; -impl < > crate ::marker::Copy for Foo< > where {}"#]], +impl < > $crate::marker::Copy for Foo< > where {}"#]], ); } @@ -57,7 +56,7 @@ struct Foo; #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo where {}"#]], +impl $crate::marker::Copy for Foo where {}"#]], ); } @@ -74,7 +73,7 @@ struct Foo; #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo where {}"#]], +impl $crate::marker::Copy for Foo where {}"#]], ); } @@ -98,7 +97,7 @@ enum Command { Jump, } -impl core::clone::Clone for Command where { +impl $crate::clone::Clone for Command where { fn clone(&self ) -> Self { match self { Command::Move { @@ -158,7 +157,7 @@ where generic: Vec, } -impl core::clone::Clone for Foo where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, { +impl $crate::clone::Clone for Foo where T: Trait, T::InFieldShorthand: $crate::clone::Clone, T::InGenericArg: $crate::clone::Clone, { fn clone(&self ) -> Self { match self { Foo { @@ -186,7 +185,7 @@ struct Foo(u32); #[derive(Clone)] struct Foo(u32); -impl core::clone::Clone for Foo where { +impl $crate::clone::Clone for Foo where { fn clone(&self ) -> Self { match self { Foo(f0, )=>Foo(f0.clone(), ), @@ -226,14 +225,14 @@ enum Bar { Bar, } -impl < > core::default::Default for Foo< > where { +impl < > $crate::default::Default for Foo< > where { fn default() -> Self { Foo { - field1: core::default::Default::default(), field2: core::default::Default::default(), + field1: $crate::default::Default::default(), field2: $crate::default::Default::default(), } } } -impl < > core::default::Default for Bar< > where { +impl < > $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } @@ -261,7 +260,7 @@ enum Command { Jump, } -impl < > core::cmp::PartialEq for Command< > where { +impl < > $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -274,7 +273,7 @@ impl < > core::cmp::PartialEq for Command< > where { } } } -impl < > core::cmp::Eq for Command< > where {}"#]], +impl < > $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -299,7 +298,7 @@ enum Command { Jump, } -impl < > core::cmp::PartialEq for Command< > where { +impl < > $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -312,7 +311,7 @@ impl < > core::cmp::PartialEq for Command< > where { } } } -impl < > core::cmp::Eq for Command< > where {}"#]], +impl < > $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -336,10 +335,10 @@ enum Command { Jump, } -impl < > core::cmp::PartialOrd for Command< > where { - fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option { - match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { +impl < > $crate::cmp::PartialOrd for Command< > where { + fn partial_cmp(&self , other: &Self ) -> $crate::option::Option::Option<$crate::cmp::Ordering> { + match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) { + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { match (self , other) { (Command::Move { x: x_self, y: y_self, @@ -348,10 +347,10 @@ impl < > core::cmp::PartialOrd for Command< > where { x: x_other, y: y_other, } )=>match x_self.partial_cmp(&x_other) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { match y_self.partial_cmp(&y_other) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { - core::option::Option::Some(core::cmp::Ordering::Equal) + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { + $crate::option::Option::Some($crate::cmp::Ordering::Equal) } c=>return c, } @@ -359,22 +358,22 @@ impl < > core::cmp::PartialOrd for Command< > where { c=>return c, } , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) { - core::option::Option::Some(core::cmp::Ordering::Equal)=> { - core::option::Option::Some(core::cmp::Ordering::Equal) + $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { + $crate::option::Option::Some($crate::cmp::Ordering::Equal) } c=>return c, } - , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal) + , (Command::Jump, Command::Jump)=>$crate::option::Option::Some($crate::cmp::Ordering::Equal), _unused=>$crate::option::Option::Some($crate::cmp::Ordering::Equal) } } c=>return c, } } } -impl < > core::cmp::Ord for Command< > where { - fn cmp(&self , other: &Self ) -> core::cmp::Ordering { - match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) { - core::cmp::Ordering::Equal=> { +impl < > $crate::cmp::Ord for Command< > where { + fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering { + match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) { + $crate::cmp::Ordering::Equal=> { match (self , other) { (Command::Move { x: x_self, y: y_self, @@ -383,10 +382,10 @@ impl < > core::cmp::Ord for Command< > where { x: x_other, y: y_other, } )=>match x_self.cmp(&x_other) { - core::cmp::Ordering::Equal=> { + $crate::cmp::Ordering::Equal=> { match y_self.cmp(&y_other) { - core::cmp::Ordering::Equal=> { - core::cmp::Ordering::Equal + $crate::cmp::Ordering::Equal=> { + $crate::cmp::Ordering::Equal } c=>return c, } @@ -394,12 +393,12 @@ impl < > core::cmp::Ord for Command< > where { c=>return c, } , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) { - core::cmp::Ordering::Equal=> { - core::cmp::Ordering::Equal + $crate::cmp::Ordering::Equal=> { + $crate::cmp::Ordering::Equal } c=>return c, } - , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal + , (Command::Jump, Command::Jump)=>$crate::cmp::Ordering::Equal, _unused=>$crate::cmp::Ordering::Equal } } c=>return c, @@ -433,8 +432,8 @@ struct Foo { z: (i32, u64), } -impl < > core::hash::Hash for Foo< > where { - fn hash(&self , ra_expand_state: &mut H) { +impl < > $crate::hash::Hash for Foo< > where { + fn hash(&self , ra_expand_state: &mut H) { match self { Foo { x: x, y: y, z: z, @@ -471,9 +470,9 @@ enum Command { Jump, } -impl < > core::hash::Hash for Command< > where { - fn hash(&self , ra_expand_state: &mut H) { - core::mem::discriminant(self ).hash(ra_expand_state); +impl < > $crate::hash::Hash for Command< > where { + fn hash(&self , ra_expand_state: &mut H) { + $crate::mem::discriminant(self ).hash(ra_expand_state); match self { Command::Move { x: x, y: y, @@ -517,8 +516,8 @@ enum Command { Jump, } -impl < > core::fmt::Debug for Command< > where { - fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result { +impl < > $crate::fmt::Debug for Command< > where { + fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { Command::Move { x: x, y: y, diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index d4798f4507d12..4690ca5d363cd 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -136,7 +136,7 @@ fn main() { option_env!("TEST_ENV_VAR"); } #[rustc_builtin_macro] macro_rules! option_env {() => {}} -fn main() { ::core::option::Option::None:: < &str>; } +fn main() { $crate::option::Option::None:: < &str>; } "#]], ); } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 52a981fd19ebc..53644f58efc44 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -79,7 +79,7 @@ use crate::{ nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, path::ModPath, per_ns::PerNs, - visibility::Visibility, + visibility::{Visibility, VisibilityExplicity}, AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; @@ -332,7 +332,10 @@ impl DefMap { // NB: we use `None` as block here, which would be wrong for implicit // modules declared by blocks with items. At the moment, we don't use // this visibility for anything outside IDE, so that's probably OK. - let visibility = Visibility::Module(ModuleId { krate, local_id, block: None }); + let visibility = Visibility::Module( + ModuleId { krate, local_id, block: None }, + VisibilityExplicity::Implicit, + ); let module_data = ModuleData::new( ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id }, visibility, diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index be3438e427dba..389dabdbc8678 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -87,7 +87,7 @@ impl DefMap { within_impl: bool, ) -> Option { let mut vis = match visibility { - RawVisibility::Module(path) => { + RawVisibility::Module(path, explicity) => { let (result, remaining) = self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None); if remaining.is_some() { @@ -95,7 +95,7 @@ impl DefMap { } let types = result.take_types()?; match types { - ModuleDefId::ModuleId(m) => Visibility::Module(m), + ModuleDefId::ModuleId(m) => Visibility::Module(m, *explicity), // error: visibility needs to refer to module _ => { return None; @@ -108,11 +108,11 @@ impl DefMap { // In block expressions, `self` normally refers to the containing non-block module, and // `super` to its parent (etc.). However, visibilities must only refer to a module in the // DefMap they're written in, so we restrict them when that happens. - if let Visibility::Module(m) = vis { + if let Visibility::Module(m, mv) = vis { // ...unless we're resolving visibility for an associated item in an impl. if self.block_id() != m.block && !within_impl { cov_mark::hit!(adjust_vis_in_block_def_map); - vis = Visibility::Module(self.module_id(Self::ROOT)); + vis = Visibility::Module(self.module_id(Self::ROOT), mv); tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); } } diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index 48fe43450a710..0f6e64016f18a 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1264,6 +1264,54 @@ struct A; ); } +#[test] +fn nested_include() { + check( + r#" +//- minicore: include +//- /lib.rs +include!("out_dir/includes.rs"); + +//- /out_dir/includes.rs +pub mod company_name { + pub mod network { + pub mod v1 { + include!("company_name.network.v1.rs"); + } + } +} +//- /out_dir/company_name.network.v1.rs +pub struct IpAddress { + pub ip_type: &'static str, +} +/// Nested message and enum types in `IpAddress`. +pub mod ip_address { + pub enum IpType { + IpV4(u32), + } +} + +"#, + expect![[r#" + crate + company_name: t + + crate::company_name + network: t + + crate::company_name::network + v1: t + + crate::company_name::network::v1 + IpAddress: t + ip_address: t + + crate::company_name::network::v1::ip_address + IpType: t + "#]], + ); +} + #[test] fn macro_use_imports_all_macro_types() { let db = TestDB::with_files( diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 301391516d64e..1d850f721c1f8 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -242,7 +242,7 @@ impl Resolver { let within_impl = self.scopes().find(|scope| matches!(scope, Scope::ImplDefScope(_))).is_some(); match visibility { - RawVisibility::Module(_) => { + RawVisibility::Module(_, _) => { let (item_map, module) = self.item_scope(); item_map.resolve_visibility(db, module, visibility, within_impl) } diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs index f4a6b61f7af5e..c992c3c920486 100644 --- a/crates/hir-def/src/test_db.rs +++ b/crates/hir-def/src/test_db.rs @@ -8,7 +8,6 @@ use base_db::{ Upcast, }; use hir_expand::{db::ExpandDatabase, InFile}; -use rustc_hash::FxHashSet; use syntax::{algo, ast, AstNode}; use triomphe::Arc; @@ -76,7 +75,7 @@ impl FileLoader for TestDB { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { FileLoaderDelegate(self).relevant_crates(file_id) } } diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index 49688c5ee9c27..cd8023f5d7d7d 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -20,14 +20,14 @@ use crate::{ pub enum RawVisibility { /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is /// equivalent to `pub(self)`. - Module(ModPath), + Module(ModPath, VisibilityExplicity), /// `pub`. Public, } impl RawVisibility { pub(crate) const fn private() -> RawVisibility { - RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))) + RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)), VisibilityExplicity::Implicit) } pub(crate) fn from_ast( @@ -41,18 +41,9 @@ impl RawVisibility { db: &dyn DefDatabase, node: Option, span_map: SpanMapRef<'_>, - ) -> RawVisibility { - Self::from_ast_with_span_map_and_default(db, node, RawVisibility::private(), span_map) - } - - pub(crate) fn from_ast_with_span_map_and_default( - db: &dyn DefDatabase, - node: Option, - default: RawVisibility, - span_map: SpanMapRef<'_>, ) -> RawVisibility { let node = match node { - None => return default, + None => return RawVisibility::private(), Some(node) => node, }; match node.kind() { @@ -62,19 +53,19 @@ impl RawVisibility { None => return RawVisibility::private(), Some(path) => path, }; - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::PubCrate => { let path = ModPath::from_kind(PathKind::Crate); - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::PubSuper => { let path = ModPath::from_kind(PathKind::Super(1)); - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::PubSelf => { let path = ModPath::from_kind(PathKind::Super(0)); - RawVisibility::Module(path) + RawVisibility::Module(path, VisibilityExplicity::Explicit) } ast::VisibilityKind::Pub => RawVisibility::Public, } @@ -94,7 +85,7 @@ impl RawVisibility { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Visibility { /// Visibility is restricted to a certain module. - Module(ModuleId), + Module(ModuleId, VisibilityExplicity), /// Visibility is unrestricted. Public, } @@ -102,7 +93,7 @@ pub enum Visibility { impl Visibility { pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool { let to_module = match self { - Visibility::Module(m) => m, + Visibility::Module(m, _) => m, Visibility::Public => return true, }; // if they're not in the same crate, it can't be visible @@ -124,7 +115,7 @@ impl Visibility { mut from_module: LocalModuleId, ) -> bool { let mut to_module = match self { - Visibility::Module(m) => m, + Visibility::Module(m, _) => m, Visibility::Public => return true, }; @@ -181,9 +172,9 @@ impl Visibility { /// visible in unrelated modules). pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option { match (self, other) { - (Visibility::Module(_) | Visibility::Public, Visibility::Public) - | (Visibility::Public, Visibility::Module(_)) => Some(Visibility::Public), - (Visibility::Module(mod_a), Visibility::Module(mod_b)) => { + (Visibility::Module(_, _) | Visibility::Public, Visibility::Public) + | (Visibility::Public, Visibility::Module(_, _)) => Some(Visibility::Public), + (Visibility::Module(mod_a, vis_a), Visibility::Module(mod_b, vis_b)) => { if mod_a.krate != mod_b.krate { return None; } @@ -199,12 +190,12 @@ impl Visibility { if a_ancestors.any(|m| m == mod_b.local_id) { // B is above A - return Some(Visibility::Module(mod_b)); + return Some(Visibility::Module(mod_b, vis_b)); } if b_ancestors.any(|m| m == mod_a.local_id) { // A is above B - return Some(Visibility::Module(mod_a)); + return Some(Visibility::Module(mod_a, vis_a)); } None @@ -213,6 +204,19 @@ impl Visibility { } } +/// Whether the item was imported through `pub(crate) use` or just `use`. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum VisibilityExplicity { + Explicit, + Implicit, +} + +impl VisibilityExplicity { + pub fn is_explicit(&self) -> bool { + matches!(self, Self::Explicit) + } +} + /// Resolve visibility of all specific fields of a struct or union variant. pub(crate) fn field_visibilities_query( db: &dyn DefDatabase, diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 8f240ef07320a..46bbb7f92c079 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,6 +1,5 @@ //! Builtin derives. -use base_db::{CrateOrigin, LangCrateOrigin}; use itertools::izip; use rustc_hash::FxHashSet; use span::{MacroCallId, Span}; @@ -10,6 +9,7 @@ use tracing::debug; use crate::{ hygiene::span_with_def_site_ctxt, name::{AsName, Name}, + quote::dollar_crate, span_map::SpanMapRef, tt, }; @@ -38,7 +38,7 @@ macro_rules! register_builtin { let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); - expander(db, id, span, tt, token_map) + expander(span, tt, token_map) } fn find_by_name(name: &name::Name) -> Option { @@ -398,41 +398,13 @@ fn expand_simple_derive( ExpandResult::ok(expanded) } -fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: Span) -> tt::TokenTree { - // FIXME: make hygiene works for builtin derive macro - // such that $crate can be used here. - let cg = db.crate_graph(); - let krate = db.lookup_intern_macro_call(id).krate; - - let tt = if matches!(cg[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core)) { - cov_mark::hit!(test_copy_expand_in_core); - quote! {span => crate } - } else { - quote! {span => core } - }; - - tt.token_trees[0].clone() -} - -fn copy_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = find_builtin_crate(db, id, span); +fn copy_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) } -fn clone_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = find_builtin_crate(db, id, span); +fn clone_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::clone::Clone }, |adt| { if matches!(adt.shape, AdtShape::Union) { let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; @@ -482,14 +454,8 @@ fn and_and(span: Span) -> tt::Subtree { quote! {span => #and& } } -fn default_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = &find_builtin_crate(db, id, span); +fn default_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::default::Default }, |adt| { let body = match &adt.shape { AdtShape::Struct(fields) => { @@ -527,14 +493,8 @@ fn default_expand( }) } -fn debug_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = &find_builtin_crate(db, id, span); +fn debug_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { @@ -605,14 +565,8 @@ fn debug_expand( }) } -fn hash_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = &find_builtin_crate(db, id, span); +fn hash_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::hash::Hash }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here @@ -658,25 +612,13 @@ fn hash_expand( }) } -fn eq_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = find_builtin_crate(db, id, span); +fn eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) } -fn partial_eq_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = find_builtin_crate(db, id, span); +fn partial_eq_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialEq }, |adt| { if matches!(adt.shape, AdtShape::Union) { // FIXME: Return expand error here @@ -747,17 +689,11 @@ fn self_and_other_patterns( (self_patterns, other_patterns) } -fn ord_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = &find_builtin_crate(db, id, span); +fn ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::Ord }, |adt| { fn compare( - krate: &tt::TokenTree, + krate: &tt::Ident, left: tt::Subtree, right: tt::Subtree, rest: tt::Subtree, @@ -811,17 +747,11 @@ fn ord_expand( }) } -fn partial_ord_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, - span: Span, - tt: &ast::Adt, - tm: SpanMapRef<'_>, -) -> ExpandResult { - let krate = &find_builtin_crate(db, id, span); +fn partial_ord_expand(span: Span, tt: &ast::Adt, tm: SpanMapRef<'_>) -> ExpandResult { + let krate = &dollar_crate(span); expand_simple_derive(span, tt, tm, quote! {span => #krate::cmp::PartialOrd }, |adt| { fn compare( - krate: &tt::TokenTree, + krate: &tt::Ident, left: tt::Subtree, right: tt::Subtree, rest: tt::Subtree, diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index f99a891762332..c892f462d2c72 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -6,18 +6,16 @@ use either::Either; use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; -use syntax::{ - ast::{self, AstToken}, - SmolStr, -}; +use syntax::ast::{self, AstToken}; use crate::{ db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, name::{self, known}, quote, + quote::dollar_crate, tt::{self, DelimSpan}, - ExpandError, ExpandResult, HirFileIdExt, MacroCallId, + ExpandError, ExpandResult, HirFileIdExt, MacroCallId, MacroFileIdExt, }; macro_rules! register_builtin { @@ -205,7 +203,7 @@ fn assert_expand( ) -> ExpandResult { let call_site_span = span_with_call_site_ctxt(db, span, id); let args = parse_exprs_with_sep(tt, ',', call_site_span); - let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_crate = dollar_crate(span); let expanded = match &*args { [cond, panic_args @ ..] => { let comma = tt::Subtree { @@ -300,7 +298,7 @@ fn asm_expand( [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))] | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] => { - let dollar_krate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_krate = dollar_crate(span); literals.push(quote!(span=>#dollar_krate::format_args!(#lit);)); } _ => break, @@ -345,7 +343,7 @@ fn panic_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); let mac = @@ -371,7 +369,7 @@ fn unreachable_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span }; + let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); let mac = if use_panic_2021(db, call_site_span) { @@ -611,7 +609,7 @@ fn relative_file( path_str: &str, allow_recursion: bool, ) -> Result { - let call_site = call_id.as_file().original_file(db); + let call_site = call_id.as_macro_file().parent(db).original_file_respecting_includes(db); let path = AnchoredPath { anchor: call_site, path: path_str }; let res = db .resolve_path(path) @@ -763,10 +761,10 @@ fn option_env_expand( return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) } }; - // FIXME: Use `DOLLAR_CRATE` when that works in eager macros. + let dollar_crate = dollar_crate(span); let expanded = match get_env_inner(db, arg_id, &key) { - None => quote! {span => ::core::option::Option::None::<&str> }, - Some(s) => quote! {span => ::core::option::Option::Some(#s) }, + None => quote! {span => #dollar_crate::option::Option::None::<&str> }, + Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) }, }; ExpandResult::ok(expanded) diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 6a122e0859cc5..ae7d17e49a904 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -318,6 +318,7 @@ pub trait MacroFileIdExt { fn expansion_level(self, db: &dyn ExpandDatabase) -> u32; /// If this is a macro call, returns the syntax node of the call. fn call_node(self, db: &dyn ExpandDatabase) -> InFile; + fn parent(self, db: &dyn ExpandDatabase) -> HirFileId; fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo; @@ -353,6 +354,9 @@ impl MacroFileIdExt for MacroFileId { }; } } + fn parent(self, db: &dyn ExpandDatabase) -> HirFileId { + self.macro_call_id.lookup(db).kind.file_id() + } /// Return expansion information if it is a macro-expansion file fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo { diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs index 9bdd75f9d2240..a3b84afd2ae4c 100644 --- a/crates/hir-expand/src/quote.rs +++ b/crates/hir-expand/src/quote.rs @@ -4,6 +4,10 @@ use span::Span; use crate::name::Name; +pub(crate) fn dollar_crate(span: Span) -> tt::Ident { + tt::Ident { text: syntax::SmolStr::new_inline("$crate"), span } +} + // A helper macro quote macro // FIXME: // 1. Not all puncts are handled diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index c7807bcf9aa1b..803c18677da53 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -34,7 +34,9 @@ nohash-hasher.workspace = true typed-arena = "2.0.1" indexmap.workspace = true -rustc-dependencies.workspace = true +ra-ap-rustc_abi.workspace = true +ra-ap-rustc_index.workspace = true + # local deps stdx.workspace = true @@ -58,7 +60,7 @@ test-utils.workspace = true test-fixture.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] workspace = true diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index f4fbace19e35c..e81d4ced5543e 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -167,7 +167,7 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { } }); }) - .map(|block_id| self.db.trait_impls_in_block(block_id)); + .filter_map(|block_id| self.db.trait_impls_in_block(block_id)); let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); let mut result = vec![]; @@ -183,7 +183,8 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { def_blocks .into_iter() .flatten() - .for_each(|it| f(&self.db.trait_impls_in_block(it))); + .filter_map(|it| self.db.trait_impls_in_block(it)) + .for_each(|it| f(&it)); } fps => { let mut f = @@ -198,7 +199,8 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { def_blocks .into_iter() .flatten() - .for_each(|it| f(&self.db.trait_impls_in_block(it))); + .filter_map(|it| self.db.trait_impls_in_block(it)) + .for_each(|it| f(&it)); } } diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 410bcbf0356f0..ad790fa094d82 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -34,6 +34,8 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::infer::infer_query)] fn infer_query(&self, def: DefWithBodyId) -> Arc; + // region:mir + #[salsa::invoke(crate::mir::mir_body_query)] #[salsa::cycle(crate::mir::mir_body_recover)] fn mir_body(&self, def: DefWithBodyId) -> Result, MirLowerError>; @@ -61,20 +63,6 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::mir::borrowck_query)] fn borrowck(&self, def: DefWithBodyId) -> Result, MirLowerError>; - #[salsa::invoke(crate::lower::ty_query)] - #[salsa::cycle(crate::lower::ty_recover)] - fn ty(&self, def: TyDefId) -> Binders; - - #[salsa::invoke(crate::lower::value_ty_query)] - fn value_ty(&self, def: ValueTyDefId) -> Binders; - - #[salsa::invoke(crate::lower::impl_self_ty_query)] - #[salsa::cycle(crate::lower::impl_self_ty_recover)] - fn impl_self_ty(&self, def: ImplId) -> Binders; - - #[salsa::invoke(crate::lower::const_param_ty_query)] - fn const_param_ty(&self, def: ConstParamId) -> Ty; - #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(crate::consteval::const_eval_recover)] fn const_eval( @@ -92,6 +80,22 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] fn const_eval_discriminant(&self, def: EnumVariantId) -> Result; + // endregion:mir + + #[salsa::invoke(crate::lower::ty_query)] + #[salsa::cycle(crate::lower::ty_recover)] + fn ty(&self, def: TyDefId) -> Binders; + + #[salsa::invoke(crate::lower::value_ty_query)] + fn value_ty(&self, def: ValueTyDefId) -> Binders; + + #[salsa::invoke(crate::lower::impl_self_ty_query)] + #[salsa::cycle(crate::lower::impl_self_ty_recover)] + fn impl_self_ty(&self, def: ImplId) -> Binders; + + #[salsa::invoke(crate::lower::const_param_ty_query)] + fn const_param_ty(&self, def: ConstParamId) -> Ty; + #[salsa::invoke(crate::lower::impl_trait_query)] fn impl_trait(&self, def: ImplId) -> Option>; @@ -158,7 +162,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc; #[salsa::invoke(InherentImpls::inherent_impls_in_block_query)] - fn inherent_impls_in_block(&self, block: BlockId) -> Arc; + fn inherent_impls_in_block(&self, block: BlockId) -> Option>; /// Collects all crates in the dependency graph that have impls for the /// given fingerprint. This is only used for primitive types and types @@ -175,7 +179,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn trait_impls_in_crate(&self, krate: CrateId) -> Arc; #[salsa::invoke(TraitImpls::trait_impls_in_block_query)] - fn trait_impls_in_block(&self, block: BlockId) -> Arc; + fn trait_impls_in_block(&self, block: BlockId) -> Option>; #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc]>; diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 23d951542214b..d63a64a70de2a 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1629,7 +1629,7 @@ pub fn write_visibility( ) -> Result<(), HirDisplayError> { match vis { Visibility::Public => write!(f, "pub "), - Visibility::Module(vis_id) => { + Visibility::Module(vis_id, _) => { let def_map = module_id.def_map(f.db.upcast()); let root_module_id = def_map.module_id(DefMap::ROOT); if vis_id == module_id { diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 68619bb8b18c4..b7bfaf2931b6b 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -12,10 +12,9 @@ use hir_def::{ LocalEnumVariantId, LocalFieldId, StructId, }; use la_arena::{Idx, RawIdx}; -use rustc_dependencies::{ - abi::AddressSpace, - index::{IndexSlice, IndexVec}, -}; +use rustc_abi::AddressSpace; +use rustc_index::{IndexSlice, IndexVec}; + use stdx::never; use triomphe::Arc; @@ -35,7 +34,7 @@ mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub LocalEnumVariantId); -impl rustc_dependencies::index::Idx for RustcEnumVariantIdx { +impl rustc_index::Idx for RustcEnumVariantIdx { fn new(idx: usize) -> Self { RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32))) } @@ -54,7 +53,7 @@ impl RustcFieldIdx { } } -impl rustc_dependencies::index::Idx for RustcFieldIdx { +impl rustc_index::Idx for RustcFieldIdx { fn new(idx: usize) -> Self { RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32))) } diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 39788a9502993..8a7715ce8729f 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -9,7 +9,7 @@ use hir_def::{ AdtId, EnumVariantId, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; -use rustc_dependencies::index::IndexVec; +use rustc_index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index ef0be7ab2dafe..57214193cfbcb 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -118,7 +118,7 @@ fn check_fail(ra_fixture: &str, e: LayoutError) { macro_rules! size_and_align { (minicore: $($x:tt),*;$($t:tt)*) => { { - #[allow(dead_code)] + #![allow(dead_code)] $($t)* check_size_and_align( stringify!($($t)*), @@ -130,7 +130,7 @@ macro_rules! size_and_align { }; ($($t:tt)*) => { { - #[allow(dead_code)] + #![allow(dead_code)] $($t)* check_size_and_align( stringify!($($t)*), diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 793b52b49faa1..e72864a12eec5 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -3,10 +3,17 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#[allow(unused)] -macro_rules! eprintln { - ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; -} +#[cfg(feature = "in-rust-tree")] +extern crate rustc_index; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_index as rustc_index; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_abi; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_abi as rustc_abi; mod builder; mod chalk_db; diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 97c4a741ff2a9..e371e42761560 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1601,7 +1601,7 @@ fn implicitly_sized_clauses<'a>( pub(crate) fn generic_defaults_query( db: &dyn HirDatabase, def: GenericDefId, -) -> Arc<[Binders>]> { +) -> Arc<[Binders]> { let resolver = def.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, def.into()) .with_type_param_mode(ParamLoweringMode::Variable); diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 33619edfee93e..03ed8d36a1dd6 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -8,10 +8,9 @@ use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ data::{adt::StructFlags, ImplData}, - item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, - ModuleDefId, ModuleId, TraitId, + ModuleId, TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; @@ -132,34 +131,40 @@ pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 2] = [ TyFingerprint::Scalar(Scalar::Float(FloatTy::F64)), ]; +type TraitFpMap = FxHashMap, Box<[ImplId]>>>; +type TraitFpMapCollector = FxHashMap, Vec>>; + /// Trait impls defined or available in some crate. #[derive(Debug, Eq, PartialEq)] pub struct TraitImpls { // If the `Option` is `None`, the impl may apply to any self type. - map: FxHashMap, Vec>>, + map: TraitFpMap, } impl TraitImpls { pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc { let _p = profile::span("trait_impls_in_crate_query").detail(|| format!("{krate:?}")); - let mut impls = Self { map: FxHashMap::default() }; + let mut impls = FxHashMap::default(); - let crate_def_map = db.crate_def_map(krate); - impls.collect_def_map(db, &crate_def_map); - impls.shrink_to_fit(); + Self::collect_def_map(db, &mut impls, &db.crate_def_map(krate)); - Arc::new(impls) + Arc::new(Self::finish(impls)) } - pub(crate) fn trait_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc { + pub(crate) fn trait_impls_in_block_query( + db: &dyn HirDatabase, + block: BlockId, + ) -> Option> { let _p = profile::span("trait_impls_in_block_query"); - let mut impls = Self { map: FxHashMap::default() }; + let mut impls = FxHashMap::default(); - let block_def_map = db.block_def_map(block); - impls.collect_def_map(db, &block_def_map); - impls.shrink_to_fit(); + Self::collect_def_map(db, &mut impls, &db.block_def_map(block)); - Arc::new(impls) + if impls.is_empty() { + None + } else { + Some(Arc::new(Self::finish(impls))) + } } pub(crate) fn trait_impls_in_deps_query( @@ -174,15 +179,16 @@ impl TraitImpls { ) } - fn shrink_to_fit(&mut self) { - self.map.shrink_to_fit(); - self.map.values_mut().for_each(|map| { - map.shrink_to_fit(); - map.values_mut().for_each(Vec::shrink_to_fit); - }); + fn finish(map: TraitFpMapCollector) -> TraitImpls { + TraitImpls { + map: map + .into_iter() + .map(|(k, v)| (k, v.into_iter().map(|(k, v)| (k, v.into_boxed_slice())).collect())) + .collect(), + } } - fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) { + fn collect_def_map(db: &dyn HirDatabase, map: &mut TraitFpMapCollector, def_map: &DefMap) { for (_module_id, module_data) in def_map.modules() { for impl_id in module_data.scope.impls() { // Reservation impls should be ignored during trait resolution, so we never need @@ -200,20 +206,15 @@ impl TraitImpls { }; let self_ty = db.impl_self_ty(impl_id); let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); - self.map - .entry(target_trait) - .or_default() - .entry(self_ty_fp) - .or_default() - .push(impl_id); + map.entry(target_trait).or_default().entry(self_ty_fp).or_default().push(impl_id); } // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in collect_unnamed_consts(db, &module_data.scope) { + for konst in module_data.scope.unnamed_consts(db.upcast()) { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { - self.collect_def_map(db, &block_def_map); + Self::collect_def_map(db, map, &block_def_map); } } } @@ -281,7 +282,10 @@ impl InherentImpls { Arc::new(impls) } - pub(crate) fn inherent_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc { + pub(crate) fn inherent_impls_in_block_query( + db: &dyn HirDatabase, + block: BlockId, + ) -> Option> { let _p = profile::span("inherent_impls_in_block_query"); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; @@ -289,7 +293,11 @@ impl InherentImpls { impls.collect_def_map(db, &block_def_map); impls.shrink_to_fit(); - Arc::new(impls) + if impls.map.is_empty() && impls.invalid_impls.is_empty() { + None + } else { + Some(Arc::new(impls)) + } } fn shrink_to_fit(&mut self) { @@ -321,7 +329,7 @@ impl InherentImpls { // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in collect_unnamed_consts(db, &module_data.scope) { + for konst in module_data.scope.unnamed_consts(db.upcast()) { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { self.collect_def_map(db, &block_def_map); @@ -367,34 +375,6 @@ pub(crate) fn incoherent_inherent_impl_crates( res } -fn collect_unnamed_consts<'a>( - db: &'a dyn HirDatabase, - scope: &'a ItemScope, -) -> impl Iterator + 'a { - let unnamed_consts = scope.unnamed_consts(); - - // FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those. - // Should be removed once synstructure stops doing that. - let synstructure_hack_consts = scope.values().filter_map(|(item, _)| match item { - ModuleDefId::ConstId(id) => { - let loc = id.lookup(db.upcast()); - let item_tree = loc.id.item_tree(db.upcast()); - if item_tree[loc.id.value] - .name - .as_ref() - .map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_")) - { - Some(id) - } else { - None - } - } - _ => None, - }); - - unnamed_consts.chain(synstructure_hack_consts) -} - pub fn def_crates( db: &dyn HirDatabase, ty: &Ty, @@ -737,7 +717,7 @@ fn lookup_impl_assoc_item_for_trait_ref( let impls = db.trait_impls_in_deps(env.krate); let self_impls = match self_ty.kind(Interner) { TyKind::Adt(id, _) => { - id.0.module(db.upcast()).containing_block().map(|it| db.trait_impls_in_block(it)) + id.0.module(db.upcast()).containing_block().and_then(|it| db.trait_impls_in_block(it)) } _ => None, }; @@ -1254,17 +1234,18 @@ fn iterate_inherent_methods( }; while let Some(block_id) = block { - let impls = db.inherent_impls_in_block(block_id); - impls_for_self_ty( - &impls, - self_ty, - table, - name, - receiver_ty, - receiver_adjustments.clone(), - module, - callback, - )?; + if let Some(impls) = db.inherent_impls_in_block(block_id) { + impls_for_self_ty( + &impls, + self_ty, + table, + name, + receiver_ty, + receiver_adjustments.clone(), + module, + callback, + )?; + } block = db.block_def_map(block_id).parent().and_then(|module| module.containing_block()); } diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs index 6f4aef22d2f78..d0a1fb1d57691 100644 --- a/crates/hir-ty/src/test_db.rs +++ b/crates/hir-ty/src/test_db.rs @@ -9,7 +9,6 @@ use base_db::{ use hir_def::{db::DefDatabase, ModuleId}; use hir_expand::db::ExpandDatabase; use nohash_hasher::IntMap; -use rustc_hash::FxHashSet; use syntax::TextRange; use test_utils::extract_annotations; use triomphe::Arc; @@ -81,7 +80,7 @@ impl FileLoader for TestDB { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { FileLoaderDelegate(self).relevant_crates(file_id) } } diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index d16e0eb0137bf..622b4f56d4ffa 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -987,15 +987,12 @@ fn infer_builtin_macros_env() { fn infer_builtin_macros_option_env() { check_types( r#" - //- minicore: option - //- /main.rs env:foo=bar - #[rustc_builtin_macro] - macro_rules! option_env {() => {}} - - fn main() { - let x = option_env!("foo"); - //^ Option<&str> - } +//- minicore: env +//- /main.rs env:foo=bar +fn main() { + let x = option_env!("foo"); + //^ Option<&str> +} "#, ); } @@ -1014,6 +1011,21 @@ fn test() { ); } +#[test] +fn infer_builtin_derive_resolves_with_core_module() { + check_types( + r#" +//- minicore: derive, clone +mod core {} +#[derive(Clone)] +struct S; +fn test() { + S.clone(); +} //^^^^^^^^^ S +"#, + ); +} + #[test] fn infer_derive_clone_with_params() { check_types( diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 60ddc4aa86fee..5a21f41dca84a 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -35,7 +35,7 @@ macro_rules! impl_has_attrs { impl HasAttrs for $def { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { let def = AttrDefId::$def_id(self.into()); - db.attrs_with_owner(def) + AttrsWithOwner::attrs_with_owner(db.upcast(), def) } fn attr_id(self) -> AttrDefId { AttrDefId::$def_id(self.into()) diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index 7204868464b30..403a6c88ab0f5 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs @@ -15,8 +15,8 @@ pub use hir_def::db::{ InternExternBlockQuery, InternExternCrateQuery, InternFunctionQuery, InternImplQuery, InternInTypeConstQuery, InternMacro2Query, InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery, InternStructQuery, InternTraitAliasQuery, InternTraitQuery, - InternTypeAliasQuery, InternUnionQuery, InternUseQuery, LangAttrQuery, LangItemQuery, - Macro2DataQuery, MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataQuery, + InternTypeAliasQuery, InternUnionQuery, InternUseQuery, LangItemQuery, Macro2DataQuery, + MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataQuery, StructDataWithDiagnosticsQuery, TraitAliasDataQuery, TraitDataQuery, TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, UnionDataQuery, UnionDataWithDiagnosticsQuery, VariantsAttrsQuery, VariantsAttrsSourceMapQuery, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0266915c39b68..3180a2b713a89 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -754,7 +754,7 @@ impl Module { scope .declarations() .map(ModuleDef::from) - .chain(scope.unnamed_consts().map(|id| ModuleDef::Const(Const::from(id)))) + .chain(scope.unnamed_consts(db.upcast()).map(|id| ModuleDef::Const(Const::from(id)))) .collect() } diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 841ddfb9c4373..e1101dd8236e6 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -165,6 +165,7 @@ impl<'a> SymbolCollector<'a> { // Record renamed imports. // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily // for now. + // FIXME: This parses! for id in scope.imports() { let source = id.import.child_source(self.db.upcast()); let Some(use_tree_src) = source.value.get(id.idx) else { continue }; @@ -195,7 +196,7 @@ impl<'a> SymbolCollector<'a> { }); } - for const_id in scope.unnamed_consts() { + for const_id in scope.unnamed_consts(self.db.upcast()) { self.collect_from_body(const_id); } diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index 7b71d9b869685..a64591c9ca090 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -1551,4 +1551,39 @@ use foo::Foo$0; ", ); } + + #[test] + fn considers_pub_crate() { + check_assist( + auto_import, + r#" +mod foo { + pub struct Foo; +} + +pub(crate) use self::foo::*; + +mod bar { + fn main() { + Foo$0; + } +} +"#, + r#" +mod foo { + pub struct Foo; +} + +pub(crate) use self::foo::*; + +mod bar { + use crate::Foo; + + fn main() { + Foo; + } +} +"#, + ); + } } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 347a3e9ba0747..1eb28626f75d1 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -25,7 +25,7 @@ use syntax::{ edit::{AstNodeEdit, IndentLevel}, AstNode, HasGenericParams, }, - match_ast, ted, SyntaxElement, + match_ast, ted, AstToken, SyntaxElement, SyntaxKind::{self, COMMENT}, SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, }; @@ -1733,8 +1733,23 @@ fn make_body( ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. let block = block.dedent(old_indent); - // Recreate the block for formatting consistency with other extracted functions. - make::block_expr(block.statements(), block.tail_expr()) + let elements = block.stmt_list().map_or_else( + || Either::Left(iter::empty()), + |stmt_list| { + let elements = stmt_list.syntax().children_with_tokens().filter_map( + |node_or_token| match &node_or_token { + syntax::NodeOrToken::Node(node) => { + ast::Stmt::cast(node.clone()).map(|_| node_or_token) + } + syntax::NodeOrToken::Token(token) => { + ast::Comment::cast(token.clone()).map(|_| node_or_token) + } + }, + ); + Either::Right(elements) + }, + ); + make::hacky_block_expr(elements, block.tail_expr()) } _ => { let expr = expr.dedent(old_indent).indent(IndentLevel(1)); @@ -5961,6 +5976,37 @@ fn $0fun_name() -> ControlFlow<()> { ); } + #[test] + fn comments_in_block_expr() { + check_assist( + extract_function, + r#" +fn f() { + let c = $0{ + // comment 1 + let a = 2 + 3; + // comment 2 + let b = 5; + a + b + }$0; +} +"#, + r#" +fn f() { + let c = fun_name(); +} + +fn $0fun_name() -> i32 { + // comment 1 + let a = 2 + 3; + // comment 2 + let b = 5; + a + b +} +"#, + ); + } + #[test] fn in_left_curly_is_not_applicable() { cov_mark::check!(extract_function_in_braces_is_not_applicable); diff --git a/crates/ide-assists/src/handlers/generate_constant.rs b/crates/ide-assists/src/handlers/generate_constant.rs index a4e8e7388f624..8b8c6ceee99e2 100644 --- a/crates/ide-assists/src/handlers/generate_constant.rs +++ b/crates/ide-assists/src/handlers/generate_constant.rs @@ -50,6 +50,10 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?; let target = statement.syntax().parent()?.text_range(); let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?; + if path.parent_path().is_some() { + cov_mark::hit!(not_last_path_segment); + return None; + } let name_refs = path.segments().map(|s| s.name_ref()); let mut outer_exists = false; @@ -250,6 +254,18 @@ fn bar() -> i32 { } fn bar() -> i32 { foo::goo::A_CONSTANT +}"#, + ); + } + + #[test] + fn test_wont_apply_when_not_last_path_segment() { + cov_mark::check!(not_last_path_segment); + check_assist_not_applicable( + generate_constant, + r#"mod foo {} +fn bar() -> i32 { + foo::A_CON$0STANT::invalid_segment }"#, ); } diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index dc02aaf9af68c..1f5c24f8ea446 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -107,31 +107,48 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' |edit| { // Create the function let method_source = match ctx.sema.source(method) { - Some(source) => source.value, + Some(source) => { + let v = source.value.clone_for_update(); + let source_scope = ctx.sema.scope(v.syntax()); + let target_scope = ctx.sema.scope(strukt.syntax()); + if let (Some(s), Some(t)) = (source_scope, target_scope) { + PathTransform::generic_transformation(&t, &s).apply(v.syntax()); + } + v + } None => return, }; + let vis = method_source.visibility(); + let is_async = method_source.async_token().is_some(); + let is_const = method_source.const_token().is_some(); + let is_unsafe = method_source.unsafe_token().is_some(); + let fn_name = make::name(&name); + + let type_params = method_source.generic_param_list(); + let where_clause = method_source.where_clause(); let params = method_source.param_list().unwrap_or_else(|| make::param_list(None, [])); - let type_params = method_source.generic_param_list(); - let arg_list = match method_source.param_list() { - Some(list) => convert_param_list_to_arg_list(list), - None => make::arg_list([]), - }; + + // compute the `body` + let arg_list = method_source + .param_list() + .map(|list| convert_param_list_to_arg_list(list)) + .unwrap_or_else(|| make::arg_list([])); + let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list); - let ret_type = method_source.ret_type(); - let is_async = method_source.async_token().is_some(); - let is_const = method_source.const_token().is_some(); - let is_unsafe = method_source.unsafe_token().is_some(); let tail_expr_finished = if is_async { make::expr_await(tail_expr) } else { tail_expr }; let body = make::block_expr([], Some(tail_expr_finished)); + + let ret_type = method_source.ret_type(); + let f = make::fn_( vis, fn_name, type_params, - method_source.where_clause(), + where_clause, params, body, ret_type, @@ -184,12 +201,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); - if let Some((target, source)) = - ctx.sema.scope(strukt.syntax()).zip(ctx.sema.scope(method_source.syntax())) - { - PathTransform::generic_transformation(&target, &source).apply(f.syntax()); - } - if let Some(cap) = ctx.config.snippet_cap { edit.add_tabstop_before(cap, f) } diff --git a/crates/ide-assists/src/handlers/merge_nested_if.rs b/crates/ide-assists/src/handlers/merge_nested_if.rs new file mode 100644 index 0000000000000..2f3136f027b0d --- /dev/null +++ b/crates/ide-assists/src/handlers/merge_nested_if.rs @@ -0,0 +1,246 @@ +use ide_db::syntax_helpers::node_ext::is_pattern_cond; +use syntax::{ + ast::{self, AstNode, BinaryOp}, + T, +}; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, AssistKind, +}; +// Assist: merge_nested_if +// +// This transforms if expressions of the form `if x { if y {A} }` into `if x && y {A}` +// This assist can only be applied with the cursor on `if`. +// +// ``` +// fn main() { +// i$0f x == 3 { if y == 4 { 1 } } +// } +// ``` +// -> +// ``` +// fn main() { +// if x == 3 && y == 4 { 1 } +// } +// ``` +pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; + let expr = ast::IfExpr::cast(if_keyword.parent()?)?; + let if_range = if_keyword.text_range(); + let cursor_in_range = if_range.contains_range(ctx.selection_trimmed()); + if !cursor_in_range { + return None; + } + + //should not apply to if with else branch. + if expr.else_branch().is_some() { + return None; + } + + let cond = expr.condition()?; + //should not apply for if-let + if is_pattern_cond(cond.clone()) { + return None; + } + + let cond_range = cond.syntax().text_range(); + + //check if the then branch is a nested if + let then_branch = expr.then_branch()?; + let stmt = then_branch.stmt_list()?; + if stmt.statements().count() != 0 { + return None; + } + + let nested_if_to_merge = then_branch.tail_expr().and_then(|e| match e { + ast::Expr::IfExpr(e) => Some(e), + _ => None, + })?; + // should not apply to nested if with else branch. + if nested_if_to_merge.else_branch().is_some() { + return None; + } + let nested_if_cond = nested_if_to_merge.condition()?; + if is_pattern_cond(nested_if_cond.clone()) { + return None; + } + + let nested_if_then_branch = nested_if_to_merge.then_branch()?; + let then_branch_range = then_branch.syntax().text_range(); + + acc.add( + AssistId("merge_nested_if", AssistKind::RefactorRewrite), + "Merge nested if", + if_range, + |edit| { + let cond_text = if has_logic_op_or(&cond) { + format!("({})", cond.syntax().text()) + } else { + cond.syntax().text().to_string() + }; + + let nested_if_cond_text = if has_logic_op_or(&nested_if_cond) { + format!("({})", nested_if_cond.syntax().text()) + } else { + nested_if_cond.syntax().text().to_string() + }; + + let replace_cond = format!("{} && {}", cond_text, nested_if_cond_text); + + edit.replace(cond_range, replace_cond); + edit.replace(then_branch_range, nested_if_then_branch.syntax().text()); + }, + ) +} + +/// Returns whether the given if condition has logical operators. +fn has_logic_op_or(expr: &ast::Expr) -> bool { + match expr { + ast::Expr::BinExpr(bin_expr) => { + if let Some(kind) = bin_expr.op_kind() { + matches!(kind, BinaryOp::LogicOp(ast::LogicOp::Or)) + } else { + false + } + } + _ => false, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn merge_nested_if_test1() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } } }", + "fn f() { if x == 3 && y == 4 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test2() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 || y == 1 { if z == 4 { 1 } } }", + "fn f() { if (x == 3 || y == 1) && z == 4 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test3() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 && y == 1 { if z == 4 { 1 } } }", + "fn f() { if x == 3 && y == 1 && z == 4 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test4() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 && y == 1 { if z == 4 && q == 3 { 1 } } }", + "fn f() { if x == 3 && y == 1 && z == 4 && q == 3 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test5() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 && y == 1 { if z == 4 || q == 3 { 1 } } }", + "fn f() { if x == 3 && y == 1 && (z == 4 || q == 3) { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test6() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 || y == 1 { if z == 4 || q == 3 { 1 } } }", + "fn f() { if (x == 3 || y == 1) && (z == 4 || q == 3) { 1 } }", + ) + } + + #[test] + fn merge_nested_if_test7() { + check_assist( + merge_nested_if, + "fn f() { i$0f x == 3 || y == 1 { if z == 4 && q == 3 { 1 } } }", + "fn f() { if (x == 3 || y == 1) && z == 4 && q == 3 { 1 } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_if_with_else_branch() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } } else { 2 } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_nested_if_with_else_branch() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } else { 2 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_if_let() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f let Some(x) = y { if x == 4 { 1 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_nested_if_let() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f y == 0 { if let Some(x) = y { 1 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_to_if_with_else_branch_and_nested_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 3 { if y == 4 { 1 } } else { if z == 5 { 2 } } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_with_cursor_not_on_if() { + check_assist_not_applicable(merge_nested_if, "fn f() { if $0x==0 { if y == 3 { 1 } } }") + } + + #[test] + fn merge_nested_if_do_not_apply_with_mulpiple_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 0 { if y == 3 { 1 } else if y == 4 { 2 } } }", + ) + } + #[test] + fn merge_nested_if_do_not_apply_with_not_only_has_nested_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 0 { if y == 3 { foo(); } foo(); } }", + ) + } + + #[test] + fn merge_nested_if_do_not_apply_with_multiply_nested_if() { + check_assist_not_applicable( + merge_nested_if, + "fn f() { i$0f x == 0 { if y == 3 { foo(); } if z == 3 { 2 } } }", + ) + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 1e4d1c94f5bee..1eb4903ab2033 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -217,6 +217,7 @@ mod handlers { mod unqualify_method_call; mod wrap_return_type_in_result; mod into_to_qualified_from; + mod merge_nested_if; pub(crate) fn all() -> &'static [Handler] { &[ @@ -291,6 +292,7 @@ mod handlers { invert_if::invert_if, merge_imports::merge_imports, merge_match_arms::merge_match_arms, + merge_nested_if::merge_nested_if, move_bounds::move_bounds_to_where_clause, move_const_to_impl::move_const_to_impl, move_guard::move_arm_cond_to_match_guard, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 0c2331796f9ea..0ce89ae0a9a5e 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -2051,6 +2051,23 @@ fn handle(action: Action) { ) } +#[test] +fn doctest_merge_nested_if() { + check_doc_test( + "merge_nested_if", + r#####" +fn main() { + i$0f x == 3 { if y == 4 { 1 } } +} +"#####, + r#####" +fn main() { + if x == 3 && y == 4 { 1 } +} +"#####, + ) +} + #[test] fn doctest_move_arm_cond_to_match_guard() { check_doc_test( diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 108b040de6bad..92aa1da89c411 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -529,6 +529,11 @@ impl CompletionContext<'_> { } } + /// Whether the given trait has `#[doc(notable_trait)]` + pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool { + trait_.attrs(self.db).has_doc_notable_trait() + } + /// Returns the traits in scope, with the [`Drop`] trait removed. pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits { let mut traits_in_scope = self.scope.visible_traits(); diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index de41a5bd70c4d..affd9b729646d 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -152,6 +152,8 @@ pub struct CompletionRelevance { pub is_local: bool, /// This is set when trait items are completed in an impl of that trait. pub is_item_from_trait: bool, + /// This is set for when trait items are from traits with `#[doc(notable_trait)]` + pub is_item_from_notable_trait: bool, /// This is set when an import is suggested whose name is already imported. pub is_name_already_imported: bool, /// This is set for completions that will insert a `use` item. @@ -228,6 +230,7 @@ impl CompletionRelevance { is_private_editable, postfix_match, is_definite, + is_item_from_notable_trait, } = self; // lower rank private things @@ -266,6 +269,9 @@ impl CompletionRelevance { if is_item_from_trait { score += 1; } + if is_item_from_notable_trait { + score += 1; + } if is_definite { score += 10; } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 581d557e831ad..8c0e6694761ed 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -1170,6 +1170,7 @@ fn main() { let _: m::Spam = S$0 } ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -1196,6 +1197,7 @@ fn main() { let _: m::Spam = S$0 } ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -1274,6 +1276,7 @@ fn foo() { A { the$0 } } ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -2089,6 +2092,7 @@ fn foo() { ), is_local: false, is_item_from_trait: false, + is_item_from_notable_trait: false, is_name_already_imported: false, requires_import: false, is_op_method: false, @@ -2439,4 +2443,81 @@ impl S { "#, ) } + + #[test] + fn notable_traits_method_relevance() { + check_kinds( + r#" +#[doc(notable_trait)] +trait Write { + fn write(&self); + fn flush(&self); +} + +struct Writer; + +impl Write for Writer { + fn write(&self) {} + fn flush(&self) {} +} + +fn main() { + Writer.$0 +} +"#, + &[ + CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Field), + CompletionItemKind::SymbolKind(SymbolKind::Function), + ], + expect![[r#" + [ + CompletionItem { + label: "flush()", + source_range: 193..193, + delete: 193..193, + insert: "flush()$0", + kind: Method, + lookup: "flush", + detail: "fn(&self)", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + is_item_from_trait: false, + is_item_from_notable_trait: true, + is_name_already_imported: false, + requires_import: false, + is_op_method: false, + is_private_editable: false, + postfix_match: None, + is_definite: false, + }, + }, + CompletionItem { + label: "write()", + source_range: 193..193, + delete: 193..193, + insert: "write()$0", + kind: Method, + lookup: "write", + detail: "fn(&self)", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + is_item_from_trait: false, + is_item_from_notable_trait: true, + is_name_already_imported: false, + requires_import: false, + is_op_method: false, + is_private_editable: false, + postfix_match: None, + is_definite: false, + }, + }, + ] + "#]], + ); + } } diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index b306bede653be..6ad84eba33bea 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -74,10 +74,13 @@ fn render( ); let ret_type = func.ret_type(db); - let is_op_method = func - .as_assoc_item(ctx.db()) - .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db())) - .map_or(false, |trait_| completion.is_ops_trait(trait_)); + let assoc_item = func.as_assoc_item(db); + + let trait_ = assoc_item.and_then(|trait_| trait_.containing_trait_or_trait_impl(db)); + let is_op_method = trait_.map_or(false, |trait_| completion.is_ops_trait(trait_)); + + let is_item_from_notable_trait = + trait_.map_or(false, |trait_| completion.is_doc_notable_trait(trait_)); let (has_dot_receiver, has_call_parens, cap) = match func_kind { FuncKind::Function(&PathCompletionCtx { @@ -105,6 +108,7 @@ fn render( }, exact_name_match: compute_exact_name_match(completion, &call), is_op_method, + is_item_from_notable_trait, ..ctx.completion_relevance() }); @@ -141,7 +145,7 @@ fn render( item.add_import(import_to_add); } None => { - if let Some(actm) = func.as_assoc_item(db) { + if let Some(actm) = assoc_item { if let Some(trt) = actm.containing_trait_or_trait_impl(db) { item.trait_name(trt.name(db).to_smol_str()); } diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index db6cd128e83d3..259d141404d3f 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -84,26 +84,53 @@ impl RootDatabase { )*} } purge_each_query![ - // SourceDatabase - base_db::ParseQuery - base_db::CrateGraphQuery - - // SourceDatabaseExt - base_db::FileTextQuery - base_db::FileSourceRootQuery - base_db::SourceRootQuery - base_db::SourceRootCratesQuery - - // ExpandDatabase - hir::db::AstIdMapQuery - hir::db::DeclMacroExpanderQuery - hir::db::ExpandProcMacroQuery - hir::db::InternMacroCallQuery - hir::db::InternSyntaxContextQuery - hir::db::MacroArgQuery - hir::db::ParseMacroExpansionQuery - hir::db::RealSpanMapQuery - hir::db::ProcMacrosQuery + // SymbolsDatabase + crate::symbol_index::ModuleSymbolsQuery + crate::symbol_index::LibrarySymbolsQuery + crate::symbol_index::LocalRootsQuery + crate::symbol_index::LibraryRootsQuery + // HirDatabase + hir::db::InferQueryQuery + hir::db::MirBodyQuery + hir::db::BorrowckQuery + hir::db::TyQuery + hir::db::ValueTyQuery + hir::db::ImplSelfTyQuery + hir::db::ConstParamTyQuery + hir::db::ConstEvalQuery + hir::db::ConstEvalDiscriminantQuery + hir::db::ImplTraitQuery + hir::db::FieldTypesQuery + hir::db::LayoutOfAdtQuery + hir::db::TargetDataLayoutQuery + hir::db::CallableItemSignatureQuery + hir::db::ReturnTypeImplTraitsQuery + hir::db::GenericPredicatesForParamQuery + hir::db::GenericPredicatesQuery + hir::db::TraitEnvironmentQuery + hir::db::GenericDefaultsQuery + hir::db::InherentImplsInCrateQuery + hir::db::InherentImplsInBlockQuery + hir::db::IncoherentInherentImplCratesQuery + hir::db::TraitImplsInCrateQuery + hir::db::TraitImplsInBlockQuery + hir::db::TraitImplsInDepsQuery + hir::db::InternCallableDefQuery + hir::db::InternLifetimeParamIdQuery + hir::db::InternImplTraitIdQuery + hir::db::InternTypeOrConstParamIdQuery + hir::db::InternClosureQuery + hir::db::InternGeneratorQuery + hir::db::AssociatedTyDataQuery + hir::db::TraitDatumQuery + hir::db::StructDatumQuery + hir::db::ImplDatumQuery + hir::db::FnDefDatumQuery + hir::db::FnDefVarianceQuery + hir::db::AdtVarianceQuery + hir::db::AssociatedTyValueQuery + hir::db::TraitSolveQueryQuery + hir::db::ProgramClausesForChalkEnvQuery // DefDatabase hir::db::FileItemTreeQuery @@ -145,64 +172,11 @@ impl RootDatabase { hir::db::CrateSupportsNoStdQuery hir::db::BlockItemTreeQueryQuery hir::db::ExternCrateDeclDataQuery - hir::db::LangAttrQuery hir::db::InternAnonymousConstQuery hir::db::InternExternCrateQuery hir::db::InternInTypeConstQuery hir::db::InternUseQuery - // HirDatabase - hir::db::InferQueryQuery - hir::db::MirBodyQuery - hir::db::BorrowckQuery - hir::db::TyQuery - hir::db::ValueTyQuery - hir::db::ImplSelfTyQuery - hir::db::ConstParamTyQuery - hir::db::ConstEvalQuery - hir::db::ConstEvalDiscriminantQuery - hir::db::ImplTraitQuery - hir::db::FieldTypesQuery - hir::db::LayoutOfAdtQuery - hir::db::TargetDataLayoutQuery - hir::db::CallableItemSignatureQuery - hir::db::ReturnTypeImplTraitsQuery - hir::db::GenericPredicatesForParamQuery - hir::db::GenericPredicatesQuery - hir::db::TraitEnvironmentQuery - hir::db::GenericDefaultsQuery - hir::db::InherentImplsInCrateQuery - hir::db::InherentImplsInBlockQuery - hir::db::IncoherentInherentImplCratesQuery - hir::db::TraitImplsInCrateQuery - hir::db::TraitImplsInBlockQuery - hir::db::TraitImplsInDepsQuery - hir::db::InternCallableDefQuery - hir::db::InternLifetimeParamIdQuery - hir::db::InternImplTraitIdQuery - hir::db::InternTypeOrConstParamIdQuery - hir::db::InternClosureQuery - hir::db::InternGeneratorQuery - hir::db::AssociatedTyDataQuery - hir::db::TraitDatumQuery - hir::db::StructDatumQuery - hir::db::ImplDatumQuery - hir::db::FnDefDatumQuery - hir::db::FnDefVarianceQuery - hir::db::AdtVarianceQuery - hir::db::AssociatedTyValueQuery - hir::db::TraitSolveQueryQuery - hir::db::ProgramClausesForChalkEnvQuery - - // SymbolsDatabase - crate::symbol_index::ModuleSymbolsQuery - crate::symbol_index::LibrarySymbolsQuery - crate::symbol_index::LocalRootsQuery - crate::symbol_index::LibraryRootsQuery - - // LineIndexDatabase - crate::LineIndexQuery - // InternDatabase hir::db::InternFunctionQuery hir::db::InternStructQuery @@ -219,6 +193,30 @@ impl RootDatabase { hir::db::InternMacro2Query hir::db::InternProcMacroQuery hir::db::InternMacroRulesQuery + + // ExpandDatabase + hir::db::AstIdMapQuery + hir::db::DeclMacroExpanderQuery + hir::db::ExpandProcMacroQuery + hir::db::InternMacroCallQuery + hir::db::InternSyntaxContextQuery + hir::db::MacroArgQuery + hir::db::ParseMacroExpansionQuery + hir::db::RealSpanMapQuery + hir::db::ProcMacrosQuery + + // LineIndexDatabase + crate::LineIndexQuery + + // SourceDatabase + base_db::ParseQuery + base_db::CrateGraphQuery + + // SourceDatabaseExt + base_db::FileTextQuery + base_db::FileSourceRootQuery + base_db::SourceRootQuery + base_db::SourceRootCratesQuery ]; acc.sort_by_key(|it| std::cmp::Reverse(it.1)); diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 652968d808fac..b834f517d491d 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -681,11 +681,10 @@ fn path_import_candidate( Some(qualifier) => match sema.resolve_path(&qualifier) { None => { if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) { - let mut qualifier = qualifier - .segments_of_this_path_only_rev() + let qualifier = qualifier + .segments() .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) .collect::>>()?; - qualifier.reverse(); ImportCandidate::Path(PathImportCandidate { qualifier: Some(qualifier), name }) } else { return None; diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 128971994f64b..eae23e9548240 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -124,7 +124,7 @@ impl FileLoader for RootDatabase { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> { FileLoaderDelegate(self).relevant_crates(file_id) } } @@ -145,7 +145,7 @@ impl RootDatabase { db.set_local_roots_with_durability(Default::default(), Durability::HIGH); db.set_library_roots_with_durability(Default::default(), Durability::HIGH); db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH); - db.update_parse_query_lru_capacity(lru_capacity); + db.update_base_query_lru_capacities(lru_capacity); db.setup_syntax_context_root(); db } @@ -154,11 +154,12 @@ impl RootDatabase { self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); } - pub fn update_parse_query_lru_capacity(&mut self, lru_capacity: Option) { + pub fn update_base_query_lru_capacities(&mut self, lru_capacity: Option) { let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); // macro expansions are usually rather small, so we can afford to keep more of them alive hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); + hir::db::BorrowckQuery.in_db_mut(self).set_lru_capacity(base_db::DEFAULT_BORROWCK_LRU_CAP); } pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, usize>) { @@ -176,6 +177,12 @@ impl RootDatabase { .copied() .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP), ); + hir_db::BorrowckQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(BorrowckQuery)) + .copied() + .unwrap_or(base_db::DEFAULT_BORROWCK_LRU_CAP), + ); macro_rules! update_lru_capacity_per_query { ($( $module:ident :: $query:ident )*) => {$( diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index edfeddc1bc9c8..47bcaae259b41 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -3,6 +3,7 @@ use crate::helpers::mod_path_to_ast; use either::Either; use hir::{AsAssocItem, HirDisplay, ModuleDef, SemanticsScope}; +use itertools::Itertools; use rustc_hash::FxHashMap; use syntax::{ ast::{self, make, AstNode}, @@ -227,11 +228,15 @@ struct Ctx<'a> { same_self_type: bool, } -fn postorder(item: &SyntaxNode) -> impl Iterator { - item.preorder().filter_map(|event| match event { - syntax::WalkEvent::Enter(_) => None, - syntax::WalkEvent::Leave(node) => Some(node), - }) +fn preorder_rev(item: &SyntaxNode) -> impl Iterator { + let x = item + .preorder() + .filter_map(|event| match event { + syntax::WalkEvent::Enter(node) => Some(node), + syntax::WalkEvent::Leave(_) => None, + }) + .collect_vec(); + x.into_iter().rev() } impl Ctx<'_> { @@ -239,12 +244,12 @@ impl Ctx<'_> { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = postorder(item).filter_map(ast::Path::cast).collect::>(); + let paths = preorder_rev(item).filter_map(ast::Path::cast).collect::>(); for path in paths { self.transform_path(path); } - postorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { + preorder_rev(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); } @@ -263,7 +268,7 @@ impl Ctx<'_> { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = postorder(value).filter_map(ast::Path::cast).collect::>(); + let paths = preorder_rev(value).filter_map(ast::Path::cast).collect::>(); for path in paths { self.transform_path(path); } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index a40dd2692cfd1..e2b20ef92fc43 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -356,7 +356,7 @@ impl Definition { if let Some(Visibility::Public) = vis { return SearchScope::reverse_dependencies(db, module.krate()); } - if let Some(Visibility::Module(module)) = vis { + if let Some(Visibility::Module(module, _)) = vis { return SearchScope::module_and_children(db, module.into()); } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs index f1c95993c843e..551021c55a98e 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs @@ -13,6 +13,7 @@ pub(crate) fn unresolved_assoc_item( "no such associated item", d.expr_or_pat.clone().map(Into::into), ) + .experimental() } #[cfg(test)] diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 024053effe423..17c701ad03591 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -481,7 +481,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl < >$crate::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -507,7 +507,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl < >$crate::marker::Copy for Foo< >where{}"#]], ); } @@ -522,7 +522,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl < >$crate::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -533,7 +533,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl < >$crate::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index c98e9fba120ad..60a9367adceec 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -171,7 +171,7 @@ impl AnalysisHost { } pub fn update_lru_capacity(&mut self, lru_capacity: Option) { - self.db.update_parse_query_lru_capacity(lru_capacity); + self.db.update_base_query_lru_capacities(lru_capacity); } pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, usize>) { diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 556ed73a04c2c..e6ddfd580c30e 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -322,7 +322,7 @@ fn load_crate_graph( break; } } - vfs::loader::Message::Loaded { files } => { + vfs::loader::Message::Loaded { files } | vfs::loader::Message::Changed { files } => { for (path, contents) in files { vfs.set_file_contents(path.into(), contents); } @@ -331,9 +331,8 @@ fn load_crate_graph( } let changes = vfs.take_changes(); for file in changes { - if file.exists() { - let contents = vfs.file_contents(file.file_id); - if let Ok(text) = std::str::from_utf8(contents) { + if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change { + if let Ok(text) = std::str::from_utf8(&v) { analysis_change.change_file(file.file_id, Some(text.into())) } } diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 0c63484634be9..e74b340126c0a 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -13,8 +13,7 @@ doctest = false [dependencies] drop_bomb = "0.1.5" -rustc-dependencies.workspace = true - +ra-ap-rustc_lexer.workspace = true limit.workspace = true [dev-dependencies] @@ -24,7 +23,7 @@ stdx.workspace = true sourcegen.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index e346ece2f94b6..c8626111145de 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -371,7 +371,15 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik if p.at(op) { m = p.start(); p.bump(op); - if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])) { + + // test closure_range_method_call + // fn foo() { + // || .. .method(); + // || .. .field; + // } + let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); + let struct_forbidden = r.forbid_structs && p.at(T!['{']); + if p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden { expr_bp(p, None, r, 2); } let cm = m.complete(p, RANGE_EXPR); diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs index b9e7566fdf9bc..f47ec49df1d9f 100644 --- a/crates/parser/src/lexed_str.rs +++ b/crates/parser/src/lexed_str.rs @@ -8,8 +8,6 @@ //! Note that these tokens, unlike the tokens we feed into the parser, do //! include info about comments and whitespace. -use rustc_dependencies::lexer as rustc_lexer; - use std::ops; use rustc_lexer::unescape::{EscapeError, Mode}; diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index d9b3f46f2001c..ed0aec3cab32a 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -21,6 +21,11 @@ #![allow(rustdoc::private_intra_doc_links)] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + mod lexed_str; mod token_set; mod syntax_kind; diff --git a/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast b/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast new file mode 100644 index 0000000000000..542711339d19c --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast @@ -0,0 +1,49 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + METHOD_CALL_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "method" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + FIELD_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "field" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs b/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs new file mode 100644 index 0000000000000..a81d3c37133a6 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs @@ -0,0 +1,4 @@ +fn foo() { + || .. .method(); + || .. .field; +} diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index a87becd63e288..208051113a7d2 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -14,8 +14,10 @@ mod version; use indexmap::IndexSet; use paths::AbsPathBuf; use span::Span; -use std::{fmt, io, sync::Mutex}; -use triomphe::Arc; +use std::{ + fmt, io, + sync::{Arc, Mutex}, +}; use serde::{Deserialize, Serialize}; @@ -81,9 +83,11 @@ impl PartialEq for ProcMacro { } } +#[derive(Clone, Debug)] pub struct ServerError { pub message: String, - pub io: Option, + // io::Error isn't Clone for some reason + pub io: Option>, } impl fmt::Display for ServerError { diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 3494164c0675c..5ce601bce6929 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -1,8 +1,9 @@ //! Handle process life-time and message passing for proc-macro client use std::{ - io::{self, BufRead, BufReader, Write}, + io::{self, BufRead, BufReader, Read, Write}, process::{Child, ChildStdin, ChildStdout, Command, Stdio}, + sync::Arc, }; use paths::{AbsPath, AbsPathBuf}; @@ -15,9 +16,11 @@ use crate::{ #[derive(Debug)] pub(crate) struct ProcMacroProcessSrv { - _process: Process, + process: Process, stdin: ChildStdin, stdout: BufReader, + /// Populated when the server exits. + server_exited: Option, version: u32, mode: SpanMode, } @@ -29,9 +32,10 @@ impl ProcMacroProcessSrv { let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); io::Result::Ok(ProcMacroProcessSrv { - _process: process, + process, stdin, stdout, + server_exited: None, version: 0, mode: SpanMode::Id, }) @@ -105,8 +109,35 @@ impl ProcMacroProcessSrv { } pub(crate) fn send_task(&mut self, req: Request) -> Result { + if let Some(server_error) = &self.server_exited { + return Err(server_error.clone()); + } + let mut buf = String::new(); - send_request(&mut self.stdin, &mut self.stdout, req, &mut buf) + send_request(&mut self.stdin, &mut self.stdout, req, &mut buf).map_err(|e| { + if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { + match self.process.child.try_wait() { + Ok(None) => e, + Ok(Some(status)) => { + let mut msg = String::new(); + if !status.success() { + if let Some(stderr) = self.process.child.stderr.as_mut() { + _ = stderr.read_to_string(&mut msg); + } + } + let server_error = ServerError { + message: format!("server exited with {status}: {msg}"), + io: None, + }; + self.server_exited = Some(server_error.clone()); + server_error + } + Err(_) => e, + } + } else { + e + } + }) } } @@ -131,12 +162,19 @@ impl Process { } fn mk_child(path: &AbsPath, null_stderr: bool) -> io::Result { - Command::new(path.as_os_str()) - .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") + let mut cmd = Command::new(path.as_os_str()); + cmd.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) .stdout(Stdio::piped()) - .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }) - .spawn() + .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }); + if cfg!(windows) { + let mut path_var = std::ffi::OsString::new(); + path_var.push(path.parent().unwrap().parent().unwrap().as_os_str()); + path_var.push("\\bin;"); + path_var.push(std::env::var_os("PATH").unwrap_or_default()); + cmd.env("PATH", path_var); + } + cmd.spawn() } fn send_request( @@ -145,9 +183,13 @@ fn send_request( req: Request, buf: &mut String, ) -> Result { - req.write(&mut writer) - .map_err(|err| ServerError { message: "failed to write request".into(), io: Some(err) })?; - let res = Response::read(&mut reader, buf) - .map_err(|err| ServerError { message: "failed to read response".into(), io: Some(err) })?; + req.write(&mut writer).map_err(|err| ServerError { + message: "failed to write request".into(), + io: Some(Arc::new(err)), + })?; + let res = Response::read(&mut reader, buf).map_err(|err| ServerError { + message: "failed to read response".into(), + io: Some(Arc::new(err)), + })?; res.ok_or_else(|| ServerError { message: "server exited".into(), io: None }) } diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index 87f7555b02ce4..af9a03826ffc0 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -24,7 +24,8 @@ fn main() -> std::io::Result<()> { #[cfg(not(any(feature = "sysroot-abi", rust_analyzer)))] fn run() -> io::Result<()> { - panic!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled"); + eprintln!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled"); + std::process::exit(70); } #[cfg(any(feature = "sysroot-abi", rust_analyzer))] diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index 9c4375559c11b..ba17ea6f7b439 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -37,7 +37,7 @@ expect-test = "1.4.0" proc-macro-test.path = "./proc-macro-test" [features] -sysroot-abi = ["proc-macro-test/sysroot-abi"] +sysroot-abi = [] in-rust-tree = ["mbe/in-rust-tree", "sysroot-abi"] [lints] diff --git a/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/crates/proc-macro-srv/proc-macro-test/Cargo.toml index 90545bb5130c7..7977afb1cbd23 100644 --- a/crates/proc-macro-srv/proc-macro-test/Cargo.toml +++ b/crates/proc-macro-srv/proc-macro-test/Cargo.toml @@ -14,6 +14,3 @@ cargo_metadata = "0.18.1" # local deps toolchain.workspace = true - -[features] -sysroot-abi = [] diff --git a/crates/proc-macro-srv/proc-macro-test/build.rs b/crates/proc-macro-srv/proc-macro-test/build.rs index 7299147686df7..6cf2b5643e579 100644 --- a/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/crates/proc-macro-srv/proc-macro-test/build.rs @@ -17,11 +17,24 @@ use cargo_metadata::Message; fn main() { println!("cargo:rerun-if-changed=imp"); - println!("cargo:rerun-if-env-changed=PROC_MACRO_TEST_TOOLCHAIN"); + + let has_features = env::var_os("RUSTC_BOOTSTRAP").is_some() + || String::from_utf8( + Command::new(toolchain::cargo()).arg("--version").output().unwrap().stdout, + ) + .unwrap() + .contains("nightly"); let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = Path::new(&out_dir); + if !has_features { + println!("proc-macro-test testing only works on nightly toolchains"); + let info_path = out_dir.join("proc_macro_test_location.txt"); + fs::File::create(info_path).unwrap(); + return; + } + let name = "proc-macro-test-impl"; let version = "0.0.0"; @@ -53,15 +66,7 @@ fn main() { let target_dir = out_dir.join("target"); - let mut cmd = if let Ok(toolchain) = std::env::var("PROC_MACRO_TEST_TOOLCHAIN") { - // leverage rustup to find user-specific toolchain - let mut cmd = Command::new("cargo"); - cmd.arg(format!("+{toolchain}")); - cmd - } else { - Command::new(toolchain::cargo()) - }; - + let mut cmd = Command::new(toolchain::cargo()); cmd.current_dir(&staging_dir) .args(["build", "-p", "proc-macro-test-impl", "--message-format", "json"]) // Explicit override the target directory to avoid using the same one which the parent @@ -70,9 +75,6 @@ fn main() { // instance to use the same target directory. .arg("--target-dir") .arg(&target_dir); - if cfg!(feature = "sysroot-abi") { - cmd.args(["--features", "sysroot-abi"]); - } if let Ok(target) = std::env::var("TARGET") { cmd.args(["--target", &target]); diff --git a/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml b/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml index dc94fcd61a4f7..fa189752b76f3 100644 --- a/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml +++ b/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml @@ -13,7 +13,4 @@ proc-macro = true # this crate should not have any dependencies, since it uses its own workspace, # and its own `Cargo.lock` -[features] -sysroot-abi = [] - [workspace] diff --git a/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index b8aad4acefcf0..d9018b1b87d34 100644 --- a/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -1,8 +1,5 @@ //! Exports a few trivial procedural macros for testing. -#![allow(unexpected_cfgs)] -#![cfg(feature = "sysroot-abi")] -#![cfg(any(feature = "sysroot-abi", rust_analyzer))] #![warn(rust_2018_idioms, unused_lifetimes)] #![feature(proc_macro_span, proc_macro_def_site)] diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index f1575a5b0bd83..67b9f57a1636c 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -11,11 +11,13 @@ //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… #![cfg(any(feature = "sysroot-abi", rust_analyzer))] -#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unreachable_pub, internal_features)] extern crate proc_macro; +#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod dylib; diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 00cc7c30ee3e0..c04eddc56fb7f 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -1398,7 +1398,7 @@ fn sysroot_to_crate_graph( let public_deps = SysrootPublicDeps { deps: sysroot .public_deps() - .map(|(name, idx, prelude)| (name, sysroot_crates[&idx], prelude)) + .filter_map(|(name, idx, prelude)| Some((name, *sysroot_crates.get(&idx)?, prelude))) .collect::>(), }; diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index ad24d6d28cd7a..76414160716c8 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -49,7 +49,6 @@ flycheck.workspace = true hir-def.workspace = true hir-ty.workspace = true hir.workspace = true -rustc-dependencies.workspace = true ide-db.workspace = true # This should only be used in CLI ide-ssr.workspace = true @@ -89,11 +88,10 @@ in-rust-tree = [ "ide/in-rust-tree", "syntax/in-rust-tree", "parser/in-rust-tree", - "rustc-dependencies/in-rust-tree", "hir/in-rust-tree", "hir-def/in-rust-tree", "hir-ty/in-rust-tree", ] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index f57a27305f03c..c4a29e0cbb03b 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -3,7 +3,7 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::time::Instant; +use std::{collections::hash_map::Entry, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; @@ -21,7 +21,7 @@ use proc_macro_api::ProcMacroServer; use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts}; use rustc_hash::{FxHashMap, FxHashSet}; use triomphe::Arc; -use vfs::{AnchoredPathBuf, Vfs}; +use vfs::{AnchoredPathBuf, ChangedFile, Vfs}; use crate::{ config::{Config, ConfigError}, @@ -217,8 +217,8 @@ impl GlobalState { pub(crate) fn process_changes(&mut self) -> bool { let _p = profile::span("GlobalState::process_changes"); - let mut file_changes = FxHashMap::default(); - let (change, changed_files, workspace_structure_change) = { + let mut file_changes = FxHashMap::<_, (bool, ChangedFile)>::default(); + let (change, modified_rust_files, workspace_structure_change) = { let mut change = Change::new(); let mut guard = self.vfs.write(); let changed_files = guard.0.take_changes(); @@ -233,64 +233,63 @@ impl GlobalState { // id that is followed by a delete we actually skip observing the file text from the // earlier event, to avoid problems later on. for changed_file in changed_files { - use vfs::ChangeKind::*; - - file_changes - .entry(changed_file.file_id) - .and_modify(|(change, just_created)| { - // None -> Delete => keep - // Create -> Delete => collapse - // - match (change, just_created, changed_file.change_kind) { + use vfs::Change::*; + match file_changes.entry(changed_file.file_id) { + Entry::Occupied(mut o) => { + let (just_created, change) = o.get_mut(); + match (&mut change.change, just_created, changed_file.change) { // latter `Delete` wins (change, _, Delete) => *change = Delete, // merge `Create` with `Create` or `Modify` - (Create, _, Create | Modify) => {} + (Create(prev), _, Create(new) | Modify(new)) => *prev = new, // collapse identical `Modify`es - (Modify, _, Modify) => {} + (Modify(prev), _, Modify(new)) => *prev = new, // equivalent to `Modify` - (change @ Delete, just_created, Create) => { - *change = Modify; + (change @ Delete, just_created, Create(new)) => { + *change = Modify(new); *just_created = true; } // shouldn't occur, but collapse into `Create` - (change @ Delete, just_created, Modify) => { - *change = Create; + (change @ Delete, just_created, Modify(new)) => { + *change = Create(new); *just_created = true; } - // shouldn't occur, but collapse into `Modify` - (Modify, _, Create) => {} + // shouldn't occur, but keep the Create + (prev @ Modify(_), _, new @ Create(_)) => *prev = new, } - }) - .or_insert(( - changed_file.change_kind, - matches!(changed_file.change_kind, Create), - )); + } + Entry::Vacant(v) => { + _ = v.insert((matches!(&changed_file.change, Create(_)), changed_file)) + } + } } let changed_files: Vec<_> = file_changes .into_iter() - .filter(|(_, (change_kind, just_created))| { - !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) + .filter(|(_, (just_created, change))| { + !(*just_created && matches!(change.change, vfs::Change::Delete)) }) - .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }) + .map(|(file_id, (_, change))| vfs::ChangedFile { file_id, ..change }) .collect(); let mut workspace_structure_change = None; // A file was added or deleted let mut has_structure_changes = false; let mut bytes = vec![]; - for file in &changed_files { + let mut modified_rust_files = vec![]; + for file in changed_files { let vfs_path = &vfs.file_path(file.file_id); if let Some(path) = vfs_path.as_path() { let path = path.to_path_buf(); - if reload::should_refresh_for_change(&path, file.change_kind) { + if reload::should_refresh_for_change(&path, file.kind()) { workspace_structure_change = Some((path.clone(), false)); } if file.is_created_or_deleted() { has_structure_changes = true; workspace_structure_change = Some((path, self.crate_graph_file_dependencies.contains(vfs_path))); + } else if path.extension() == Some("rs".as_ref()) { + modified_rust_files.push(file.file_id); } } @@ -299,10 +298,8 @@ impl GlobalState { self.diagnostics.clear_native_for(file.file_id); } - let text = if file.exists() { - let bytes = vfs.file_contents(file.file_id).to_vec(); - - String::from_utf8(bytes).ok().and_then(|text| { + let text = if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change { + String::from_utf8(v).ok().and_then(|text| { // FIXME: Consider doing normalization in the `vfs` instead? That allows // getting rid of some locking let (text, line_endings) = LineEndings::normalize(text); @@ -327,11 +324,10 @@ impl GlobalState { let roots = self.source_root_config.partition(vfs); change.set_roots(roots); } - (change, changed_files, workspace_structure_change) + (change, modified_rust_files, workspace_structure_change) }; self.analysis_host.apply_change(change); - { let raw_database = self.analysis_host.raw_database(); // FIXME: ideally we should only trigger a workspace fetch for non-library changes @@ -343,13 +339,12 @@ impl GlobalState { force_crate_graph_reload, ); } - self.proc_macro_changed = - changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| { - let crates = raw_database.relevant_crates(file.file_id); - let crate_graph = raw_database.crate_graph(); + self.proc_macro_changed = modified_rust_files.into_iter().any(|file_id| { + let crates = raw_database.relevant_crates(file_id); + let crate_graph = raw_database.crate_graph(); - crates.iter().any(|&krate| crate_graph[krate].is_proc_macro) - }); + crates.iter().any(|&krate| crate_graph[krate].is_proc_macro) + }); } true @@ -494,10 +489,6 @@ impl GlobalStateSnapshot { }) } - pub(crate) fn vfs_memory_usage(&self) -> usize { - self.vfs_read().memory_usage() - } - pub(crate) fn file_exists(&self, file_id: FileId) -> bool { self.vfs.read().0.exists(file_id) } diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 7e6219991b665..ce69d612255a5 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -59,7 +59,13 @@ pub(crate) fn handle_did_open_text_document( if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { let already_exists = state .mem_docs - .insert(path.clone(), DocumentData::new(params.text_document.version)) + .insert( + path.clone(), + DocumentData::new( + params.text_document.version, + params.text_document.text.clone().into_bytes(), + ), + ) .is_err(); if already_exists { tracing::error!("duplicate DidOpenTextDocument: {}", path); @@ -76,11 +82,12 @@ pub(crate) fn handle_did_change_text_document( let _p = profile::span("handle_did_change_text_document"); if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { - match state.mem_docs.get_mut(&path) { + let data = match state.mem_docs.get_mut(&path) { Some(doc) => { // The version passed in DidChangeTextDocument is the version after all edits are applied // so we should apply it before the vfs is notified. doc.version = params.text_document.version; + &mut doc.data } None => { tracing::error!("unexpected DidChangeTextDocument: {}", path); @@ -88,16 +95,16 @@ pub(crate) fn handle_did_change_text_document( } }; - let text = apply_document_changes( + let new_contents = apply_document_changes( state.config.position_encoding(), - || { - let vfs = &state.vfs.read().0; - let file_id = vfs.file_id(&path).unwrap(); - std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into() - }, + std::str::from_utf8(data).unwrap(), params.content_changes, - ); - state.vfs.write().0.set_file_contents(path, Some(text.into_bytes())); + ) + .into_bytes(); + if *data != new_contents { + *data = new_contents.clone(); + state.vfs.write().0.set_file_contents(path, Some(new_contents)); + } } Ok(()) } diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 13544558c50c9..22c7e9b0503c5 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -103,7 +103,6 @@ pub(crate) fn handle_analyzer_status( .collect::>() ); } - format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _)); buf.push_str("\nAnalysis:\n"); buf.push_str( &snap diff --git a/crates/rust-analyzer/src/lsp/utils.rs b/crates/rust-analyzer/src/lsp/utils.rs index a4417e4d4a143..fa5ea5b57db00 100644 --- a/crates/rust-analyzer/src/lsp/utils.rs +++ b/crates/rust-analyzer/src/lsp/utils.rs @@ -168,7 +168,7 @@ impl GlobalState { pub(crate) fn apply_document_changes( encoding: PositionEncoding, - file_contents: impl FnOnce() -> String, + file_contents: &str, mut content_changes: Vec, ) -> String { // If at least one of the changes is a full document change, use the last @@ -179,7 +179,7 @@ pub(crate) fn apply_document_changes( let text = mem::take(&mut content_changes[idx].text); (text, &content_changes[idx + 1..]) } - None => (file_contents(), &content_changes[..]), + None => (file_contents.to_owned(), &content_changes[..]), }; if content_changes.is_empty() { return text; @@ -276,11 +276,11 @@ mod tests { } let encoding = PositionEncoding::Wide(WideEncoding::Utf16); - let text = apply_document_changes(encoding, || String::new(), vec![]); + let text = apply_document_changes(encoding, "", vec![]); assert_eq!(text, ""); let text = apply_document_changes( encoding, - || text, + &text, vec![TextDocumentContentChangeEvent { range: None, range_length: None, @@ -288,49 +288,46 @@ mod tests { }], ); assert_eq!(text, "the"); - let text = apply_document_changes(encoding, || text, c![0, 3; 0, 3 => " quick"]); + let text = apply_document_changes(encoding, &text, c![0, 3; 0, 3 => " quick"]); assert_eq!(text, "the quick"); let text = - apply_document_changes(encoding, || text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); + apply_document_changes(encoding, &text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); assert_eq!(text, "quick foxes"); - let text = apply_document_changes(encoding, || text, c![0, 11; 0, 11 => "\ndream"]); + let text = apply_document_changes(encoding, &text, c![0, 11; 0, 11 => "\ndream"]); assert_eq!(text, "quick foxes\ndream"); - let text = apply_document_changes(encoding, || text, c![1, 0; 1, 0 => "have "]); + let text = apply_document_changes(encoding, &text, c![1, 0; 1, 0 => "have "]); assert_eq!(text, "quick foxes\nhave dream"); let text = apply_document_changes( encoding, - || text, + &text, c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"], ); assert_eq!(text, "the quick foxes\nhave quiet dreams\n"); - let text = apply_document_changes( - encoding, - || text, - c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"], - ); + let text = + apply_document_changes(encoding, &text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); let text = apply_document_changes( encoding, - || text, + &text, c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], ); assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); let text = - apply_document_changes(encoding, || text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); + apply_document_changes(encoding, &text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); assert_eq!(text, "the quick \nthey have quiet dreams\n"); let text = String::from("❤️"); - let text = apply_document_changes(encoding, || text, c![0, 0; 0, 0 => "a"]); + let text = apply_document_changes(encoding, &text, c![0, 0; 0, 0 => "a"]); assert_eq!(text, "a❤️"); let text = String::from("a\nb"); let text = - apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); + apply_document_changes(encoding, &text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); assert_eq!(text, "adcb"); let text = String::from("a\nb"); let text = - apply_document_changes(encoding, || text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); + apply_document_changes(encoding, &text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); assert_eq!(text, "ațc\ncb"); } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index cdf41c955d26f..ca7893faf5dd7 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -571,13 +571,18 @@ impl GlobalState { } fn handle_vfs_msg(&mut self, message: vfs::loader::Message) { + let is_changed = matches!(message, vfs::loader::Message::Changed { .. }); match message { - vfs::loader::Message::Loaded { files } => { + vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => { let vfs = &mut self.vfs.write().0; for (path, contents) in files { let path = VfsPath::from(path); + // if the file is in mem docs, it's managed by the client via notifications + // so only set it if its not in there if !self.mem_docs.contains(&path) { - vfs.set_file_contents(path, contents); + if is_changed || vfs.file_id(&path).is_none() { + vfs.set_file_contents(path, contents); + } } } } diff --git a/crates/rust-analyzer/src/mem_docs.rs b/crates/rust-analyzer/src/mem_docs.rs index 45a1dab9772fa..6a93a0ebb4cec 100644 --- a/crates/rust-analyzer/src/mem_docs.rs +++ b/crates/rust-analyzer/src/mem_docs.rs @@ -62,10 +62,11 @@ impl MemDocs { #[derive(Debug, Clone)] pub(crate) struct DocumentData { pub(crate) version: i32, + pub(crate) data: Vec, } impl DocumentData { - pub(crate) fn new(version: i32) -> Self { - DocumentData { version } + pub(crate) fn new(version: i32, data: Vec) -> Self { + DocumentData { version, data } } } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 91dc6c2e4b411..8e3fa7fa4dace 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -503,10 +503,9 @@ impl GlobalState { match vfs.file_id(&vfs_path) { Some(file_id) => Some(file_id), None => { - if !self.mem_docs.contains(&vfs_path) { - let contents = loader.handle.load_sync(path); - vfs.set_file_contents(vfs_path.clone(), contents); - } + // FIXME: Consider not loading this here? + let contents = loader.handle.load_sync(path); + vfs.set_file_contents(vfs_path.clone(), contents); vfs.file_id(&vfs_path) } } diff --git a/crates/rustc-dependencies/Cargo.toml b/crates/rustc-dependencies/Cargo.toml deleted file mode 100644 index 0bf04301df169..0000000000000 --- a/crates/rustc-dependencies/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "rustc-dependencies" -version = "0.0.0" -description = "TBD" - -rust-version.workspace = true -edition.workspace = true -license.workspace = true -authors.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ra-ap-rustc_lexer = { version = "0.21.0" } -ra-ap-rustc_parse_format = { version = "0.21.0", default-features = false } -ra-ap-rustc_index = { version = "0.21.0", default-features = false } -ra-ap-rustc_abi = { version = "0.21.0", default-features = false } - -[features] -in-rust-tree = [] - -[lints] -workspace = true \ No newline at end of file diff --git a/crates/rustc-dependencies/src/lib.rs b/crates/rustc-dependencies/src/lib.rs deleted file mode 100644 index 13fcbc4919370..0000000000000 --- a/crates/rustc-dependencies/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! A wrapper around rustc internal crates, which enables switching between compiler provided -//! ones and stable ones published in crates.io - -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_lexer; - -pub mod lexer { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_lexer::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_lexer::*; -} - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_parse_format; - -pub mod parse_format { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_parse_format::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_parse_format::*; -} - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_abi; - -pub mod abi { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_abi::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_abi::*; -} - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_index; - -pub mod index { - #[cfg(not(feature = "in-rust-tree"))] - pub use ::ra_ap_rustc_index::*; - - #[cfg(feature = "in-rust-tree")] - pub use ::rustc_index::*; -} diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 40a93fec2cec2..9f78614bba66b 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -23,7 +23,7 @@ indexmap.workspace = true smol_str.workspace = true triomphe.workspace = true -rustc-dependencies.workspace = true +ra-ap-rustc_lexer.workspace = true parser.workspace = true profile.workspace = true @@ -41,7 +41,7 @@ test-utils.workspace = true sourcegen.workspace = true [features] -in-rust-tree = ["rustc-dependencies/in-rust-tree"] +in-rust-tree = [] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 8618018c0b6a5..1c6157de55958 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -285,14 +285,16 @@ impl ast::Path { self.first_qualifier_or_self().segment() } - // FIXME: Check usages of Self::segments, they might be wrong because of the logic of the bloew function - pub fn segments_of_this_path_only_rev(&self) -> impl Iterator + Clone { - self.qualifiers_and_self().filter_map(|it| it.segment()) - } - pub fn segments(&self) -> impl Iterator + Clone { - successors(self.first_segment(), |p| { - p.parent_path().parent_path().and_then(|p| p.segment()) + let path_range = self.syntax().text_range(); + successors(self.first_segment(), move |p| { + p.parent_path().parent_path().and_then(|p| { + if path_range.contains_range(p.syntax().text_range()) { + p.segment() + } else { + None + } + }) }) } @@ -300,10 +302,6 @@ impl ast::Path { successors(self.qualifier(), |p| p.qualifier()) } - pub fn qualifiers_and_self(&self) -> impl Iterator + Clone { - successors(Some(self.clone()), |p| p.qualifier()) - } - pub fn top_path(&self) -> ast::Path { let mut this = self.clone(); while let Some(path) = this.parent_path() { diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index d5d565a015a0d..ede392fc62a2c 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -2,8 +2,6 @@ use std::borrow::Cow; -use rustc_dependencies::lexer as rustc_lexer; - use rustc_lexer::unescape::{ unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, Mode, }; diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index d600698040dc7..1b41596a5f27b 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -22,6 +22,11 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![warn(rust_2018_idioms, unused_lifetimes)] +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + #[allow(unused)] macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index eabbda2c3983c..6c6916c585f1f 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs @@ -5,7 +5,7 @@ mod block; use rowan::Direction; -use rustc_dependencies::lexer::unescape::{self, unescape_literal, Mode}; +use rustc_lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 1f3136404c615..140bb08042785 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -25,6 +25,7 @@ //! derive: //! discriminant: //! drop: +//! env: option //! eq: sized //! error: fmt //! fmt: result, transmute, coerce_unsized @@ -1450,6 +1451,15 @@ mod macros { #[macro_export] macro_rules! concat {} // endregion:concat + + // region:env + #[rustc_builtin_macro] + #[macro_export] + macro_rules! env {} + #[rustc_builtin_macro] + #[macro_export] + macro_rules! option_env {} + // endregion:env } // region:non_zero diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 0306504371465..19b34ffe6b9d7 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -160,7 +160,7 @@ impl NotifyActor { Some((path, contents)) }) .collect(); - self.send(loader::Message::Loaded { files }); + self.send(loader::Message::Changed { files }); } } } diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index ef5b10ee9db2f..34a85818eb84b 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -1,8 +1,8 @@ //! # Virtual File System //! -//! VFS stores all files read by rust-analyzer. Reading file contents from VFS -//! always returns the same contents, unless VFS was explicitly modified with -//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via +//! VFS records all file changes pushed to it via [`set_file_contents`]. +//! As such it only ever stores changes, not the actual content of a file at any given moment. +//! All file changes are logged, and can be retrieved via //! [`take_changes`] method. The pack of changes is then pushed to `salsa` and //! triggers incremental recomputation. //! @@ -84,40 +84,65 @@ impl FileId { /// safe because `FileId` is a newtype of `u32` impl nohash_hasher::IsEnabled for FileId {} -/// Storage for all files read by rust-analyzer. +/// Storage for all file changes and the file id to path mapping. /// /// For more information see the [crate-level](crate) documentation. #[derive(Default)] pub struct Vfs { interner: PathInterner, - data: Vec>>, + data: Vec, changes: Vec, } +#[derive(Copy, PartialEq, PartialOrd, Clone)] +pub enum FileState { + Exists, + Deleted, +} + /// Changed file in the [`Vfs`]. #[derive(Debug)] pub struct ChangedFile { /// Id of the changed file pub file_id: FileId, /// Kind of change - pub change_kind: ChangeKind, + pub change: Change, } impl ChangedFile { /// Returns `true` if the change is not [`Delete`](ChangeKind::Delete). pub fn exists(&self) -> bool { - self.change_kind != ChangeKind::Delete + !matches!(self.change, Change::Delete) } /// Returns `true` if the change is [`Create`](ChangeKind::Create) or - /// [`Delete`](ChangeKind::Delete). + /// [`Delete`](Change::Delete). pub fn is_created_or_deleted(&self) -> bool { - matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete) + matches!(self.change, Change::Create(_) | Change::Delete) } + + pub fn kind(&self) -> ChangeKind { + match self.change { + Change::Create(_) => ChangeKind::Create, + Change::Modify(_) => ChangeKind::Modify, + Change::Delete => ChangeKind::Delete, + } + } +} + +/// Kind of [file change](ChangedFile). +#[derive(Eq, PartialEq, Debug)] +pub enum Change { + /// The file was (re-)created + Create(Vec), + /// The file was modified + Modify(Vec), + /// The file was deleted + Delete, } /// Kind of [file change](ChangedFile). -#[derive(Eq, PartialEq, Copy, Clone, Debug)] +#[derive(Eq, PartialEq, Debug)] pub enum ChangeKind { /// The file was (re-)created Create, @@ -130,7 +155,7 @@ pub enum ChangeKind { impl Vfs { /// Id of the given path if it exists in the `Vfs` and is not deleted. pub fn file_id(&self, path: &VfsPath) -> Option { - self.interner.get(path).filter(|&it| self.get(it).is_some()) + self.interner.get(path).filter(|&it| matches!(self.get(it), FileState::Exists)) } /// File path corresponding to the given `file_id`. @@ -142,28 +167,13 @@ impl Vfs { self.interner.lookup(file_id).clone() } - /// File content corresponding to the given `file_id`. - /// - /// # Panics - /// - /// Panics if the id is not present in the `Vfs`, or if the corresponding file is - /// deleted. - pub fn file_contents(&self, file_id: FileId) -> &[u8] { - self.get(file_id).as_deref().unwrap() - } - - /// Returns the overall memory usage for the stored files. - pub fn memory_usage(&self) -> usize { - self.data.iter().flatten().map(|d| d.capacity()).sum() - } - /// Returns an iterator over the stored ids and their corresponding paths. /// /// This will skip deleted files. pub fn iter(&self) -> impl Iterator + '_ { (0..self.data.len()) .map(|it| FileId(it as u32)) - .filter(move |&file_id| self.get(file_id).is_some()) + .filter(move |&file_id| matches!(self.get(file_id), FileState::Exists)) .map(move |file_id| { let path = self.interner.lookup(file_id); (file_id, path) @@ -176,28 +186,21 @@ impl Vfs { /// /// If the path does not currently exists in the `Vfs`, allocates a new /// [`FileId`] for it. - pub fn set_file_contents(&mut self, path: VfsPath, mut contents: Option>) -> bool { + pub fn set_file_contents(&mut self, path: VfsPath, contents: Option>) -> bool { let file_id = self.alloc_file_id(path); - let change_kind = match (self.get(file_id), &contents) { - (None, None) => return false, - (Some(old), Some(new)) if old == new => return false, - (None, Some(_)) => ChangeKind::Create, - (Some(_), None) => ChangeKind::Delete, - (Some(_), Some(_)) => ChangeKind::Modify, + let change_kind = match (self.get(file_id), contents) { + (FileState::Deleted, None) => return false, + (FileState::Deleted, Some(v)) => Change::Create(v), + (FileState::Exists, None) => Change::Delete, + (FileState::Exists, Some(v)) => Change::Modify(v), }; - if let Some(contents) = &mut contents { - contents.shrink_to_fit(); - } - *self.get_mut(file_id) = contents; - self.changes.push(ChangedFile { file_id, change_kind }); + let changed_file = ChangedFile { file_id, change: change_kind }; + self.data[file_id.0 as usize] = + if changed_file.exists() { FileState::Exists } else { FileState::Deleted }; + self.changes.push(changed_file); true } - /// Returns `true` if the `Vfs` contains [changes](ChangedFile). - pub fn has_changes(&self) -> bool { - !self.changes.is_empty() - } - /// Drain and returns all the changes in the `Vfs`. pub fn take_changes(&mut self) -> Vec { mem::take(&mut self.changes) @@ -205,7 +208,7 @@ impl Vfs { /// Provides a panic-less way to verify file_id validity. pub fn exists(&self, file_id: FileId) -> bool { - self.get(file_id).is_some() + matches!(self.get(file_id), FileState::Exists) } /// Returns the id associated with `path` @@ -219,26 +222,17 @@ impl Vfs { let file_id = self.interner.intern(path); let idx = file_id.0 as usize; let len = self.data.len().max(idx + 1); - self.data.resize_with(len, || None); + self.data.resize(len, FileState::Deleted); file_id } - /// Returns the content associated with the given `file_id`. - /// - /// # Panics - /// - /// Panics if no file is associated to that id. - fn get(&self, file_id: FileId) -> &Option> { - &self.data[file_id.0 as usize] - } - - /// Mutably returns the content associated with the given `file_id`. + /// Returns the status of the file associated with the given `file_id`. /// /// # Panics /// /// Panics if no file is associated to that id. - fn get_mut(&mut self, file_id: FileId) -> &mut Option> { - &mut self.data[file_id.0 as usize] + fn get(&self, file_id: FileId) -> FileState { + self.data[file_id.0 as usize] } } diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs index e2d74782ae526..89a544c81d8c9 100644 --- a/crates/vfs/src/loader.rs +++ b/crates/vfs/src/loader.rs @@ -51,6 +51,8 @@ pub enum Message { Progress { n_total: usize, n_done: usize, config_version: u32 }, /// The handle loaded the following files' content. Loaded { files: Vec<(AbsPathBuf, Option>)> }, + /// The handle loaded the following files' content. + Changed { files: Vec<(AbsPathBuf, Option>)> }, } /// Type that will receive [`Messages`](Message) from a [`Handle`]. @@ -199,6 +201,9 @@ impl fmt::Debug for Message { Message::Loaded { files } => { f.debug_struct("Loaded").field("n_files", &files.len()).finish() } + Message::Changed { files } => { + f.debug_struct("Changed").field("n_files", &files.len()).finish() + } Message::Progress { n_total, n_done, config_version } => f .debug_struct("Progress") .field("n_total", n_total) diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md index 97e376787c828..fd6f220f4fa90 100644 --- a/docs/dev/syntax.md +++ b/docs/dev/syntax.md @@ -41,7 +41,6 @@ Syntax trees are a semi-transient data structure. In general, frontend does not keep syntax trees for all files in memory. Instead, it *lowers* syntax trees to more compact and rigid representation, which is not full-fidelity, but which can be mapped back to a syntax tree if so desired. - ### GreenNode GreenNode is a purely-functional tree with arbitrary arity. Conceptually, it is equivalent to the following run of the mill struct: @@ -500,7 +499,7 @@ Specifically, `TreeSink` constructs the tree in lockstep with draining the origi In the process, it records which tokens of the tree correspond to which tokens of the input, by using text ranges to identify syntax tokens. The end result is that parsing an expanded code yields a syntax tree and a mapping of text-ranges of the tree to original tokens. -To deal with precedence in cases like `$expr * 1`, we use special invisible parenthesis, which are explicitly handled by the parser +To deal with precedence in cases like `$expr * 1`, we use special invisible parenthesis, which are explicitly handled by the parser. ### Whitespace & Comments diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 8b9d5d4a74619..291cef926f82e 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -2290,9 +2290,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "dev": true, "funding": [ { From 2d72ec71ec454fed5812a749683a3114ee4c0af5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jan 2024 10:58:05 +0100 Subject: [PATCH 018/118] Deduplicate --- crates/hir-def/src/body/pretty.rs | 30 +++++------ crates/hir-def/src/child_by_source.rs | 7 ++- crates/hir-def/src/data/adt.rs | 10 ++-- crates/hir-def/src/db.rs | 2 +- crates/hir-def/src/dyn_map/keys.rs | 2 +- crates/hir-def/src/item_scope.rs | 7 +-- crates/hir-def/src/item_tree.rs | 7 +++ crates/hir-def/src/lang_item.rs | 2 +- crates/hir-def/src/nameres.rs | 8 ++- crates/hir-def/src/nameres/collector.rs | 40 +++++--------- crates/hir-def/src/nameres/path_resolution.rs | 53 +++++++------------ crates/hir/src/lib.rs | 7 ++- crates/hir/src/semantics/source_to_def.rs | 2 +- 13 files changed, 82 insertions(+), 95 deletions(-) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 601878e8e79ca..a389a283b593f 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -18,27 +18,21 @@ use super::*; pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String { let header = match owner { DefWithBodyId::FunctionId(it) => { - let item_tree_id = it.lookup(db).id; - format!( - "fn {}", - item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast()) - ) + it.lookup(db).id.resolved(db, |it| format!("fn {} = ", it.name.display(db.upcast()))) } - DefWithBodyId::StaticId(it) => { - let item_tree_id = it.lookup(db).id; + DefWithBodyId::StaticId(it) => it + .lookup(db) + .id + .resolved(db, |it| format!("static {} = ", it.name.display(db.upcast()))), + DefWithBodyId::ConstId(it) => it.lookup(db).id.resolved(db, |it| { format!( - "static {} = ", - item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast()) + "const {} = ", + match &it.name { + Some(name) => name.display(db.upcast()).to_string(), + None => "_".to_string(), + } ) - } - DefWithBodyId::ConstId(it) => { - let item_tree_id = it.lookup(db).id; - let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name { - Some(name) => name.display(db.upcast()).to_string(), - None => "_".to_string(), - }; - format!("const {name} = ") - } + }), DefWithBodyId::InTypeConstId(_) => format!("In type const = "), DefWithBodyId::VariantId(it) => { let loc = it.lookup(db); diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs index e8a771141effb..b3bb3355f1234 100644 --- a/crates/hir-def/src/child_by_source.rs +++ b/crates/hir-def/src/child_by_source.rs @@ -204,15 +204,18 @@ impl ChildBySource for VariantId { } impl ChildBySource for EnumId { - fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) { + fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) { let loc = &self.lookup(db); + if file_id != loc.id.file_id() { + return; + } let tree = loc.id.item_tree(db); let ast_id_map = db.ast_id_map(loc.id.file_id()); let root = db.parse_or_expand(loc.id.file_id()); db.enum_data(*self).variants.iter().for_each(|&(variant, _)| { - res[keys::VARIANT].insert( + res[keys::ENUM_VARIANT].insert( ast_id_map.get(tree[variant.lookup(db).id.value].ast_id).to_node(&root), variant, ); diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 93a64dc6ec6fb..942fafe329efa 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -298,7 +298,7 @@ impl EnumData { Arc::new(EnumData { name: enum_.name.clone(), - variants: loc.container.def_map(db)[loc.container.local_id].scope.enums[&e] + variants: loc.container.def_map(db).enum_definitions[&e] .iter() .map(|&id| (id, item_tree[id.lookup(db).id.value].name.clone())) .collect(), @@ -332,7 +332,7 @@ impl EnumVariantData { pub(crate) fn enum_variant_data_with_diagnostics_query( db: &dyn DefDatabase, e: EnumVariantId, - ) -> (Arc, Arc<[DefDiagnostic]>) { + ) -> (Arc, Option>>) { let loc = e.lookup(db); let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); @@ -355,7 +355,11 @@ impl EnumVariantData { name: variant.name.clone(), variant_data: Arc::new(var_data), }), - field_diagnostics.into(), + if field_diagnostics.is_empty() { + None + } else { + Some(Arc::new(field_diagnostics.into_boxed_slice())) + }, ) } } diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 4201b1dd17435..2b1d0bca283be 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -140,7 +140,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast (Arc, Arc<[DefDiagnostic]>); + ) -> (Arc, Option>>); #[salsa::invoke(ImplData::impl_data_query)] fn impl_data(&self, e: ImplId) -> Arc; diff --git a/crates/hir-def/src/dyn_map/keys.rs b/crates/hir-def/src/dyn_map/keys.rs index d0f2bfab43223..60832f59eb9c5 100644 --- a/crates/hir-def/src/dyn_map/keys.rs +++ b/crates/hir-def/src/dyn_map/keys.rs @@ -28,7 +28,7 @@ pub const ENUM: Key = Key::new(); pub const EXTERN_CRATE: Key = Key::new(); pub const USE: Key = Key::new(); -pub const VARIANT: Key = Key::new(); +pub const ENUM_VARIANT: Key = Key::new(); pub const TUPLE_FIELD: Key = Key::new(); pub const RECORD_FIELD: Key = Key::new(); pub const TYPE_PARAM: Key = Key::new(); diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 436bc0c98c2ad..168ee4acffbef 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -18,8 +18,8 @@ use crate::{ db::DefDatabase, per_ns::PerNs, visibility::{Visibility, VisibilityExplicity}, - AdtId, BuiltinType, ConstId, EnumId, EnumVariantId, ExternCrateId, HasModule, ImplId, - LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId, + AdtId, BuiltinType, ConstId, ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, UseId, }; #[derive(Debug, Default)] @@ -79,7 +79,6 @@ pub struct ItemScope { /// declared. declarations: Vec, - pub enums: FxHashMap>, impls: Vec, unnamed_consts: Vec, /// Traits imported via `use Trait as _;`. @@ -719,7 +718,6 @@ impl ItemScope { use_imports_types, use_imports_macros, macro_invocations, - enums, } = self; types.shrink_to_fit(); values.shrink_to_fit(); @@ -738,7 +736,6 @@ impl ItemScope { extern_crate_decls.shrink_to_fit(); use_decls.shrink_to_fit(); macro_invocations.shrink_to_fit(); - enums.shrink_to_fit(); } } diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 0f81bef500643..173aa817b0d53 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -445,6 +445,13 @@ impl ItemTreeId { pub fn item_tree(self, db: &dyn DefDatabase) -> Arc { self.tree.item_tree(db) } + + pub fn resolved(self, db: &dyn DefDatabase, cb: impl FnOnce(&N) -> R) -> R + where + ItemTree: Index, Output = N>, + { + cb(&self.tree.item_tree(db)[self.value]) + } } impl Copy for ItemTreeId {} diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index fc2edce93da71..bd2d804e4b380 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -125,7 +125,7 @@ impl LangItems { } ModuleDefId::AdtId(AdtId::EnumId(e)) => { lang_items.collect_lang_item(db, e, LangItemTarget::EnumId); - module_data.scope.enums[&e].iter().for_each(|&id| { + crate_def_map.enum_definitions[&e].iter().for_each(|&id| { lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant); }); } diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 53644f58efc44..7eb2f3adddbf9 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -80,8 +80,8 @@ use crate::{ path::ModPath, per_ns::PerNs, visibility::{Visibility, VisibilityExplicity}, - AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup, - MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, + AstId, BlockId, BlockLoc, CrateRootModuleId, EnumId, EnumVariantId, ExternCrateId, FunctionId, + LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; /// Contains the results of (early) name resolution. @@ -113,6 +113,7 @@ pub struct DefMap { /// this contains all kinds of macro, not just `macro_rules!` macro. /// ExternCrateId being None implies it being imported from the general prelude import. macro_use_prelude: FxHashMap)>, + pub(crate) enum_definitions: FxHashMap>, /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. @@ -370,6 +371,7 @@ impl DefMap { macro_use_prelude: FxHashMap::default(), derive_helpers_in_scope: FxHashMap::default(), diagnostics: Vec::new(), + enum_definitions: FxHashMap::default(), data: Arc::new(DefMapCrateData { extern_prelude: FxHashMap::default(), exported_derives: FxHashMap::default(), @@ -612,12 +614,14 @@ impl DefMap { krate: _, prelude: _, data: _, + enum_definitions, } = self; macro_use_prelude.shrink_to_fit(); diagnostics.shrink_to_fit(); modules.shrink_to_fit(); derive_helpers_in_scope.shrink_to_fit(); + enum_definitions.shrink_to_fit(); for (_, module) in modules.iter_mut() { module.children.shrink_to_fit(); module.scope.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index fc15a77e8c349..760f811ede6a0 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -980,35 +980,26 @@ impl DefCollector<'_> { cov_mark::hit!(glob_enum); // glob import from enum => just import all the variants - // We need to check if the def map the enum is from is us, then we can't + // We need to check if the def map the enum is from is us, if it is we can't // call the def-map query since we are currently constructing it! let loc = e.lookup(self.db); let tree = loc.id.item_tree(self.db); let current_def_map = self.def_map.krate == loc.container.krate && self.def_map.block_id() == loc.container.block; + let def_map; let resolutions = if current_def_map { - self.def_map.modules[loc.container.local_id].scope.enums[&e] - .iter() - .map(|&variant| { - let name = tree[variant.lookup(self.db).id.value].name.clone(); - let res = - PerNs::both(variant.into(), variant.into(), vis, None); - (Some(name), res) - }) - .collect::>() + &self.def_map.enum_definitions[&e] } else { - loc.container.def_map(self.db).modules[loc.container.local_id] - .scope - .enums[&e] - .iter() - .map(|&variant| { - let name = tree[variant.lookup(self.db).id.value].name.clone(); - let res = - PerNs::both(variant.into(), variant.into(), vis, None); - (Some(name), res) - }) - .collect::>() - }; + def_map = loc.container.def_map(self.db); + &def_map.enum_definitions[&e] + } + .iter() + .map(|&variant| { + let name = tree[variant.lookup(self.db).id.value].name.clone(); + let res = PerNs::both(variant.into(), variant.into(), vis, None); + (Some(name), res) + }) + .collect::>(); self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); } Some(d) => { @@ -1749,10 +1740,7 @@ impl ModCollector<'_, '_> { ) }) .collect(); - self.def_collector.def_map.modules[module_id] - .scope - .enums - .insert(enum_, variants); + self.def_collector.def_map.enum_definitions.insert(enum_, variants); } ModItem::Const(id) => { let it = &self.item_tree[id]; diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 372d148021841..7300a0628f572 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -360,41 +360,28 @@ impl DefMap { let tree = loc.id.item_tree(db); let current_def_map = self.krate == loc.container.krate && self.block_id() == loc.container.block; + let def_map; let res = if current_def_map { - self.modules[loc.container.local_id].scope.enums[&e].iter().find_map( - |&variant| { - let variant_data = &tree[variant.lookup(db).id.value]; - (variant_data.name == *segment).then(|| match variant_data.fields { - Fields::Record(_) => { - PerNs::types(variant.into(), Visibility::Public, None) - } - Fields::Tuple(_) | Fields::Unit => PerNs::both( - variant.into(), - variant.into(), - Visibility::Public, - None, - ), - }) - }, - ) + &self.enum_definitions[&e] } else { - loc.container.def_map(db).modules[loc.container.local_id].scope.enums[&e] - .iter() - .find_map(|&variant| { - let variant_data = &tree[variant.lookup(db).id.value]; - (variant_data.name == *segment).then(|| match variant_data.fields { - Fields::Record(_) => { - PerNs::types(variant.into(), Visibility::Public, None) - } - Fields::Tuple(_) | Fields::Unit => PerNs::both( - variant.into(), - variant.into(), - Visibility::Public, - None, - ), - }) - }) - }; + def_map = loc.container.def_map(db); + &def_map.enum_definitions[&e] + } + .iter() + .find_map(|&variant| { + let variant_data = &tree[variant.lookup(db).id.value]; + (variant_data.name == *segment).then(|| match variant_data.fields { + Fields::Record(_) => { + PerNs::types(variant.into(), Visibility::Public, None) + } + Fields::Tuple(_) | Fields::Unit => PerNs::both( + variant.into(), + variant.into(), + Visibility::Public, + None, + ), + }) + }); match res { Some(res) => res, None => { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 246fc231b47f5..5e133bf5c758d 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -584,8 +584,11 @@ impl Module { Adt::Enum(e) => { for v in e.variants(db) { acc.extend(ModuleDef::Variant(v).diagnostics(db)); - for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() { - emit_def_diagnostic(db, acc, diag); + if let Some(diags) = &db.enum_variant_data_with_diagnostics(v.id).1 + { + for diag in &***diags { + emit_def_diagnostic(db, acc, diag); + } } } } diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index df8c1e904fe89..cb04f98911ebd 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -201,7 +201,7 @@ impl SourceToDefCtx<'_, '_> { &mut self, src: InFile, ) -> Option { - self.to_def(src, keys::VARIANT) + self.to_def(src, keys::ENUM_VARIANT) } pub(super) fn extern_crate_to_def( &mut self, From 1669344b2a4168afdebc10fba34b9a0c63a29e72 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jan 2024 11:07:26 +0100 Subject: [PATCH 019/118] Thinner DefDiagnostics --- crates/hir-def/src/data.rs | 10 +++++----- crates/hir-def/src/data/adt.rs | 20 ++++++++------------ crates/hir-def/src/db.rs | 13 ++++++------- crates/hir-def/src/nameres/diagnostics.rs | 17 +++++++++++++++++ crates/hir-ty/src/mir/lower.rs | 1 + crates/hir/src/lib.rs | 7 ++----- 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 9c183c9332ba8..a42d6bb33789a 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -19,7 +19,7 @@ use crate::{ macro_call_as_call_id, nameres::{ attr_resolution::ResolvedAttr, - diagnostics::DefDiagnostic, + diagnostics::{DefDiagnostic, DefDiagnostics}, proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind}, DefMap, MacroSubNs, }, @@ -240,7 +240,7 @@ impl TraitData { pub(crate) fn trait_data_with_diagnostics_query( db: &dyn DefDatabase, tr: TraitId, - ) -> (Arc, Arc<[DefDiagnostic]>) { + ) -> (Arc, DefDiagnostics) { let tr_loc @ ItemLoc { container: module_id, id: tree_id } = tr.lookup(db); let item_tree = tree_id.item_tree(db); let tr_def = &item_tree[tree_id.value]; @@ -274,7 +274,7 @@ impl TraitData { rustc_has_incoherent_inherent_impls, fundamental, }), - diagnostics.into(), + DefDiagnostics::new(diagnostics), ) } @@ -340,7 +340,7 @@ impl ImplData { pub(crate) fn impl_data_with_diagnostics_query( db: &dyn DefDatabase, id: ImplId, - ) -> (Arc, Arc<[DefDiagnostic]>) { + ) -> (Arc, DefDiagnostics) { let _p = profile::span("impl_data_with_diagnostics_query"); let ItemLoc { container: module_id, id: tree_id } = id.lookup(db); @@ -367,7 +367,7 @@ impl ImplData { is_unsafe, attribute_calls, }), - diagnostics.into(), + DefDiagnostics::new(diagnostics), ) } diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 942fafe329efa..6e302aea653de 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -21,7 +21,7 @@ use crate::{ item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, lang_item::LangItem, lower::LowerCtx, - nameres::diagnostics::DefDiagnostic, + nameres::diagnostics::{DefDiagnostic, DefDiagnostics}, src::HasChildSource, src::HasSource, trace::Trace, @@ -187,7 +187,7 @@ impl StructData { pub(crate) fn struct_data_with_diagnostics_query( db: &dyn DefDatabase, id: StructId, - ) -> (Arc, Arc<[DefDiagnostic]>) { + ) -> (Arc, DefDiagnostics) { let loc = id.lookup(db); let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); @@ -232,7 +232,7 @@ impl StructData { visibility: item_tree[strukt.visibility].clone(), flags, }), - diagnostics.into(), + DefDiagnostics::new(diagnostics), ) } @@ -243,7 +243,7 @@ impl StructData { pub(crate) fn union_data_with_diagnostics_query( db: &dyn DefDatabase, id: UnionId, - ) -> (Arc, Arc<[DefDiagnostic]>) { + ) -> (Arc, DefDiagnostics) { let loc = id.lookup(db); let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); @@ -278,7 +278,7 @@ impl StructData { visibility: item_tree[union.visibility].clone(), flags, }), - diagnostics.into(), + DefDiagnostics::new(diagnostics), ) } } @@ -332,14 +332,14 @@ impl EnumVariantData { pub(crate) fn enum_variant_data_with_diagnostics_query( db: &dyn DefDatabase, e: EnumVariantId, - ) -> (Arc, Option>>) { + ) -> (Arc, DefDiagnostics) { let loc = e.lookup(db); let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); let cfg_options = db.crate_graph()[krate].cfg_options.clone(); let variant = &item_tree[loc.id.value]; - let (var_data, field_diagnostics) = lower_fields( + let (var_data, diagnostics) = lower_fields( db, krate, loc.id.file_id(), @@ -355,11 +355,7 @@ impl EnumVariantData { name: variant.name.clone(), variant_data: Arc::new(var_data), }), - if field_diagnostics.is_empty() { - None - } else { - Some(Arc::new(field_diagnostics.into_boxed_slice())) - }, + DefDiagnostics::new(diagnostics), ) } } diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 2b1d0bca283be..186a8cf2d9ff5 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -19,7 +19,7 @@ use crate::{ import_map::ImportMap, item_tree::{AttrOwner, ItemTree}, lang_item::{self, LangItem, LangItemTarget, LangItems}, - nameres::{diagnostics::DefDiagnostic, DefMap}, + nameres::{diagnostics::DefDiagnostics, DefMap}, visibility::{self, Visibility}, AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, @@ -121,14 +121,13 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; #[salsa::invoke(StructData::struct_data_with_diagnostics_query)] - fn struct_data_with_diagnostics(&self, id: StructId) - -> (Arc, Arc<[DefDiagnostic]>); + fn struct_data_with_diagnostics(&self, id: StructId) -> (Arc, DefDiagnostics); #[salsa::invoke(StructData::union_data_query)] fn union_data(&self, id: UnionId) -> Arc; #[salsa::invoke(StructData::union_data_with_diagnostics_query)] - fn union_data_with_diagnostics(&self, id: UnionId) -> (Arc, Arc<[DefDiagnostic]>); + fn union_data_with_diagnostics(&self, id: UnionId) -> (Arc, DefDiagnostics); #[salsa::invoke(EnumData::enum_data_query)] fn enum_data(&self, e: EnumId) -> Arc; @@ -140,19 +139,19 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast (Arc, Option>>); + ) -> (Arc, DefDiagnostics); #[salsa::invoke(ImplData::impl_data_query)] fn impl_data(&self, e: ImplId) -> Arc; #[salsa::invoke(ImplData::impl_data_with_diagnostics_query)] - fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc, Arc<[DefDiagnostic]>); + fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc, DefDiagnostics); #[salsa::invoke(TraitData::trait_data_query)] fn trait_data(&self, e: TraitId) -> Arc; #[salsa::invoke(TraitData::trait_data_with_diagnostics_query)] - fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc, Arc<[DefDiagnostic]>); + fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc, DefDiagnostics); #[salsa::invoke(TraitAliasData::trait_alias_query)] fn trait_alias_data(&self, e: TraitAliasId) -> Arc; diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs index 9cffb3c9f37fe..0a3f7bf7ec3d6 100644 --- a/crates/hir-def/src/nameres/diagnostics.rs +++ b/crates/hir-def/src/nameres/diagnostics.rs @@ -40,6 +40,23 @@ pub enum DefDiagnosticKind { MacroDefError { ast: AstId, message: String }, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DefDiagnostics(Option>>); + +impl DefDiagnostics { + pub fn new(diagnostics: Vec) -> Self { + Self(if diagnostics.is_empty() { + None + } else { + Some(triomphe::Arc::new(diagnostics.into_boxed_slice())) + }) + } + + pub fn iter(&self) -> impl Iterator { + self.0.as_ref().into_iter().flat_map(|it| &***it) + } +} + #[derive(Debug, PartialEq, Eq)] pub struct DefDiagnostic { pub in_module: LocalModuleId, diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 69d8046524b43..749ca43fba7fc 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -69,6 +69,7 @@ struct MirLowerCtx<'a> { drop_scopes: Vec, } +// FIXME: Make this smaller, its stored in database queries #[derive(Debug, Clone, PartialEq, Eq)] pub enum MirLowerError { ConstEvalError(Box, Box), diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5e133bf5c758d..246fc231b47f5 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -584,11 +584,8 @@ impl Module { Adt::Enum(e) => { for v in e.variants(db) { acc.extend(ModuleDef::Variant(v).diagnostics(db)); - if let Some(diags) = &db.enum_variant_data_with_diagnostics(v.id).1 - { - for diag in &***diags { - emit_def_diagnostic(db, acc, diag); - } + for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() { + emit_def_diagnostic(db, acc, diag); } } } From 180e9b2bbfa39c2c00cfa944d9823cae4867d651 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 15 Jan 2024 12:03:31 +0100 Subject: [PATCH 020/118] Cleanup --- crates/hir-def/src/attr.rs | 13 ++- crates/hir-def/src/body/pretty.rs | 2 +- crates/hir-def/src/data/adt.rs | 13 +-- crates/hir-def/src/item_tree.rs | 6 +- crates/hir-def/src/item_tree/lower.rs | 2 +- crates/hir-def/src/lib.rs | 63 ++++++---- crates/hir-def/src/nameres/collector.rs | 109 +++++++++++------- crates/hir-def/src/nameres/path_resolution.rs | 2 +- crates/hir-def/src/src.rs | 6 +- crates/hir-def/src/trace.rs | 4 + crates/hir-def/src/visibility.rs | 6 +- crates/hir-ty/src/consteval.rs | 10 +- crates/hir-ty/src/inhabitedness.rs | 4 +- crates/hir-ty/src/lower.rs | 53 +++++---- crates/hir-ty/src/mir/eval.rs | 26 +---- crates/hir/src/lib.rs | 18 +-- crates/syntax/src/ast/node_ext.rs | 6 + 17 files changed, 175 insertions(+), 168 deletions(-) diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index be69d6f1351ac..8fbfcc81d2800 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -24,7 +24,7 @@ use triomphe::Arc; use crate::{ db::DefDatabase, - item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode}, + item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeModItemNode}, lang_item::LangItem, nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource}, @@ -82,7 +82,7 @@ impl Attrs { let (fields, item_tree, krate) = match v { VariantId::EnumVariantId(it) => { let loc = it.lookup(db); - let krate = loc.container.krate; + let krate = loc.parent.lookup(db).container.krate; let item_tree = loc.id.item_tree(db); let variant = &item_tree[loc.id.value]; (variant.fields.clone(), item_tree, krate) @@ -606,13 +606,16 @@ fn any_has_attrs<'db>( id.lookup(db).source(db).map(ast::AnyHasAttrs::new) } -fn attrs_from_item_tree(db: &dyn DefDatabase, id: ItemTreeId) -> RawAttrs { +fn attrs_from_item_tree( + db: &dyn DefDatabase, + id: ItemTreeId, +) -> RawAttrs { let tree = id.item_tree(db); let mod_item = N::id_to_mod_item(id.value); tree.raw_attrs(mod_item.into()).clone() } -fn attrs_from_item_tree_loc<'db, N: ItemTreeNode>( +fn attrs_from_item_tree_loc<'db, N: ItemTreeModItemNode>( db: &(dyn DefDatabase + 'db), lookup: impl Lookup = dyn DefDatabase + 'db, Data = ItemLoc>, ) -> RawAttrs { @@ -620,7 +623,7 @@ fn attrs_from_item_tree_loc<'db, N: ItemTreeNode>( attrs_from_item_tree(db, id) } -fn attrs_from_item_tree_assoc<'db, N: ItemTreeNode>( +fn attrs_from_item_tree_assoc<'db, N: ItemTreeModItemNode>( db: &(dyn DefDatabase + 'db), lookup: impl Lookup = dyn DefDatabase + 'db, Data = AssocItemLoc>, ) -> RawAttrs { diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index a389a283b593f..deb87f87d5e7c 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -18,7 +18,7 @@ use super::*; pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String { let header = match owner { DefWithBodyId::FunctionId(it) => { - it.lookup(db).id.resolved(db, |it| format!("fn {} = ", it.name.display(db.upcast()))) + it.lookup(db).id.resolved(db, |it| format!("fn {}", it.name.display(db.upcast()))) } DefWithBodyId::StaticId(it) => it .lookup(db) diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 6e302aea653de..5d44371521397 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -334,7 +334,8 @@ impl EnumVariantData { e: EnumVariantId, ) -> (Arc, DefDiagnostics) { let loc = e.lookup(db); - let krate = loc.container.krate; + let container = loc.parent.lookup(db).container; + let krate = container.krate; let item_tree = loc.id.item_tree(db); let cfg_options = db.crate_graph()[krate].cfg_options.clone(); let variant = &item_tree[loc.id.value]; @@ -343,7 +344,7 @@ impl EnumVariantData { db, krate, loc.id.file_id(), - loc.container.local_id, + container.local_id, &item_tree, &cfg_options, &variant.fields, @@ -395,7 +396,7 @@ impl HasChildSource for VariantId { ( lookup.source(db).map(|it| it.kind()), &item_tree[lookup.id.value].fields, - lookup.container, + lookup.parent.lookup(db).container, ) } VariantId::StructId(it) => { @@ -411,11 +412,7 @@ impl HasChildSource for VariantId { let lookup = it.lookup(db); item_tree = lookup.id.item_tree(db); ( - lookup.source(db).map(|it| { - it.record_field_list() - .map(ast::StructKind::Record) - .unwrap_or(ast::StructKind::Unit) - }), + lookup.source(db).map(|it| it.kind()), &item_tree[lookup.id.value].fields, lookup.container, ) diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 173aa817b0d53..c37cf52155102 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -337,7 +337,7 @@ from_attrs!( ); /// Trait implemented by all item nodes in the item tree. -pub trait ItemTreeNode: Clone { +pub trait ItemTreeModItemNode: Clone { type Source: AstIdNode + Into; fn ast_id(&self) -> FileAstId; @@ -494,7 +494,7 @@ macro_rules! mod_items { )+ $( - impl ItemTreeNode for $typ { + impl ItemTreeModItemNode for $typ { type Source = $ast; fn ast_id(&self) -> FileAstId { @@ -577,7 +577,7 @@ impl Index for ItemTree { } } -impl Index> for ItemTree { +impl Index> for ItemTree { type Output = N; fn index(&self, id: FileItemTreeId) -> &N { N::lookup(self, id.index()) diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 8d19b5000cf4d..f6086ed6d9b24 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -13,7 +13,7 @@ use crate::{ use super::*; -fn id(index: Idx) -> FileItemTreeId { +fn id(index: Idx) -> FileItemTreeId { FileItemTreeId(index) } diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 1712eaede0920..bcd1f37bec4a3 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -99,8 +99,8 @@ use crate::{ data::adt::VariantData, db::DefDatabase, item_tree::{ - Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules, - Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant, + Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeModItemNode, Macro2, + MacroRules, Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant, }, }; @@ -213,28 +213,28 @@ impl ModuleId { pub type LocalModuleId = Idx; #[derive(Debug)] -pub struct ItemLoc { +pub struct ItemLoc { pub container: ModuleId, pub id: ItemTreeId, } -impl Clone for ItemLoc { +impl Clone for ItemLoc { fn clone(&self) -> Self { Self { container: self.container, id: self.id } } } -impl Copy for ItemLoc {} +impl Copy for ItemLoc {} -impl PartialEq for ItemLoc { +impl PartialEq for ItemLoc { fn eq(&self, other: &Self) -> bool { self.container == other.container && self.id == other.id } } -impl Eq for ItemLoc {} +impl Eq for ItemLoc {} -impl Hash for ItemLoc { +impl Hash for ItemLoc { fn hash(&self, state: &mut H) { self.container.hash(state); self.id.hash(state); @@ -242,28 +242,28 @@ impl Hash for ItemLoc { } #[derive(Debug)] -pub struct AssocItemLoc { +pub struct AssocItemLoc { pub container: ItemContainerId, pub id: ItemTreeId, } -impl Clone for AssocItemLoc { +impl Clone for AssocItemLoc { fn clone(&self) -> Self { Self { container: self.container, id: self.id } } } -impl Copy for AssocItemLoc {} +impl Copy for AssocItemLoc {} -impl PartialEq for AssocItemLoc { +impl PartialEq for AssocItemLoc { fn eq(&self, other: &Self) -> bool { self.container == other.container && self.id == other.id } } -impl Eq for AssocItemLoc {} +impl Eq for AssocItemLoc {} -impl Hash for AssocItemLoc { +impl Hash for AssocItemLoc { fn hash(&self, state: &mut H) { self.container.hash(state); self.id.hash(state); @@ -297,15 +297,14 @@ pub struct EnumId(salsa::InternId); pub type EnumLoc = ItemLoc; impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum); -// FIXME: rename to `VariantId`, only enums can ave variants #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariantId(salsa::InternId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariantLoc { - pub container: ModuleId, pub id: ItemTreeId, pub parent: EnumId, + pub index: u32, } impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant); @@ -992,7 +991,8 @@ impl HasModule for ItemContainerId { } } -impl HasModule for AssocItemLoc { +impl HasModule for AssocItemLoc { + #[inline] fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.container.module(db) } @@ -1008,7 +1008,22 @@ impl HasModule for AdtId { } } +impl HasModule for EnumId { + #[inline] + fn module(&self, db: &dyn DefDatabase) -> ModuleId { + self.lookup(db).container + } +} + +impl HasModule for EnumVariantId { + #[inline] + fn module(&self, db: &dyn DefDatabase) -> ModuleId { + self.lookup(db).parent.module(db) + } +} + impl HasModule for ExternCrateId { + #[inline] fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.lookup(db).container } @@ -1017,7 +1032,7 @@ impl HasModule for ExternCrateId { impl HasModule for VariantId { fn module(&self, db: &dyn DefDatabase) -> ModuleId { match self { - VariantId::EnumVariantId(it) => it.lookup(db).container, + VariantId::EnumVariantId(it) => it.lookup(db).parent.module(db), VariantId::StructId(it) => it.lookup(db).container, VariantId::UnionId(it) => it.lookup(db).container, } @@ -1046,7 +1061,7 @@ impl HasModule for TypeOwnerId { TypeOwnerId::TraitAliasId(it) => it.lookup(db).container, TypeOwnerId::TypeAliasId(it) => it.lookup(db).module(db), TypeOwnerId::ImplId(it) => it.lookup(db).container, - TypeOwnerId::EnumVariantId(it) => it.lookup(db).container, + TypeOwnerId::EnumVariantId(it) => it.lookup(db).parent.module(db), } } } @@ -1057,7 +1072,7 @@ impl HasModule for DefWithBodyId { DefWithBodyId::FunctionId(it) => it.lookup(db).module(db), DefWithBodyId::StaticId(it) => it.lookup(db).module(db), DefWithBodyId::ConstId(it) => it.lookup(db).module(db), - DefWithBodyId::VariantId(it) => it.lookup(db).container, + DefWithBodyId::VariantId(it) => it.lookup(db).parent.module(db), DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db), } } @@ -1072,19 +1087,21 @@ impl HasModule for GenericDefId { GenericDefId::TraitAliasId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), GenericDefId::ImplId(it) => it.lookup(db).container, - GenericDefId::EnumVariantId(it) => it.lookup(db).container, + GenericDefId::EnumVariantId(it) => it.lookup(db).parent.lookup(db).container, GenericDefId::ConstId(it) => it.lookup(db).module(db), } } } impl HasModule for TypeAliasId { + #[inline] fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.lookup(db).module(db) } } impl HasModule for TraitId { + #[inline] fn module(&self, db: &dyn DefDatabase) -> ModuleId { self.lookup(db).container } @@ -1099,7 +1116,7 @@ impl ModuleDefId { ModuleDefId::ModuleId(id) => *id, ModuleDefId::FunctionId(id) => id.lookup(db).module(db), ModuleDefId::AdtId(id) => id.module(db), - ModuleDefId::EnumVariantId(id) => id.lookup(db).container, + ModuleDefId::EnumVariantId(id) => id.lookup(db).parent.module(db), ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), ModuleDefId::StaticId(id) => id.lookup(db).module(db), ModuleDefId::TraitId(id) => id.lookup(db).container, @@ -1118,7 +1135,7 @@ impl AttrDefId { AttrDefId::FieldId(it) => it.parent.module(db).krate, AttrDefId::AdtId(it) => it.module(db).krate, AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, - AttrDefId::EnumVariantId(it) => it.lookup(db).container.krate, + AttrDefId::EnumVariantId(it) => it.lookup(db).parent.module(db).krate, AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, AttrDefId::TraitId(it) => it.lookup(db).container.krate, diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 760f811ede6a0..893ad47e241a9 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -3,7 +3,7 @@ //! `DefCollector::collect` contains the fixed-point iteration loop which //! resolves imports and expands macros. -use std::{cmp::Ordering, iter, mem}; +use std::{cmp::Ordering, iter, mem, ops::Not}; use base_db::{CrateId, Dependency, Edition, FileId}; use cfg::{CfgExpr, CfgOptions}; @@ -35,8 +35,8 @@ use crate::{ derive_macro_as_call_id, item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, item_tree::{ - self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, - Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, + self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, + ItemTreeModItemNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, }, macro_call_as_call_id, macro_call_as_call_id_with_eager, nameres::{ @@ -1579,7 +1579,10 @@ impl ModCollector<'_, '_> { let attrs = self.item_tree.attrs(db, krate, item.into()); if let Some(cfg) = attrs.cfg() { if !self.is_cfg_enabled(&cfg) { - self.emit_unconfigured_diagnostic(item.ast_id(self.item_tree).erase(), &cfg); + self.emit_unconfigured_diagnostic( + InFile::new(self.file_id(), item.ast_id(self.item_tree).erase()), + &cfg, + ); return; } } @@ -1717,27 +1720,37 @@ impl ModCollector<'_, '_> { let vis = resolve_vis(def_map, &self.item_tree[it.visibility]); update_def(self.def_collector, enum_.into(), &it.name, vis, false); + let mut index = 0; let variants = FileItemTreeId::range_iter(it.variants.clone()) .filter_map(|variant| { - let attrs = self.item_tree.attrs(db, krate, variant.into()); - if let Some(cfg) = attrs.cfg() { - if !self.is_cfg_enabled(&cfg) { + let is_enabled = self + .item_tree + .attrs(db, krate, variant.into()) + .cfg() + .and_then(|cfg| self.is_cfg_enabled(&cfg).not().then_some(cfg)) + .map_or(Ok(()), Err); + match is_enabled { + Err(cfg) => { self.emit_unconfigured_diagnostic( - self.item_tree[variant.index()].ast_id.erase(), + InFile::new( + self.file_id(), + self.item_tree[variant.index()].ast_id.erase(), + ), &cfg, ); - return None; + None } + Ok(()) => Some({ + let loc = EnumVariantLoc { + id: ItemTreeId::new(self.tree_id, variant), + parent: enum_, + index, + } + .intern(db); + index += 1; + loc + }), } - - Some( - EnumVariantLoc { - container: module, - id: ItemTreeId::new(self.tree_id, variant), - parent: enum_, - } - .intern(db), - ) }) .collect(); self.def_collector.def_map.enum_definitions.insert(enum_, variants); @@ -1927,31 +1940,40 @@ impl ModCollector<'_, '_> { let is_enabled = item_tree .top_level_attrs(db, krate) .cfg() - .map_or(true, |cfg| self.is_cfg_enabled(&cfg)); - if is_enabled { - let module_id = self.push_child_module( - module.name.clone(), - ast_id.value, - Some((file_id, is_mod_rs)), - &self.item_tree[module.visibility], - module_id, - ); - ModCollector { - def_collector: self.def_collector, - macro_depth: self.macro_depth, - module_id, - tree_id: TreeId::new(file_id.into(), None), - item_tree: &item_tree, - mod_dir, + .and_then(|cfg| self.is_cfg_enabled(&cfg).not().then_some(cfg)) + .map_or(Ok(()), Err); + match is_enabled { + Err(cfg) => { + self.emit_unconfigured_diagnostic( + ast_id.map(|it| it.erase()), + &cfg, + ); } - .collect_in_top_module(item_tree.top_level_items()); - let is_macro_use = is_macro_use - || item_tree - .top_level_attrs(db, krate) - .by_key("macro_use") - .exists(); - if is_macro_use { - self.import_all_legacy_macros(module_id); + Ok(()) => { + let module_id = self.push_child_module( + module.name.clone(), + ast_id.value, + Some((file_id, is_mod_rs)), + &self.item_tree[module.visibility], + module_id, + ); + ModCollector { + def_collector: self.def_collector, + macro_depth: self.macro_depth, + module_id, + tree_id: TreeId::new(file_id.into(), None), + item_tree: &item_tree, + mod_dir, + } + .collect_in_top_module(item_tree.top_level_items()); + let is_macro_use = is_macro_use + || item_tree + .top_level_attrs(db, krate) + .by_key("macro_use") + .exists(); + if is_macro_use { + self.import_all_legacy_macros(module_id); + } } } } @@ -2382,8 +2404,7 @@ impl ModCollector<'_, '_> { self.def_collector.cfg_options.check(cfg) != Some(false) } - fn emit_unconfigured_diagnostic(&mut self, ast_id: ErasedFileAstId, cfg: &CfgExpr) { - let ast_id = InFile::new(self.file_id(), ast_id); + fn emit_unconfigured_diagnostic(&mut self, ast_id: InFile, cfg: &CfgExpr) { self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id, ast_id, diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 7300a0628f572..01f79f042f789 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -355,12 +355,12 @@ impl DefMap { ModuleDefId::AdtId(AdtId::EnumId(e)) => { // enum variant cov_mark::hit!(can_import_enum_variant); + let def_map; let loc = e.lookup(db); let tree = loc.id.item_tree(db); let current_def_map = self.krate == loc.container.krate && self.block_id() == loc.container.block; - let def_map; let res = if current_def_map { &self.enum_definitions[&e] } else { diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs index 4698e967eb7d9..9bd8c8d2215e0 100644 --- a/crates/hir-def/src/src.rs +++ b/crates/hir-def/src/src.rs @@ -5,7 +5,7 @@ use la_arena::ArenaMap; use syntax::ast; use crate::{ - db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, EnumVariantLoc, ItemLoc, Lookup, + db::DefDatabase, item_tree::ItemTreeModItemNode, AssocItemLoc, EnumVariantLoc, ItemLoc, Lookup, Macro2Loc, MacroRulesLoc, ProcMacroLoc, UseId, }; @@ -14,7 +14,7 @@ pub trait HasSource { fn source(&self, db: &dyn DefDatabase) -> InFile; } -impl HasSource for AssocItemLoc { +impl HasSource for AssocItemLoc { type Value = N::Source; fn source(&self, db: &dyn DefDatabase) -> InFile { @@ -27,7 +27,7 @@ impl HasSource for AssocItemLoc { } } -impl HasSource for ItemLoc { +impl HasSource for ItemLoc { type Value = N::Source; fn source(&self, db: &dyn DefDatabase) -> InFile { diff --git a/crates/hir-def/src/trace.rs b/crates/hir-def/src/trace.rs index 01654f04ccf40..04d5b266194e3 100644 --- a/crates/hir-def/src/trace.rs +++ b/crates/hir-def/src/trace.rs @@ -19,6 +19,10 @@ pub(crate) struct Trace { impl Trace { #[allow(dead_code)] + pub(crate) fn new_for_arena() -> Trace { + Trace { arena: Some(Arena::default()), map: None, len: 0 } + } + pub(crate) fn new_for_map() -> Trace { Trace { arena: None, map: Some(ArenaMap::default()), len: 0 } } diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index c73ffa334c3de..3294ce29a4a05 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -222,11 +222,7 @@ pub(crate) fn field_visibilities_query( db: &dyn DefDatabase, variant_id: VariantId, ) -> Arc> { - let var_data = match variant_id { - VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), - VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), - VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(), - }; + let var_data = variant_id.variant_data(db); let resolver = variant_id.module(db).resolver(db); let mut res = ArenaMap::default(); for (field_id, field_data) in var_data.fields().iter() { diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 2e1525ebe178c..dae612e6d25c8 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -257,15 +257,7 @@ pub(crate) fn const_eval_discriminant_variant( let body = db.body(def); if body.exprs[body.body_expr] == Expr::Missing { let loc = variant_id.lookup(db.upcast()); - let parent_id = loc.parent.lookup(db.upcast()).id; - let index = loc.id.value.index().into_raw().into_u32() - - parent_id.item_tree(db.upcast())[parent_id.value] - .variants - .start - .index() - .into_raw() - .into_u32(); - let prev_idx = index.checked_sub(1); + let prev_idx = loc.index.checked_sub(1); let value = match prev_idx { Some(prev_idx) => { 1 + db.const_eval_discriminant( diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs index dda5ddd0bae85..a63556f450dda 100644 --- a/crates/hir-ty/src/inhabitedness.rs +++ b/crates/hir-ty/src/inhabitedness.rs @@ -7,7 +7,7 @@ use chalk_ir::{ }; use hir_def::{ attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule, - Lookup, ModuleId, VariantId, + ModuleId, VariantId, }; use rustc_hash::FxHashSet; @@ -30,7 +30,7 @@ pub(crate) fn is_enum_variant_uninhabited_from( target_mod: ModuleId, db: &dyn HirDatabase, ) -> bool { - let is_local = variant.lookup(db.upcast()).container.krate() == target_mod.krate(); + let is_local = variant.module(db.upcast()).krate() == target_mod.krate(); let mut uninhabited_from = UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index e160f05b01383..f66b584e3b599 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1729,16 +1729,19 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS /// Build the type of a tuple struct constructor. fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders { let struct_data = db.struct_data(def); - if let StructKind::Unit = struct_data.variant_data.kind() { - return type_for_adt(db, def.into()); + match struct_data.variant_data.kind() { + StructKind::Record => unreachable!("callers check for valueness of variant"), + StructKind::Unit => return type_for_adt(db, def.into()), + StructKind::Tuple => { + let generics = generics(db.upcast(), AdtId::from(def).into()); + let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); + make_binders( + db, + &generics, + TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(Interner), + ) + } } - let generics = generics(db.upcast(), AdtId::from(def).into()); - let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - make_binders( - db, - &generics, - TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(Interner), - ) } fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig { @@ -1756,16 +1759,20 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) /// Build the type of a tuple enum variant constructor. fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> Binders { let e = def.lookup(db.upcast()).parent; - if let StructKind::Unit = db.enum_variant_data(def).variant_data.kind() { - return type_for_adt(db, e.into()); + match db.enum_variant_data(def).variant_data.kind() { + StructKind::Record => unreachable!("callers check for valueness of variant"), + StructKind::Unit => return type_for_adt(db, e.into()), + StructKind::Tuple => { + let generics = generics(db.upcast(), e.into()); + let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); + make_binders( + db, + &generics, + TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs) + .intern(Interner), + ) + } } - let generics = generics(db.upcast(), e.into()); - let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); - make_binders( - db, - &generics, - TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs).intern(Interner), - ) } fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders { @@ -1813,7 +1820,7 @@ impl CallableDefId { match self { CallableDefId::FunctionId(f) => f.lookup(db).module(db), CallableDefId::StructId(s) => s.lookup(db).container, - CallableDefId::EnumVariantId(e) => e.lookup(db).container, + CallableDefId::EnumVariantId(e) => e.module(db), } .krate() } @@ -1894,12 +1901,8 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders } pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders { - let impl_loc = impl_id.lookup(db.upcast()); let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); - let _cx = stdx::panic_context::enter(format!( - "impl_self_ty_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})" - )); let generics = generics(db.upcast(), impl_id.into()); let ctx = TyLoweringContext::new(db, &resolver, impl_id.into()) .with_type_param_mode(ParamLoweringMode::Variable); @@ -1931,12 +1934,8 @@ pub(crate) fn impl_self_ty_recover( } pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option> { - let impl_loc = impl_id.lookup(db.upcast()); let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); - let _cx = stdx::panic_context::enter(format!( - "impl_trait_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})" - )); let ctx = TyLoweringContext::new(db, &resolver, impl_id.into()) .with_type_param_mode(ParamLoweringMode::Variable); let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 431d09c5650f4..ceb170f6cf7d9 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -751,19 +751,9 @@ impl Evaluator<'_> { Variants::Single { .. } => &layout, Variants::Multiple { variants, .. } => { &variants[match f.parent { - hir_def::VariantId::EnumVariantId(it) => RustcEnumVariantIdx({ - let lookup = it.lookup(self.db.upcast()); - let rustc_enum_variant_idx = - lookup.id.value.index().into_raw().into_u32() - - lookup.id.item_tree(self.db.upcast()) - [lookup.parent.lookup(self.db.upcast()).id.value] - .variants - .start - .index() - .into_raw() - .into_u32(); - rustc_enum_variant_idx as usize - }), + hir_def::VariantId::EnumVariantId(it) => { + RustcEnumVariantIdx(it.lookup(self.db.upcast()).index as usize) + } _ => { return Err(MirEvalError::TypeError( "Multivariant layout only happens for enums", @@ -1604,15 +1594,7 @@ impl Evaluator<'_> { }; let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; let lookup = enum_variant_id.lookup(self.db.upcast()); - let rustc_enum_variant_idx = lookup.id.value.index().into_raw().into_u32() - - lookup.id.item_tree(self.db.upcast()) - [lookup.parent.lookup(self.db.upcast()).id.value] - .variants - .start - .index() - .into_raw() - .into_u32(); - let rustc_enum_variant_idx = RustcEnumVariantIdx(rustc_enum_variant_idx as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 246fc231b47f5..91f6065354397 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -44,7 +44,7 @@ use hir_def::{ data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, - item_tree::ItemTreeNode, + item_tree::ItemTreeModItemNode, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, nameres::{self, diagnostics::DefDiagnostic}, @@ -1294,7 +1294,7 @@ pub struct Variant { impl Variant { pub fn module(self, db: &dyn HirDatabase) -> Module { - Module { id: self.id.lookup(db.upcast()).container } + Module { id: self.id.module(db.upcast()) } } pub fn parent_enum(self, db: &dyn HirDatabase) -> Enum { @@ -1340,17 +1340,7 @@ impl Variant { layout::Variants::Multiple { variants, .. } => Layout( { let lookup = self.id.lookup(db.upcast()); - let rustc_enum_variant_idx = lookup.id.value.index().into_raw().into_u32() - - lookup.id.item_tree(db.upcast()) - [lookup.parent.lookup(db.upcast()).id.value] - .variants - .start - .index() - .into_raw() - .into_u32(); - let rustc_enum_variant_idx = - RustcEnumVariantIdx(rustc_enum_variant_idx as usize); - + let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); Arc::new(variants[rustc_enum_variant_idx].clone()) }, db.target_data_layout(parent_enum.krate(db).into()).unwrap(), @@ -2838,7 +2828,7 @@ where ID: Lookup = dyn DefDatabase + 'db, Data = AssocItemLoc>, DEF: From, CTOR: FnOnce(DEF) -> AssocItem, - AST: ItemTreeNode, + AST: ItemTreeModItemNode, { match id.lookup(db.upcast()).container { ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => Some(ctor(DEF::from(id))), diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 1c6157de55958..19c2d3be16b78 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -440,6 +440,12 @@ impl ast::Struct { } } +impl ast::Union { + pub fn kind(&self) -> StructKind { + StructKind::from_node(self) + } +} + impl ast::RecordExprField { pub fn for_field_name(field_name: &ast::NameRef) -> Option { let candidate = Self::for_name_ref(field_name)?; From a356172f9220a4aa2bec5b5c1554e510ed55a96a Mon Sep 17 00:00:00 2001 From: Moritz Hedtke Date: Mon, 25 Dec 2023 22:58:04 +0100 Subject: [PATCH 021/118] internal: re-generate lints.rs --- crates/ide-db/src/generated/lints.rs | 1683 ++++++++++++++------------ 1 file changed, 911 insertions(+), 772 deletions(-) diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index 1cb6ff8627a23..f160def0aff00 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs @@ -5,6 +5,7 @@ pub struct Lint { pub label: &'static str, pub description: &'static str, } + pub struct LintGroup { pub lint: Lint, pub children: &'static [&'static str], @@ -21,6 +22,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"detects certain glob imports that require reporting an ambiguity error"##, }, Lint { label: "ambiguous_glob_reexports", description: r##"ambiguous glob re-exports"## }, + Lint { + label: "ambiguous_wide_pointer_comparisons", + description: r##"detects ambiguous wide pointer comparisons"##, + }, Lint { label: "anonymous_parameters", description: r##"detects anonymous parameters"## }, Lint { label: "arithmetic_overflow", description: r##"arithmetic operation overflows"## }, Lint { @@ -105,7 +110,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "deref_into_dyn_supertrait", - description: r##"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future"##, + description: r##"`Deref` implementation usage with a supertrait trait object for output are shadow by implicit coercion"##, }, Lint { label: "deref_nullptr", @@ -175,7 +180,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ }, Lint { label: "future_incompatible", - description: r##"lint group for: deref-into-dyn-supertrait, ambiguous-associated-items, ambiguous-glob-imports, byte-slice-in-packed-struct-with-derive, cenum-impl-drop-cast, coherence-leak-check, coinductive-overlap-in-coherence, conflicting-repr-hints, const-evaluatable-unchecked, const-patterns-without-partial-eq, deprecated-cfg-attr-crate-type-name, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, illegal-floating-point-literal-pattern, implied-bounds-entailment, indirect-structural-match, invalid-alignment, invalid-doc-attributes, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, nontrivial-structural-match, order-dependent-trait-objects, patterns-in-fns-without-body, pointer-structural-match, proc-macro-back-compat, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, semicolon-in-expressions-from-macros, soft-unstable, suspicious-auto-trait-impls, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, where-clauses-object-safety"##, + description: r##"lint group for: ambiguous-associated-items, ambiguous-glob-imports, byte-slice-in-packed-struct-with-derive, cenum-impl-drop-cast, coherence-leak-check, coinductive-overlap-in-coherence, conflicting-repr-hints, const-evaluatable-unchecked, const-patterns-without-partial-eq, deprecated-cfg-attr-crate-type-name, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, illegal-floating-point-literal-pattern, indirect-structural-match, invalid-doc-attributes, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, nontrivial-structural-match, order-dependent-trait-objects, patterns-in-fns-without-body, pointer-structural-match, proc-macro-back-compat, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, semicolon-in-expressions-from-macros, soft-unstable, suspicious-auto-trait-impls, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, where-clauses-object-safety, writes-through-immutable-pointer"##, }, Lint { label: "fuzzy_provenance_casts", @@ -193,10 +198,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "illegal_floating_point_literal_pattern", description: r##"floating-point literals cannot be used in patterns"##, }, - Lint { - label: "implied_bounds_entailment", - description: r##"impl method assumes more implied bounds than its corresponding trait method"##, - }, Lint { label: "improper_ctypes", description: r##"proper use of libc types in foreign modules"##, @@ -226,10 +227,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "internal_features", description: r##"internal features are not supposed to be used"##, }, - Lint { - label: "invalid_alignment", - description: r##"raw pointers must be aligned before dereferencing"##, - }, Lint { label: "invalid_atomic_ordering", description: r##"usage of invalid atomic ordering in atomic operations and memory fences"##, @@ -582,6 +579,10 @@ pub const DEFAULT_LINTS: &[Lint] = &[ description: r##"enabling track_caller on an async fn is a no-op unless the async_fn_track_caller feature is enabled"##, }, Lint { label: "uninhabited_static", description: r##"uninhabited static"## }, + Lint { + label: "unit_bindings", + description: r##"binding is useless because it has the unit `()` type"##, + }, Lint { label: "unknown_crate_types", description: r##"unknown crate type found in `#[crate_type]` directive"##, @@ -739,16 +740,19 @@ pub const DEFAULT_LINTS: &[Lint] = &[ label: "while_true", description: r##"suggest using `loop { }` instead of `while true { }`"##, }, + Lint { + label: "writes_through_immutable_pointer", + description: r##"shared references are immutable, and pointers derived from them must not be written to"##, + }, ]; pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "future_incompatible", - description: r##"lint group for: deref-into-dyn-supertrait, ambiguous-associated-items, ambiguous-glob-imports, byte-slice-in-packed-struct-with-derive, cenum-impl-drop-cast, coherence-leak-check, coinductive-overlap-in-coherence, conflicting-repr-hints, const-evaluatable-unchecked, const-patterns-without-partial-eq, deprecated-cfg-attr-crate-type-name, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, illegal-floating-point-literal-pattern, implied-bounds-entailment, indirect-structural-match, invalid-alignment, invalid-doc-attributes, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, nontrivial-structural-match, order-dependent-trait-objects, patterns-in-fns-without-body, pointer-structural-match, proc-macro-back-compat, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, semicolon-in-expressions-from-macros, soft-unstable, suspicious-auto-trait-impls, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, where-clauses-object-safety"##, + description: r##"lint group for: ambiguous-associated-items, ambiguous-glob-imports, byte-slice-in-packed-struct-with-derive, cenum-impl-drop-cast, coherence-leak-check, coinductive-overlap-in-coherence, conflicting-repr-hints, const-evaluatable-unchecked, const-patterns-without-partial-eq, deprecated-cfg-attr-crate-type-name, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, illegal-floating-point-literal-pattern, indirect-structural-match, invalid-doc-attributes, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, nontrivial-structural-match, order-dependent-trait-objects, patterns-in-fns-without-body, pointer-structural-match, proc-macro-back-compat, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, semicolon-in-expressions-from-macros, soft-unstable, suspicious-auto-trait-impls, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, where-clauses-object-safety, writes-through-immutable-pointer"##, }, children: &[ - "deref_into_dyn_supertrait", "ambiguous_associated_items", "ambiguous_glob_imports", "byte_slice_in_packed_struct_with_derive", @@ -763,9 +767,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ "forbidden_lint_groups", "ill_formed_attribute_input", "illegal_floating_point_literal_pattern", - "implied_bounds_entailment", "indirect_structural_match", - "invalid_alignment", "invalid_doc_attributes", "invalid_type_param_default", "late_bound_lifetime_arguments", @@ -788,6 +790,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[ "unstable_syntax_pre_expansion", "unsupported_calling_conventions", "where_clauses_object_safety", + "writes_through_immutable_pointer", ], }, LintGroup { @@ -1367,17 +1370,6 @@ The tracking issue for this feature is: [#44874] [#44874]: https://github.com/rust-lang/rust/issues/44874 ------------------------- -"##, - }, - Lint { - label: "arc_unwrap_or_clone", - description: r##"# `arc_unwrap_or_clone` - -The tracking issue for this feature is: [#93610] - -[#93610]: https://github.com/rust-lang/rust/issues/93610 - ------------------------ "##, }, @@ -1757,23 +1749,32 @@ The tracking issue for this feature is: [#62290] "##, }, Lint { - label: "async_fn_in_trait", - description: r##"# `async_fn_in_trait` + label: "async_fn_track_caller", + description: r##"# `async_fn_track_caller` -The tracking issue for this feature is: [#91611] +The tracking issue for this feature is: [#110011] -[#91611]: https://github.com/rust-lang/rust/issues/91611 +[#110011]: https://github.com/rust-lang/rust/issues/110011 ------------------------ "##, }, Lint { - label: "async_fn_track_caller", - description: r##"# `async_fn_track_caller` + label: "async_for_loop", + description: r##"# `async_for_loop` -The tracking issue for this feature is: [#110011] +The tracking issue for this feature is: [#118898] -[#110011]: https://github.com/rust-lang/rust/issues/110011 +[#118898]: https://github.com/rust-lang/rust/issues/118898 + +------------------------ +"##, + }, + Lint { + label: "async_gen_internals", + description: r##"# `async_gen_internals` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------ "##, @@ -1819,17 +1820,6 @@ The tracking issue for this feature is: [#76314] [#76314]: https://github.com/rust-lang/rust/issues/76314 ------------------------- -"##, - }, - Lint { - label: "atomic_from_ptr", - description: r##"# `atomic_from_ptr` - -The tracking issue for this feature is: [#108652] - -[#108652]: https://github.com/rust-lang/rust/issues/108652 - ------------------------ "##, }, @@ -2139,6 +2129,17 @@ The tracking issue for this feature is: [#86423] [#86423]: https://github.com/rust-lang/rust/issues/86423 +------------------------ +"##, + }, + Lint { + label: "bufread_skip_until", + description: r##"# `bufread_skip_until` + +The tracking issue for this feature is: [#111735] + +[#111735]: https://github.com/rust-lang/rust/issues/111735 + ------------------------ "##, }, @@ -2172,17 +2173,6 @@ The tracking issue for this feature is: [#88345] [#88345]: https://github.com/rust-lang/rust/issues/88345 ------------------------- -"##, - }, - Lint { - label: "c_str_literals", - description: r##"# `c_str_literals` - -The tracking issue for this feature is: [#105723] - -[#105723]: https://github.com/rust-lang/rust/issues/105723 - ------------------------ "##, }, @@ -2584,8 +2574,8 @@ The tracking issue for this feature is: [#87417] ------------------------ -Allows using the `#[track_caller]` attribute on closures and generators. -Calls made to the closure or generator will have caller information +Allows using the `#[track_caller]` attribute on closures and coroutines. +Calls made to the closure or coroutine will have caller information available through `std::panic::Location::caller()`, just like using `#[track_caller]` on a function. "##, @@ -2839,15 +2829,6 @@ The tracking issue for this feature is: [#91583] [#91583]: https://github.com/rust-lang/rust/issues/91583 ------------------------- -"##, - }, - Lint { - label: "const_assert_type2", - description: r##"# `const_assert_type2` - -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. - ------------------------ "##, }, @@ -2870,6 +2851,17 @@ The tracking issue for this feature is: [#85368] [#85368]: https://github.com/rust-lang/rust/issues/85368 +------------------------ +"##, + }, + Lint { + label: "const_atomic_from_ptr", + description: r##"# `const_atomic_from_ptr` + +The tracking issue for this feature is: [#108652] + +[#108652]: https://github.com/rust-lang/rust/issues/108652 + ------------------------ "##, }, @@ -2881,6 +2873,17 @@ The tracking issue for this feature is: [#85532] [#85532]: https://github.com/rust-lang/rust/issues/85532 +------------------------ +"##, + }, + Lint { + label: "const_binary_heap_constructor", + description: r##"# `const_binary_heap_constructor` + +The tracking issue for this feature is: [#112353] + +[#112353]: https://github.com/rust-lang/rust/issues/112353 + ------------------------ "##, }, @@ -2954,6 +2957,17 @@ The tracking issue for this feature is: [#106003] [#106003]: https://github.com/rust-lang/rust/issues/106003 +------------------------ +"##, + }, + Lint { + label: "const_cmp", + description: r##"# `const_cmp` + +The tracking issue for this feature is: [#92391] + +[#92391]: https://github.com/rust-lang/rust/issues/92391 + ------------------------ "##, }, @@ -2987,17 +3001,6 @@ The tracking issue for this feature is: [#113219] [#113219]: https://github.com/rust-lang/rust/issues/113219 ------------------------- -"##, - }, - Lint { - label: "const_discriminant", - description: r##"# `const_discriminant` - -The tracking issue for this feature is: [#69821] - -[#69821]: https://github.com/rust-lang/rust/issues/69821 - ------------------------ "##, }, @@ -3120,17 +3123,6 @@ The tracking issue for this feature is: [#79597] This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------- -"##, - }, - Lint { - label: "const_inherent_unchecked_arith", - description: r##"# `const_inherent_unchecked_arith` - -The tracking issue for this feature is: [#85122] - -[#85122]: https://github.com/rust-lang/rust/issues/85122 - ------------------------ "##, }, @@ -3273,17 +3265,6 @@ The tracking issue for this feature is: [#75251] This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. ------------------------- -"##, - }, - Lint { - label: "const_maybe_uninit_assume_init_read", - description: r##"# `const_maybe_uninit_assume_init_read` - -The tracking issue for this feature is: [#63567] - -[#63567]: https://github.com/rust-lang/rust/issues/63567 - ------------------------ "##, }, @@ -3306,17 +3287,6 @@ The tracking issue for this feature is: [#63567] [#63567]: https://github.com/rust-lang/rust/issues/63567 ------------------------- -"##, - }, - Lint { - label: "const_maybe_uninit_zeroed", - description: r##"# `const_maybe_uninit_zeroed` - -The tracking issue for this feature is: [#91850] - -[#91850]: https://github.com/rust-lang/rust/issues/91850 - ------------------------ "##, }, @@ -3383,17 +3353,6 @@ The tracking issue for this feature is: [#76654] [#76654]: https://github.com/rust-lang/rust/issues/76654 ------------------------- -"##, - }, - Lint { - label: "const_pointer_byte_offsets", - description: r##"# `const_pointer_byte_offsets` - -The tracking issue for this feature is: [#96283] - -[#96283]: https://github.com/rust-lang/rust/issues/96283 - ------------------------ "##, }, @@ -3784,6 +3743,17 @@ The tracking issue for this feature is: [#67057] This feature is internal to the Rust compiler and is not intended for general use. +------------------------ +"##, + }, + Lint { + label: "core_io_borrowed_buf", + description: r##"# `core_io_borrowed_buf` + +The tracking issue for this feature is: [#117693] + +[#117693]: https://github.com/rust-lang/rust/issues/117693 + ------------------------ "##, }, @@ -3815,119 +3785,391 @@ This feature is internal to the Rust compiler and is not intended for general us "##, }, Lint { - label: "coverage_attribute", - description: r##"# `coverage_attribute` - -The tracking issue for this feature is: [#84605] - -[#84605]: https://github.com/rust-lang/rust/issues/84605 - ---- - -The `coverage` attribute can be used to selectively disable coverage -instrumentation in an annotated function. This might be useful to: + label: "coroutine_clone", + description: r##"# `coroutine_clone` -- Avoid instrumentation overhead in a performance critical function -- Avoid generating coverage for a function that is not meant to be executed, - but still target 100% coverage for the rest of the program. - -## Example - -```rust -#![feature(coverage_attribute)] +The tracking issue for this feature is: [#95360] -// `foo()` will get coverage instrumentation (by default) -fn foo() { - // ... -} +[#95360]: https://github.com/rust-lang/rust/issues/95360 -#[coverage(off)] -fn bar() { - // ... -} -``` +------------------------ "##, }, Lint { - label: "cow_is_borrowed", - description: r##"# `cow_is_borrowed` + label: "coroutine_trait", + description: r##"# `coroutine_trait` -The tracking issue for this feature is: [#65143] +The tracking issue for this feature is: [#43122] -[#65143]: https://github.com/rust-lang/rust/issues/65143 +[#43122]: https://github.com/rust-lang/rust/issues/43122 ------------------------ "##, }, Lint { - label: "csky_target_feature", - description: r##"# `csky_target_feature` + label: "coroutines", + description: r##"# `coroutines` -The tracking issue for this feature is: [#44839] +The tracking issue for this feature is: [#43122] -[#44839]: https://github.com/rust-lang/rust/issues/44839 +[#43122]: https://github.com/rust-lang/rust/issues/43122 ------------------------ -"##, - }, - Lint { - label: "cstr_count_bytes", - description: r##"# `cstr_count_bytes` -The tracking issue for this feature is: [#114441] +The `coroutines` feature gate in Rust allows you to define coroutine or +coroutine literals. A coroutine is a "resumable function" that syntactically +resembles a closure but compiles to much different semantics in the compiler +itself. The primary feature of a coroutine is that it can be suspended during +execution to be resumed at a later date. Coroutines use the `yield` keyword to +"return", and then the caller can `resume` a coroutine to resume execution just +after the `yield` keyword. -[#114441]: https://github.com/rust-lang/rust/issues/114441 +Coroutines are an extra-unstable feature in the compiler right now. Added in +[RFC 2033] they're mostly intended right now as a information/constraint +gathering phase. The intent is that experimentation can happen on the nightly +compiler before actual stabilization. A further RFC will be required to +stabilize coroutines and will likely contain at least a few small +tweaks to the overall design. ------------------------- -"##, - }, - Lint { - label: "cursor_remaining", - description: r##"# `cursor_remaining` +[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 -The tracking issue for this feature is: [#86369] +A syntactical example of a coroutine is: -[#86369]: https://github.com/rust-lang/rust/issues/86369 +```rust +#![feature(coroutines, coroutine_trait)] ------------------------- -"##, - }, - Lint { - label: "custom_code_classes_in_docs", - description: r##"# `custom_code_classes_in_docs` +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; -The tracking issue for this feature is: [#79483] +fn main() { + let mut coroutine = || { + yield 1; + return "foo" + }; -[#79483]: https://github.com/rust-lang/rust/issues/79483 + match Pin::new(&mut coroutine).resume(()) { + CoroutineState::Yielded(1) => {} + _ => panic!("unexpected value from resume"), + } + match Pin::new(&mut coroutine).resume(()) { + CoroutineState::Complete("foo") => {} + _ => panic!("unexpected value from resume"), + } +} +``` ------------------------- -"##, - }, - Lint { - label: "custom_inner_attributes", - description: r##"# `custom_inner_attributes` +Coroutines are closure-like literals which can contain a `yield` statement. The +`yield` statement takes an optional expression of a value to yield out of the +coroutine. All coroutine literals implement the `Coroutine` trait in the +`std::ops` module. The `Coroutine` trait has one main method, `resume`, which +resumes execution of the coroutine at the previous suspension point. -The tracking issue for this feature is: [#54726] +An example of the control flow of coroutines is that the following example +prints all numbers in order: -[#54726]: https://github.com/rust-lang/rust/issues/54726 +```rust +#![feature(coroutines, coroutine_trait)] ------------------------- -"##, - }, - Lint { - label: "custom_mir", - description: r##"# `custom_mir` +use std::ops::Coroutine; +use std::pin::Pin; -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +fn main() { + let mut coroutine = || { + println!("2"); + yield; + println!("4"); + }; ------------------------- -"##, - }, - Lint { - label: "custom_test_frameworks", - description: r##"# `custom_test_frameworks` + println!("1"); + Pin::new(&mut coroutine).resume(()); + println!("3"); + Pin::new(&mut coroutine).resume(()); + println!("5"); +} +``` -The tracking issue for this feature is: [#50297] +At this time the main intended use case of coroutines is an implementation +primitive for async/await syntax, but coroutines will likely be extended to +ergonomic implementations of iterators and other primitives in the future. +Feedback on the design and usage is always appreciated! + +### The `Coroutine` trait + +The `Coroutine` trait in `std::ops` currently looks like: + +```rust +# #![feature(arbitrary_self_types, coroutine_trait)] +# use std::ops::CoroutineState; +# use std::pin::Pin; + +pub trait Coroutine { + type Yield; + type Return; + fn resume(self: Pin<&mut Self>, resume: R) -> CoroutineState; +} +``` + +The `Coroutine::Yield` type is the type of values that can be yielded with the +`yield` statement. The `Coroutine::Return` type is the returned type of the +coroutine. This is typically the last expression in a coroutine's definition or +any value passed to `return` in a coroutine. The `resume` function is the entry +point for executing the `Coroutine` itself. + +The return value of `resume`, `CoroutineState`, looks like: + +```rust +pub enum CoroutineState { + Yielded(Y), + Complete(R), +} +``` + +The `Yielded` variant indicates that the coroutine can later be resumed. This +corresponds to a `yield` point in a coroutine. The `Complete` variant indicates +that the coroutine is complete and cannot be resumed again. Calling `resume` +after a coroutine has returned `Complete` will likely result in a panic of the +program. + +### Closure-like semantics + +The closure-like syntax for coroutines alludes to the fact that they also have +closure-like semantics. Namely: + +* When created, a coroutine executes no code. A closure literal does not + actually execute any of the closure's code on construction, and similarly a + coroutine literal does not execute any code inside the coroutine when + constructed. + +* Coroutines can capture outer variables by reference or by move, and this can + be tweaked with the `move` keyword at the beginning of the closure. Like + closures all coroutines will have an implicit environment which is inferred by + the compiler. Outer variables can be moved into a coroutine for use as the + coroutine progresses. + +* Coroutine literals produce a value with a unique type which implements the + `std::ops::Coroutine` trait. This allows actual execution of the coroutine + through the `Coroutine::resume` method as well as also naming it in return + types and such. + +* Traits like `Send` and `Sync` are automatically implemented for a `Coroutine` + depending on the captured variables of the environment. Unlike closures, + coroutines also depend on variables live across suspension points. This means + that although the ambient environment may be `Send` or `Sync`, the coroutine + itself may not be due to internal variables live across `yield` points being + not-`Send` or not-`Sync`. Note that coroutines do + not implement traits like `Copy` or `Clone` automatically. + +* Whenever a coroutine is dropped it will drop all captured environment + variables. + +### Coroutines as state machines + +In the compiler, coroutines are currently compiled as state machines. Each +`yield` expression will correspond to a different state that stores all live +variables over that suspension point. Resumption of a coroutine will dispatch on +the current state and then execute internally until a `yield` is reached, at +which point all state is saved off in the coroutine and a value is returned. + +Let's take a look at an example to see what's going on here: + +```rust +#![feature(coroutines, coroutine_trait)] + +use std::ops::Coroutine; +use std::pin::Pin; + +fn main() { + let ret = "foo"; + let mut coroutine = move || { + yield 1; + return ret + }; + + Pin::new(&mut coroutine).resume(()); + Pin::new(&mut coroutine).resume(()); +} +``` + +This coroutine literal will compile down to something similar to: + +```rust +#![feature(arbitrary_self_types, coroutines, coroutine_trait)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; + +fn main() { + let ret = "foo"; + let mut coroutine = { + enum __Coroutine { + Start(&'static str), + Yield1(&'static str), + Done, + } + + impl Coroutine for __Coroutine { + type Yield = i32; + type Return = &'static str; + + fn resume(mut self: Pin<&mut Self>, resume: ()) -> CoroutineState { + use std::mem; + match mem::replace(&mut *self, __Coroutine::Done) { + __Coroutine::Start(s) => { + *self = __Coroutine::Yield1(s); + CoroutineState::Yielded(1) + } + + __Coroutine::Yield1(s) => { + *self = __Coroutine::Done; + CoroutineState::Complete(s) + } + + __Coroutine::Done => { + panic!("coroutine resumed after completion") + } + } + } + } + + __Coroutine::Start(ret) + }; + + Pin::new(&mut coroutine).resume(()); + Pin::new(&mut coroutine).resume(()); +} +``` + +Notably here we can see that the compiler is generating a fresh type, +`__Coroutine` in this case. This type has a number of states (represented here +as an `enum`) corresponding to each of the conceptual states of the coroutine. +At the beginning we're closing over our outer variable `foo` and then that +variable is also live over the `yield` point, so it's stored in both states. + +When the coroutine starts it'll immediately yield 1, but it saves off its state +just before it does so indicating that it has reached the yield point. Upon +resuming again we'll execute the `return ret` which returns the `Complete` +state. + +Here we can also note that the `Done` state, if resumed, panics immediately as +it's invalid to resume a completed coroutine. It's also worth noting that this +is just a rough desugaring, not a normative specification for what the compiler +does. +"##, + }, + Lint { + label: "coverage_attribute", + description: r##"# `coverage_attribute` + +The tracking issue for this feature is: [#84605] + +[#84605]: https://github.com/rust-lang/rust/issues/84605 + +--- + +The `coverage` attribute can be used to selectively disable coverage +instrumentation in an annotated function. This might be useful to: + +- Avoid instrumentation overhead in a performance critical function +- Avoid generating coverage for a function that is not meant to be executed, + but still target 100% coverage for the rest of the program. + +## Example + +```rust +#![feature(coverage_attribute)] + +// `foo()` will get coverage instrumentation (by default) +fn foo() { + // ... +} + +#[coverage(off)] +fn bar() { + // ... +} +``` +"##, + }, + Lint { + label: "cow_is_borrowed", + description: r##"# `cow_is_borrowed` + +The tracking issue for this feature is: [#65143] + +[#65143]: https://github.com/rust-lang/rust/issues/65143 + +------------------------ +"##, + }, + Lint { + label: "csky_target_feature", + description: r##"# `csky_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + +------------------------ +"##, + }, + Lint { + label: "cstr_count_bytes", + description: r##"# `cstr_count_bytes` + +The tracking issue for this feature is: [#114441] + +[#114441]: https://github.com/rust-lang/rust/issues/114441 + +------------------------ +"##, + }, + Lint { + label: "cursor_remaining", + description: r##"# `cursor_remaining` + +The tracking issue for this feature is: [#86369] + +[#86369]: https://github.com/rust-lang/rust/issues/86369 + +------------------------ +"##, + }, + Lint { + label: "custom_code_classes_in_docs", + description: r##"# `custom_code_classes_in_docs` + +The tracking issue for this feature is: [#79483] + +[#79483]: https://github.com/rust-lang/rust/issues/79483 + +------------------------ +"##, + }, + Lint { + label: "custom_inner_attributes", + description: r##"# `custom_inner_attributes` + +The tracking issue for this feature is: [#54726] + +[#54726]: https://github.com/rust-lang/rust/issues/54726 + +------------------------ +"##, + }, + Lint { + label: "custom_mir", + description: r##"# `custom_mir` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "custom_test_frameworks", + description: r##"# `custom_test_frameworks` + +The tracking issue for this feature is: [#50297] [#50297]: https://github.com/rust-lang/rust/issues/50297 @@ -3967,6 +4209,17 @@ The tracking issue for this feature is: [#46316] [#46316]: https://github.com/rust-lang/rust/issues/46316 +------------------------ +"##, + }, + Lint { + label: "debug_closure_helpers", + description: r##"# `debug_closure_helpers` + +The tracking issue for this feature is: [#117729] + +[#117729]: https://github.com/rust-lang/rust/issues/117729 + ------------------------ "##, }, @@ -4059,6 +4312,83 @@ The tracking issue for this feature is: [#111996] [#111996]: https://github.com/rust-lang/rust/issues/111996 ------------------------ + +The `diagnostic_namespace` feature permits customization of compilation errors. + +## diagnostic::on_unimplemented + +With [#114452] support for `diagnostic::on_unimplemented` was added. + +When used on a trait declaration, the following options are available: + +* `message` to customize the primary error message +* `note` to add a customized note message to an error message +* `label` to customize the label part of the error message + +The attribute will hint to the compiler to use these in error messages: +```rust +// some library +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented( + message = "cannot insert element", + label = "cannot be put into a table", + note = "see for more information about the Table api" +)] +pub trait Element { + // ... +} +``` + +```rust,compile_fail,E0277 +# #![feature(diagnostic_namespace)] +# +# #[diagnostic::on_unimplemented( +# message = "cannot insert element", +# label = "cannot be put into a table", +# note = "see for more information about the Table api" +# )] +# pub trait Element { +# // ... +# } +# struct Table; +# impl Table { +# fn insert(&self, element: T) { +# // .. +# } +# } +# fn main() { +# let table = Table; +# let element = (); +// user code +table.insert(element); +# } +``` + +```text +error[E0277]: cannot insert element + --> src/main.rs:24:18 + | +24 | table.insert(element); + | ------ ^^^^^^^ cannot be put into a table + | | + | required by a bound introduced by this call + | + = help: the trait `Element` is not implemented for `` + = note: see for more information about the Table api +note: required by a bound in `Table::insert` + --> src/main.rs:15:18 + | +15 | fn insert(&self, element: T) { + | ^^^^^^^ required by this bound in `Table::insert` + +For more information about this error, try `rustc --explain E0277`. +``` + +See [RFC 3368] for more information. + +[#114452]: https://github.com/rust-lang/rust/pull/114452 +[RFC 3368]: https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md "##, }, Lint { @@ -4279,6 +4609,17 @@ The tracking issue for this feature is: [#34761] [#34761]: https://github.com/rust-lang/rust/issues/34761 +------------------------ +"##, + }, + Lint { + label: "duration_abs_diff", + description: r##"# `duration_abs_diff` + +The tracking issue for this feature is: [#117618] + +[#117618]: https://github.com/rust-lang/rust/issues/117618 + ------------------------ "##, }, @@ -4505,6 +4846,17 @@ The tracking issue for this feature is: [#112788] [#112788]: https://github.com/rust-lang/rust/issues/112788 +------------------------ +"##, + }, + Lint { + label: "exposed_provenance", + description: r##"# `exposed_provenance` + +The tracking issue for this feature is: [#95228] + +[#95228]: https://github.com/rust-lang/rust/issues/95228 + ------------------------ "##, }, @@ -4697,28 +5049,6 @@ The tracking issue for this feature is: [#58314] [#58314]: https://github.com/rust-lang/rust/issues/58314 ------------------------- -"##, - }, - Lint { - label: "file_create_new", - description: r##"# `file_create_new` - -The tracking issue for this feature is: [#105135] - -[#105135]: https://github.com/rust-lang/rust/issues/105135 - ------------------------- -"##, - }, - Lint { - label: "file_set_times", - description: r##"# `file_set_times` - -The tracking issue for this feature is: [#98245] - -[#98245]: https://github.com/rust-lang/rust/issues/98245 - ------------------------ "##, }, @@ -4790,6 +5120,17 @@ The tracking issue for this feature is: [#82232] [#82232]: https://github.com/rust-lang/rust/issues/82232 +------------------------ +"##, + }, + Lint { + label: "fn_delegation", + description: r##"# `fn_delegation` + +The tracking issue for this feature is: [#118212] + +[#118212]: https://github.com/rust-lang/rust/issues/118212 + ------------------------ "##, }, @@ -4893,286 +5234,25 @@ The tracking issue for this feature is: [#91642] "##, }, Lint { - label: "gen_future", - description: r##"# `gen_future` + label: "gen_blocks", + description: r##"# `gen_blocks` -The tracking issue for this feature is: [#50547] +The tracking issue for this feature is: [#117078] -[#50547]: https://github.com/rust-lang/rust/issues/50547 +[#117078]: https://github.com/rust-lang/rust/issues/117078 ------------------------ "##, }, Lint { - label: "generator_clone", - description: r##"# `generator_clone` - -The tracking issue for this feature is: [#95360] - -[#95360]: https://github.com/rust-lang/rust/issues/95360 - ------------------------- -"##, - }, - Lint { - label: "generator_trait", - description: r##"# `generator_trait` - -The tracking issue for this feature is: [#43122] - -[#43122]: https://github.com/rust-lang/rust/issues/43122 - ------------------------- -"##, - }, - Lint { - label: "generators", - description: r##"# `generators` + label: "gen_future", + description: r##"# `gen_future` -The tracking issue for this feature is: [#43122] +The tracking issue for this feature is: [#50547] -[#43122]: https://github.com/rust-lang/rust/issues/43122 +[#50547]: https://github.com/rust-lang/rust/issues/50547 ------------------------ - -The `generators` feature gate in Rust allows you to define generator or -coroutine literals. A generator is a "resumable function" that syntactically -resembles a closure but compiles to much different semantics in the compiler -itself. The primary feature of a generator is that it can be suspended during -execution to be resumed at a later date. Generators use the `yield` keyword to -"return", and then the caller can `resume` a generator to resume execution just -after the `yield` keyword. - -Generators are an extra-unstable feature in the compiler right now. Added in -[RFC 2033] they're mostly intended right now as a information/constraint -gathering phase. The intent is that experimentation can happen on the nightly -compiler before actual stabilization. A further RFC will be required to -stabilize generators/coroutines and will likely contain at least a few small -tweaks to the overall design. - -[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 - -A syntactical example of a generator is: - -```rust -#![feature(generators, generator_trait)] - -use std::ops::{Generator, GeneratorState}; -use std::pin::Pin; - -fn main() { - let mut generator = || { - yield 1; - return "foo" - }; - - match Pin::new(&mut generator).resume(()) { - GeneratorState::Yielded(1) => {} - _ => panic!("unexpected value from resume"), - } - match Pin::new(&mut generator).resume(()) { - GeneratorState::Complete("foo") => {} - _ => panic!("unexpected value from resume"), - } -} -``` - -Generators are closure-like literals which can contain a `yield` statement. The -`yield` statement takes an optional expression of a value to yield out of the -generator. All generator literals implement the `Generator` trait in the -`std::ops` module. The `Generator` trait has one main method, `resume`, which -resumes execution of the generator at the previous suspension point. - -An example of the control flow of generators is that the following example -prints all numbers in order: - -```rust -#![feature(generators, generator_trait)] - -use std::ops::Generator; -use std::pin::Pin; - -fn main() { - let mut generator = || { - println!("2"); - yield; - println!("4"); - }; - - println!("1"); - Pin::new(&mut generator).resume(()); - println!("3"); - Pin::new(&mut generator).resume(()); - println!("5"); -} -``` - -At this time the main intended use case of generators is an implementation -primitive for async/await syntax, but generators will likely be extended to -ergonomic implementations of iterators and other primitives in the future. -Feedback on the design and usage is always appreciated! - -### The `Generator` trait - -The `Generator` trait in `std::ops` currently looks like: - -```rust -# #![feature(arbitrary_self_types, generator_trait)] -# use std::ops::GeneratorState; -# use std::pin::Pin; - -pub trait Generator { - type Yield; - type Return; - fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState; -} -``` - -The `Generator::Yield` type is the type of values that can be yielded with the -`yield` statement. The `Generator::Return` type is the returned type of the -generator. This is typically the last expression in a generator's definition or -any value passed to `return` in a generator. The `resume` function is the entry -point for executing the `Generator` itself. - -The return value of `resume`, `GeneratorState`, looks like: - -```rust -pub enum GeneratorState { - Yielded(Y), - Complete(R), -} -``` - -The `Yielded` variant indicates that the generator can later be resumed. This -corresponds to a `yield` point in a generator. The `Complete` variant indicates -that the generator is complete and cannot be resumed again. Calling `resume` -after a generator has returned `Complete` will likely result in a panic of the -program. - -### Closure-like semantics - -The closure-like syntax for generators alludes to the fact that they also have -closure-like semantics. Namely: - -* When created, a generator executes no code. A closure literal does not - actually execute any of the closure's code on construction, and similarly a - generator literal does not execute any code inside the generator when - constructed. - -* Generators can capture outer variables by reference or by move, and this can - be tweaked with the `move` keyword at the beginning of the closure. Like - closures all generators will have an implicit environment which is inferred by - the compiler. Outer variables can be moved into a generator for use as the - generator progresses. - -* Generator literals produce a value with a unique type which implements the - `std::ops::Generator` trait. This allows actual execution of the generator - through the `Generator::resume` method as well as also naming it in return - types and such. - -* Traits like `Send` and `Sync` are automatically implemented for a `Generator` - depending on the captured variables of the environment. Unlike closures, - generators also depend on variables live across suspension points. This means - that although the ambient environment may be `Send` or `Sync`, the generator - itself may not be due to internal variables live across `yield` points being - not-`Send` or not-`Sync`. Note that generators do - not implement traits like `Copy` or `Clone` automatically. - -* Whenever a generator is dropped it will drop all captured environment - variables. - -### Generators as state machines - -In the compiler, generators are currently compiled as state machines. Each -`yield` expression will correspond to a different state that stores all live -variables over that suspension point. Resumption of a generator will dispatch on -the current state and then execute internally until a `yield` is reached, at -which point all state is saved off in the generator and a value is returned. - -Let's take a look at an example to see what's going on here: - -```rust -#![feature(generators, generator_trait)] - -use std::ops::Generator; -use std::pin::Pin; - -fn main() { - let ret = "foo"; - let mut generator = move || { - yield 1; - return ret - }; - - Pin::new(&mut generator).resume(()); - Pin::new(&mut generator).resume(()); -} -``` - -This generator literal will compile down to something similar to: - -```rust -#![feature(arbitrary_self_types, generators, generator_trait)] - -use std::ops::{Generator, GeneratorState}; -use std::pin::Pin; - -fn main() { - let ret = "foo"; - let mut generator = { - enum __Generator { - Start(&'static str), - Yield1(&'static str), - Done, - } - - impl Generator for __Generator { - type Yield = i32; - type Return = &'static str; - - fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState { - use std::mem; - match mem::replace(&mut *self, __Generator::Done) { - __Generator::Start(s) => { - *self = __Generator::Yield1(s); - GeneratorState::Yielded(1) - } - - __Generator::Yield1(s) => { - *self = __Generator::Done; - GeneratorState::Complete(s) - } - - __Generator::Done => { - panic!("generator resumed after completion") - } - } - } - } - - __Generator::Start(ret) - }; - - Pin::new(&mut generator).resume(()); - Pin::new(&mut generator).resume(()); -} -``` - -Notably here we can see that the compiler is generating a fresh type, -`__Generator` in this case. This type has a number of states (represented here -as an `enum`) corresponding to each of the conceptual states of the generator. -At the beginning we're closing over our outer variable `foo` and then that -variable is also live over the `yield` point, so it's stored in both states. - -When the generator starts it'll immediately yield 1, but it saves off its state -just before it does so indicating that it has reached the yield point. Upon -resuming again we'll execute the `return ret` which returns the `Complete` -state. - -Here we can also note that the `Done` state, if resumed, panics immediately as -it's invalid to resume a completed generator. It's also worth noting that this -is just a rough desugaring, not a normative specification for what the compiler -does. "##, }, Lint { @@ -5652,17 +5732,6 @@ The tracking issue for this feature is: [#113744] [#113744]: https://github.com/rust-lang/rust/issues/113744 ------------------------- -"##, - }, - Lint { - label: "ip_in_core", - description: r##"# `ip_in_core` - -The tracking issue for this feature is: [#108443] - -[#108443]: https://github.com/rust-lang/rust/issues/108443 - ------------------------ "##, }, @@ -5737,8 +5806,8 @@ The tracking issue for this feature is: [#94780] "##, }, Lint { - label: "iter_from_generator", - description: r##"# `iter_from_generator` + label: "iter_from_coroutine", + description: r##"# `iter_from_coroutine` The tracking issue for this feature is: [#43122] @@ -6047,6 +6116,40 @@ This feature has no tracking issue, and is therefore likely internal to the comp This feature is internal to the Rust compiler and is not intended for general use. ------------------------ +"##, + }, + Lint { + label: "lifetime_capture_rules_2024", + description: r##"# `lifetime_capture_rules_2024` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + }, + Lint { + label: "link_arg_attribute", + description: r##"# `link_arg_attribute` + +The tracking issue for this feature is: [#99427] + +------ + +The `link_arg_attribute` feature allows passing arguments into the linker +from inside of the source code. Order is preserved for link attributes as +they were defined on a single extern block: + +```rust,no_run +#![feature(link_arg_attribute)] + +#[link(kind = "link-arg", name = "--start-group")] +#[link(kind = "static", name = "c")] +#[link(kind = "static", name = "gcc")] +#[link(kind = "link-arg", name = "--end-group")] +extern "C" {} +``` + +[#99427]: https://github.com/rust-lang/rust/issues/99427 "##, }, Lint { @@ -6099,6 +6202,17 @@ The tracking issue for this feature is: [#69210] [#69210]: https://github.com/rust-lang/rust/issues/69210 +------------------------ +"##, + }, + Lint { + label: "linked_list_retain", + description: r##"# `linked_list_retain` + +The tracking issue for this feature is: [#114135] + +[#114135]: https://github.com/rust-lang/rust/issues/114135 + ------------------------ "##, }, @@ -6132,6 +6246,17 @@ The tracking issue for this feature is: [#29598] [#29598]: https://github.com/rust-lang/rust/issues/29598 +------------------------ +"##, + }, + Lint { + label: "loongarch_target_feature", + description: r##"# `loongarch_target_feature` + +The tracking issue for this feature is: [#44839] + +[#44839]: https://github.com/rust-lang/rust/issues/44839 + ------------------------ "##, }, @@ -6513,8 +6638,19 @@ Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, a This serves two purposes: -* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists. -* It prevents downstream crates from creating such impls. +* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists. +* It prevents downstream crates from creating such impls. +"##, + }, + Lint { + label: "never_patterns", + description: r##"# `never_patterns` + +The tracking issue for this feature is: [#118155] + +[#118155]: https://github.com/rust-lang/rust/issues/118155 + +------------------------ "##, }, Lint { @@ -6613,6 +6749,17 @@ The tracking issue for this feature is: [#108185] [#108185]: https://github.com/rust-lang/rust/issues/108185 +------------------------ +"##, + }, + Lint { + label: "non_null_convenience", + description: r##"# `non_null_convenience` + +The tracking issue for this feature is: [#117691] + +[#117691]: https://github.com/rust-lang/rust/issues/117691 + ------------------------ "##, }, @@ -6677,6 +6824,17 @@ The tracking issue for this feature is: [#106655] [#106655]: https://github.com/rust-lang/rust/issues/106655 +------------------------ +"##, + }, + Lint { + label: "offset_of_enum", + description: r##"# `offset_of_enum` + +The tracking issue for this feature is: [#106655] + +[#106655]: https://github.com/rust-lang/rust/issues/106655 + ------------------------ "##, }, @@ -6697,6 +6855,17 @@ The tracking issue for this feature is: [#109737] [#109737]: https://github.com/rust-lang/rust/issues/109737 +------------------------ +"##, + }, + Lint { + label: "once_cell_try_insert", + description: r##"# `once_cell_try_insert` + +The tracking issue for this feature is: [#116693] + +[#116693]: https://github.com/rust-lang/rust/issues/116693 + ------------------------ "##, }, @@ -6752,6 +6921,17 @@ The tracking issue for this feature is: [#70086] [#70086]: https://github.com/rust-lang/rust/issues/70086 +------------------------ +"##, + }, + Lint { + label: "os_str_slice", + description: r##"# `os_str_slice` + +The tracking issue for this feature is: [#118485] + +[#118485]: https://github.com/rust-lang/rust/issues/118485 + ------------------------ "##, }, @@ -6904,135 +7084,6 @@ The tracking issue for this feature is: [#27731] [#27731]: https://github.com/rust-lang/rust/issues/27731 ------------------------- -"##, - }, - Lint { - label: "plugin", - description: r##"# `plugin` - -The tracking issue for this feature is: [#29597] - -[#29597]: https://github.com/rust-lang/rust/issues/29597 - - -This feature is part of "compiler plugins." It will often be used with the -`rustc_private` feature. - ------------------------- - -`rustc` can load compiler plugins, which are user-provided libraries that -extend the compiler's behavior with new lint checks, etc. - -A plugin is a dynamic library crate with a designated *registrar* function that -registers extensions with `rustc`. Other crates can load these extensions using -the crate attribute `#![plugin(...)]`. See the -`rustc_driver::plugin` documentation for more about the -mechanics of defining and loading a plugin. - -In the vast majority of cases, a plugin should *only* be used through -`#![plugin]` and not through an `extern crate` item. Linking a plugin would -pull in all of librustc_ast and librustc as dependencies of your crate. This is -generally unwanted unless you are building another plugin. - -The usual practice is to put compiler plugins in their own crate, separate from -any `macro_rules!` macros or ordinary Rust code meant to be used by consumers -of a library. - -# Lint plugins - -Plugins can extend [Rust's lint -infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with -additional checks for code style, safety, etc. Now let's write a plugin -[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/tests/ui-fulldeps/plugin/auxiliary/lint-plugin-test.rs) -that warns about any item named `lintme`. - -```rust,ignore (requires-stage-2) -#![feature(rustc_private)] - -extern crate rustc_ast; - -// Load rustc as a plugin to get macros -extern crate rustc_driver; -extern crate rustc_lint; -#[macro_use] -extern crate rustc_session; - -use rustc_ast::ast; -use rustc_driver::plugin::Registry; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; - -declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl EarlyLintPass for Pass { - fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { - if it.ident.name.as_str() == "lintme" { - cx.lint(TEST_LINT, "item is named 'lintme'", |lint| lint.set_span(it.span)); - } - } -} - -#[no_mangle] -fn __rustc_plugin_registrar(reg: &mut Registry) { - reg.lint_store.register_lints(&[&TEST_LINT]); - reg.lint_store.register_early_pass(|| Box::new(Pass)); -} -``` - -Then code like - -```rust,ignore (requires-plugin) -#![feature(plugin)] -#![plugin(lint_plugin_test)] - -fn lintme() { } -``` - -will produce a compiler warning: - -```txt -foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default -foo.rs:4 fn lintme() { } - ^~~~~~~~~~~~~~~ -``` - -The components of a lint plugin are: - -* one or more `declare_lint!` invocations, which define static `Lint` structs; - -* a struct holding any state needed by the lint pass (here, none); - -* a `LintPass` - implementation defining how to check each syntax element. A single - `LintPass` may call `span_lint` for several different `Lint`s, but should - register them all through the `get_lints` method. - -Lint passes are syntax traversals, but they run at a late stage of compilation -where type information is available. `rustc`'s [built-in -lints](https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint_defs/src/builtin.rs) -mostly use the same infrastructure as lint plugins, and provide examples of how -to access type information. - -Lints defined by plugins are controlled by the usual [attributes and compiler -flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g. -`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the -first argument to `declare_lint!`, with appropriate case and punctuation -conversion. - -You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`, -including those provided by plugins loaded by `foo.rs`. -"##, - }, - Lint { - label: "pointer_byte_offsets", - description: r##"# `pointer_byte_offsets` - -The tracking issue for this feature is: [#96283] - -[#96283]: https://github.com/rust-lang/rust/issues/96283 - ------------------------ "##, }, @@ -7075,17 +7126,6 @@ The tracking issue for this feature is: [#44839] [#44839]: https://github.com/rust-lang/rust/issues/44839 ------------------------- -"##, - }, - Lint { - label: "precise_pointer_size_matching", - description: r##"# `precise_pointer_size_matching` - -The tracking issue for this feature is: [#56354] - -[#56354]: https://github.com/rust-lang/rust/issues/56354 - ------------------------ "##, }, @@ -7248,17 +7288,6 @@ The tracking issue for this feature is: [#42524](https://github.com/rust-lang/ru This feature is internal to the Rust compiler and is not intended for general use. ------------------------- -"##, - }, - Lint { - label: "ptr_addr_eq", - description: r##"# `ptr_addr_eq` - -The tracking issue for this feature is: [#116324] - -[#116324]: https://github.com/rust-lang/rust/issues/116324 - ------------------------ "##, }, @@ -7281,17 +7310,6 @@ The tracking issue for this feature is: [#75402] [#75402]: https://github.com/rust-lang/rust/issues/75402 ------------------------- -"##, - }, - Lint { - label: "ptr_from_ref", - description: r##"# `ptr_from_ref` - -The tracking issue for this feature is: [#106116] - -[#106116]: https://github.com/rust-lang/rust/issues/106116 - ------------------------ "##, }, @@ -7502,28 +7520,6 @@ The tracking issue for this feature is: [#70142] [#70142]: https://github.com/rust-lang/rust/issues/70142 ------------------------- -"##, - }, - Lint { - label: "result_option_inspect", - description: r##"# `result_option_inspect` - -The tracking issue for this feature is: [#91345] - -[#91345]: https://github.com/rust-lang/rust/issues/91345 - ------------------------- -"##, - }, - Lint { - label: "return_position_impl_trait_in_trait", - description: r##"# `return_position_impl_trait_in_trait` - -The tracking issue for this feature is: [#91611] - -[#91611]: https://github.com/rust-lang/rust/issues/91611 - ------------------------ "##, }, @@ -7709,6 +7705,17 @@ The tracking issue for this feature is: [#101730] This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +------------------------ +"##, + }, + Lint { + label: "seek_seek_relative", + description: r##"# `seek_seek_relative` + +The tracking issue for this feature is: [#117374] + +[#117374]: https://github.com/rust-lang/rust/issues/117374 + ------------------------ "##, }, @@ -7945,6 +7952,17 @@ The tracking issue for this feature is: [#76014] [#76014]: https://github.com/rust-lang/rust/issues/76014 +------------------------ +"##, + }, + Lint { + label: "slice_split_once", + description: r##"# `slice_split_once` + +The tracking issue for this feature is: [#112811] + +[#112811]: https://github.com/rust-lang/rust/issues/112811 + ------------------------ "##, }, @@ -8174,6 +8192,17 @@ The tracking issue for this feature is: [#15701] [#15701]: https://github.com/rust-lang/rust/issues/15701 +------------------------ +"##, + }, + Lint { + label: "str_from_utf16_endian", + description: r##"# `str_from_utf16_endian` + +The tracking issue for this feature is: [#116258] + +[#116258]: https://github.com/rust-lang/rust/issues/116258 + ------------------------ "##, }, @@ -8265,6 +8294,44 @@ The tracking issue for this feature is: [#87121] [#87121]: https://github.com/rust-lang/rust/issues/87121 ------------------------ + +This feature permits pattern matching `String` to `&str` through [its `Deref` implementation]. + +```rust +#![feature(string_deref_patterns)] + +pub enum Value { + String(String), + Number(u32), +} + +pub fn is_it_the_answer(value: Value) -> bool { + match value { + Value::String("42") => true, + Value::Number(42) => true, + _ => false, + } +} +``` + +Without this feature other constructs such as match guards have to be used. + +```rust +# pub enum Value { +# String(String), +# Number(u32), +# } +# +pub fn is_it_the_answer(value: Value) -> bool { + match value { + Value::String(s) if s == "42" => true, + Value::Number(42) => true, + _ => false, + } +} +``` + +[its `Deref` implementation]: https://doc.rust-lang.org/std/string/struct.String.html#impl-Deref-for-String "##, }, Lint { @@ -8526,15 +8593,6 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured However, the optimizer can still modify a testcase in an undesirable manner even when using either of the above. -"##, - }, - Lint { - label: "test_2018_feature", - description: r##"# `test_2018_feature` - -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. - ------------------------- "##, }, Lint { @@ -8700,37 +8758,6 @@ pub fn main() { println!("{:?}", b); } ``` -"##, - }, - Lint { - label: "trait_upcasting", - description: r##"# `trait_upcasting` - -The tracking issue for this feature is: [#65991] - -[#65991]: https://github.com/rust-lang/rust/issues/65991 - ------------------------- - -The `trait_upcasting` feature adds support for trait upcasting coercion. This allows a -trait object of type `dyn Bar` to be cast to a trait object of type `dyn Foo` -so long as `Bar: Foo`. - -```rust,edition2018 -#![feature(trait_upcasting)] -#![allow(incomplete_features)] - -trait Foo {} - -trait Bar: Foo {} - -impl Foo for i32 {} - -impl Bar for T {} - -let bar: &dyn Bar = &123; -let foo: &dyn Foo = bar; -``` "##, }, Lint { @@ -8850,6 +8877,15 @@ The tracking issue for this feature is: [#48214] [#48214]: https://github.com/rust-lang/rust/issues/48214 +------------------------ +"##, + }, + Lint { + label: "trusted_fused", + description: r##"# `trusted_fused` + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + ------------------------ "##, }, @@ -9050,17 +9086,6 @@ fn main () { }; } ``` -"##, - }, - Lint { - label: "type_name_of_val", - description: r##"# `type_name_of_val` - -The tracking issue for this feature is: [#66359] - -[#66359]: https://github.com/rust-lang/rust/issues/66359 - ------------------------- "##, }, Lint { @@ -9122,6 +9147,28 @@ The tracking issue for this feature is: [#85122] [#85122]: https://github.com/rust-lang/rust/issues/85122 +------------------------ +"##, + }, + Lint { + label: "unchecked_neg", + description: r##"# `unchecked_neg` + +The tracking issue for this feature is: [#85122] + +[#85122]: https://github.com/rust-lang/rust/issues/85122 + +------------------------ +"##, + }, + Lint { + label: "unchecked_shifts", + description: r##"# `unchecked_shifts` + +The tracking issue for this feature is: [#85122] + +[#85122]: https://github.com/rust-lang/rust/issues/85122 + ------------------------ "##, }, @@ -9630,9 +9677,9 @@ The tracking issue for this feature is: [#81944] label: "waker_getters", description: r##"# `waker_getters` -The tracking issue for this feature is: [#87021] +The tracking issue for this feature is: [#96992] -[#87021]: https://github.com/rust-lang/rust/issues/87021 +[#96992]: https://github.com/rust-lang/rust/issues/96992 ------------------------ "##, @@ -9712,7 +9759,9 @@ This feature is internal to the Rust compiler and is not intended for general us label: "windows_process_exit_code_from", description: r##"# `windows_process_exit_code_from` -This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. +The tracking issue for this feature is: [#111688] + +[#111688]: https://github.com/rust-lang/rust/issues/111688 ------------------------ "##, @@ -10008,7 +10057,7 @@ table: description: r##"Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category."##, }, Lint { - label: "clippy::blocks_in_if_conditions", + label: "clippy::blocks_in_conditions", description: r##"Checks for `if` conditions that use blocks containing an expression, statements or conditions that use closures with blocks."##, }, @@ -10315,7 +10364,8 @@ with `#[rustfmt::skip]`."##, Lint { label: "clippy::deprecated_semver", description: r##"Checks for `#[deprecated]` annotations with a `since` -field that is not a valid semantic version."##, +field that is not a valid semantic version. Also allows TBD to signal +future deprecation."##, }, Lint { label: "clippy::deref_addrof", @@ -10462,7 +10512,7 @@ if the `never_type` is enabled."##, }, Lint { label: "clippy::empty_line_after_doc_comments", - description: r##"Checks for empty lines after documenation comments."##, + description: r##"Checks for empty lines after documentation comments."##, }, Lint { label: "clippy::empty_line_after_outer_attr", @@ -10777,6 +10827,13 @@ and the *else* part."##, label: "clippy::ignored_unit_patterns", description: r##"Checks for usage of `_` in patterns of type `()`."##, }, + Lint { + label: "clippy::impl_hash_borrow_with_str_and_bytes", + description: r##"This lint is concerned with the semantics of `Borrow` and `Hash` for a +type that implements all three of `Hash`, `Borrow` and `Borrow<[u8]>` +as it is impossible to satisfy the semantics of Borrow and `Hash` for +both `Borrow` and `Borrow<[u8]>`."##, + }, Lint { label: "clippy::impl_trait_in_params", description: r##"Lints when `impl Trait` is being used in a function's parameters."##, @@ -10852,6 +10909,11 @@ following table: |----------|----------|------------|-------| |`>` / `<=`|`\\|` / `^`|`x \\| 2 > 3`|`x > 3`| |`<` / `>=`|`\\|` / `^`|`x ^ 1 < 4` |`x < 4`|"##, + }, + Lint { + label: "clippy::ineffective_open_options", + description: r##"Checks if both `.write(true)` and `.append(true)` methods are called +on a same `OpenOptions`."##, }, Lint { label: "clippy::inefficient_to_string", @@ -10867,6 +10929,11 @@ or tuple struct where a `let` will suffice."##, label: "clippy::infinite_iter", description: r##"Checks for iteration that is guaranteed to be infinite."##, }, + Lint { + label: "clippy::infinite_loop", + description: r##"Checks for infinite loops in a function where the return type is not `!` +and lint accordingly."##, + }, Lint { label: "clippy::inherent_to_string", description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`."##, @@ -10997,6 +11064,10 @@ ignoring either the keys or values."##, description: r##"Looks for iterator combinator calls such as `.take(x)` or `.skip(x)` where `x` is greater than the amount of items that an iterator will produce."##, }, + Lint { + label: "clippy::iter_over_hash_type", + description: r##"This is a restriction lint which prevents the use of hash types (i.e., `HashSet` and `HashMap`) in for loops."##, + }, Lint { label: "clippy::iter_overeager_cloned", description: r##"Checks for usage of `_.cloned().()` where call to `.cloned()` can be postponed."##, @@ -11021,6 +11092,10 @@ where `x` is greater than the amount of items that an iterator will produce."##, label: "clippy::iterator_step_by_zero", description: r##"Checks for calling `.step_by(0)` on iterators which panics."##, }, + Lint { + label: "clippy::join_absolute_paths", + description: r##"Checks for calls to `Path::join` that start with a path separator (`\\\\` or `/`)."##, + }, Lint { label: "clippy::just_underscores_and_digits", description: r##"Checks if you have variables whose name consists of just @@ -11192,7 +11267,8 @@ where only the `Some` or `Ok` variant of the iterator element is used."##, Lint { label: "clippy::manual_is_ascii_check", description: r##"Suggests to use dedicated built-in methods, -`is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range"##, +`is_ascii_(lowercase|uppercase|digit|hexdigit)` for checking on corresponding +ascii range"##, }, Lint { label: "clippy::manual_is_finite", @@ -11400,7 +11476,9 @@ and take drastic actions like `panic!`."##, Lint { label: "clippy::maybe_misused_cfg", description: r##"Checks for `#[cfg(features = ...)]` and suggests to replace it with -`#[cfg(feature = ...)]`."##, +`#[cfg(feature = ...)]`. + +It also checks if `cfg(test)` was misspelled."##, }, Lint { label: "clippy::mem_forget", @@ -11478,7 +11556,10 @@ is greater than the largest index used to index into the slice."##, Lint { label: "clippy::missing_enforced_import_renames", description: r##"Checks for imports that do not rename the item as specified -in the `enforce-import-renames` config option."##, +in the `enforce-import-renames` config option. + +Note: Even though this lint is warn-by-default, it will only trigger if +import renames are defined in the clippy.toml file."##, }, Lint { label: "clippy::missing_errors_doc", @@ -11877,7 +11958,8 @@ suggests usage of the `env!` macro."##, }, Lint { label: "clippy::option_filter_map", - description: r##"Checks for indirect collection of populated `Option`"##, + description: r##"Checks for iterators of `Option`s using ``.filter(Option::is_some).map(Option::unwrap)` that may +be replaced with a `.flatten()` call."##, }, Lint { label: "clippy::option_if_let_else", @@ -11887,6 +11969,10 @@ idiomatically done with `Option::map_or` (if the else bit is a pure expression) or `Option::map_or_else` (if the else bit is an impure expression)."##, }, + Lint { + label: "clippy::option_map_or_err_ok", + description: r##"Checks for usage of `_.map_or(Err(_), Ok)`."##, + }, Lint { label: "clippy::option_map_or_none", description: r##"Checks for usage of `_.map_or(None, _)`."##, @@ -12135,7 +12221,7 @@ This is typically done indirectly with the `write!` macro or with `to_string()`. }, Lint { label: "clippy::redundant_as_str", - description: r##"Checks for usage of `as_str()` on a `String`` chained with a method available on the `String` itself."##, + description: r##"Checks for usage of `as_str()` on a `String` chained with a method available on the `String` itself."##, }, Lint { label: "clippy::redundant_async_block", @@ -12198,7 +12284,7 @@ could be used."##, Lint { label: "clippy::redundant_pattern_matching", description: r##"Lint for redundant pattern matching over `Result`, `Option`, -`std::task::Poll` or `std::net::IpAddr`"##, +`std::task::Poll`, `std::net::IpAddr` or `bool`s"##, }, Lint { label: "clippy::redundant_pub_crate", @@ -12244,6 +12330,10 @@ do not change the type."##, The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))"##, }, + Lint { + label: "clippy::repeat_vec_with_capacity", + description: r##"Looks for patterns such as `vec![Vec::with_capacity(x); n]` or `iter::repeat(Vec::with_capacity(x))`."##, + }, Lint { label: "clippy::replace_consts", description: r##"Nothing. This lint has been deprecated."##, @@ -12256,6 +12346,11 @@ they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://gi label: "clippy::rest_pat_in_fully_bound_structs", description: r##"Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched."##, }, + Lint { + label: "clippy::result_filter_map", + description: r##"Checks for iterators of `Result`s using ``.filter(Result::is_ok).map(Result::unwrap)` that may +be replaced with a `.flatten()` call."##, + }, Lint { label: "clippy::result_large_err", description: r##"Checks for functions that return `Result` with an unusually large @@ -12535,6 +12630,11 @@ and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instea label: "clippy::struct_excessive_bools", description: r##"Checks for excessive use of bools in structs."##, + }, + Lint { + label: "clippy::struct_field_names", + description: r##"Detects struct fields that are prefixed or suffixed +by the same characters or the name of the struct itself."##, }, Lint { label: "clippy::suboptimal_flops", @@ -12613,6 +12713,11 @@ but there is a space between the unary and its operand."##, label: "clippy::temporary_assignment", description: r##"Checks for construction of a structure or tuple just to assign a value in it."##, + }, + Lint { + label: "clippy::test_attr_in_doctest", + description: r##"Checks for `#[test]` in doctests unless they are marked with +either `ignore`, `no_run` or `compile_fail`."##, }, Lint { label: "clippy::tests_outside_test_module", @@ -12744,6 +12849,11 @@ declarations above a certain complexity threshold."##, label: "clippy::unchecked_duration_subtraction", description: r##"Lints subtraction between an [`Instant`] and a [`Duration`]."##, }, + Lint { + label: "clippy::unconditional_recursion", + description: r##"Checks that there isn't an infinite recursion in `PartialEq` trait +implementation."##, + }, Lint { label: "clippy::undocumented_unsafe_blocks", description: r##"Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment @@ -12778,6 +12888,11 @@ that is not equal to its label: "clippy::unimplemented", description: r##"Checks for usage of `unimplemented!`."##, }, + Lint { + label: "clippy::uninhabited_references", + description: r##"It detects references to uninhabited types, such as `!` and +warns when those are either dereferenced or returned from a function."##, + }, Lint { label: "clippy::uninit_assumed_init", description: r##"Checks for `MaybeUninit::uninit().assume_init()`."##, @@ -12821,6 +12936,11 @@ as returning a large `T` directly may be detrimental to performance."##, label: "clippy::unnecessary_cast", description: r##"Checks for casts to the same type, casts of int literals to integer types, casts of float literals to float types and casts between raw pointers without changing type or constness."##, + }, + Lint { + label: "clippy::unnecessary_fallible_conversions", + description: r##"Checks for calls to `TryInto::try_into` and `TryFrom::try_from` when their infallible counterparts +could be used."##, }, Lint { label: "clippy::unnecessary_filter_map", @@ -12864,7 +12984,8 @@ simpler code: }, Lint { label: "clippy::unnecessary_map_on_constructor", - description: r##"Suggest removing the use of a may (or map_err) method when an Option or Result is being construted."##, + description: r##"Suggests removing the use of a `map()` (or `map_err()`) method when an `Option` or `Result` +is being constructed."##, }, Lint { label: "clippy::unnecessary_mut_passed", @@ -12986,6 +13107,10 @@ types have different ABI, size or alignment."##, label: "clippy::unused_collect", description: r##"Nothing. This lint has been deprecated."##, }, + Lint { + label: "clippy::unused_enumerate_index", + description: r##"Checks for uses of the `enumerate` method where the index is unused (`_`)"##, + }, Lint { label: "clippy::unused_format_specs", description: r##"Detects [formatting parameters] that have no effect on the output of @@ -13130,8 +13255,8 @@ to `trailing_zeros`"##, description: r##"Checks for usage of File::read_to_end and File::read_to_string."##, }, Lint { - label: "clippy::vtable_address_comparisons", - description: r##"Checks for comparisons with an address of a trait vtable."##, + label: "clippy::waker_clone_wake", + description: r##"Checks for usage of `waker.clone().wake()`"##, }, Lint { label: "clippy::while_immutable_condition", @@ -13251,7 +13376,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::complexity", - description: r##"lint group for: clippy::bind_instead_of_map, clippy::bool_comparison, clippy::borrow_deref_ref, clippy::borrowed_box, clippy::bytes_count_to_len, clippy::char_lit_as_u8, clippy::clone_on_copy, clippy::crosspointer_transmute, clippy::default_constructed_unit_structs, clippy::deprecated_cfg_attr, clippy::deref_addrof, clippy::derivable_impls, clippy::diverging_sub_expression, clippy::double_comparisons, clippy::double_parens, clippy::duration_subsec, clippy::excessive_nesting, clippy::explicit_auto_deref, clippy::explicit_counter_loop, clippy::explicit_write, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, clippy::filter_map_identity, clippy::filter_next, clippy::flat_map_identity, clippy::get_last_with_len, clippy::identity_op, clippy::inspect_for_each, clippy::int_plus_one, clippy::iter_count, clippy::iter_kv_map, clippy::let_with_type_underscore, clippy::manual_filter, clippy::manual_filter_map, clippy::manual_find, clippy::manual_find_map, clippy::manual_flatten, clippy::manual_hash_one, clippy::manual_main_separator_str, clippy::manual_range_patterns, clippy::manual_rem_euclid, clippy::manual_slice_size_calculation, clippy::manual_split_once, clippy::manual_strip, clippy::manual_swap, clippy::manual_unwrap_or, clippy::map_flatten, clippy::map_identity, clippy::match_as_ref, clippy::match_single_binding, clippy::needless_arbitrary_self_type, clippy::needless_bool, clippy::needless_bool_assign, clippy::needless_borrowed_reference, clippy::needless_if, clippy::needless_lifetimes, clippy::needless_match, clippy::needless_option_as_deref, clippy::needless_option_take, clippy::needless_question_mark, clippy::needless_splitn, clippy::needless_update, clippy::neg_cmp_op_on_partial_ord, clippy::no_effect, clippy::nonminimal_bool, clippy::only_used_in_recursion, clippy::option_as_ref_deref, clippy::option_filter_map, clippy::option_map_unit_fn, clippy::or_then_unwrap, clippy::overflow_check_conditional, clippy::partialeq_ne_impl, clippy::precedence, clippy::ptr_offset_with_cast, clippy::range_zip_with_len, clippy::redundant_as_str, clippy::redundant_async_block, clippy::redundant_at_rest_pattern, clippy::redundant_closure_call, clippy::redundant_guards, clippy::redundant_slicing, clippy::repeat_once, clippy::reserve_after_initialization, clippy::result_map_unit_fn, clippy::search_is_some, clippy::seek_from_current, clippy::seek_to_start_instead_of_rewind, clippy::short_circuit_statement, clippy::single_element_loop, clippy::skip_while_next, clippy::string_from_utf8_as_bytes, clippy::strlen_on_c_strings, clippy::temporary_assignment, clippy::too_many_arguments, clippy::transmute_bytes_to_str, clippy::transmute_float_to_int, clippy::transmute_int_to_bool, clippy::transmute_int_to_char, clippy::transmute_int_to_float, clippy::transmute_int_to_non_zero, clippy::transmute_num_to_bytes, clippy::transmute_ptr_to_ref, clippy::transmutes_expressible_as_ptr_casts, clippy::type_complexity, clippy::unit_arg, clippy::unnecessary_cast, clippy::unnecessary_filter_map, clippy::unnecessary_find_map, clippy::unnecessary_literal_unwrap, clippy::unnecessary_map_on_constructor, clippy::unnecessary_operation, clippy::unnecessary_sort_by, clippy::unnecessary_unwrap, clippy::unneeded_wildcard_pattern, clippy::unused_format_specs, clippy::useless_asref, clippy::useless_conversion, clippy::useless_format, clippy::useless_transmute, clippy::vec_box, clippy::while_let_loop, clippy::wildcard_in_or_patterns, clippy::zero_divided_by_zero, clippy::zero_prefixed_literal"##, + description: r##"lint group for: clippy::bind_instead_of_map, clippy::bool_comparison, clippy::borrow_deref_ref, clippy::borrowed_box, clippy::bytes_count_to_len, clippy::char_lit_as_u8, clippy::clone_on_copy, clippy::crosspointer_transmute, clippy::default_constructed_unit_structs, clippy::deprecated_cfg_attr, clippy::deref_addrof, clippy::derivable_impls, clippy::diverging_sub_expression, clippy::double_comparisons, clippy::double_parens, clippy::duration_subsec, clippy::excessive_nesting, clippy::explicit_auto_deref, clippy::explicit_counter_loop, clippy::explicit_write, clippy::extra_unused_lifetimes, clippy::extra_unused_type_parameters, clippy::filter_map_identity, clippy::filter_next, clippy::flat_map_identity, clippy::get_last_with_len, clippy::identity_op, clippy::implied_bounds_in_impls, clippy::inspect_for_each, clippy::int_plus_one, clippy::iter_count, clippy::iter_kv_map, clippy::let_with_type_underscore, clippy::manual_filter, clippy::manual_filter_map, clippy::manual_find, clippy::manual_find_map, clippy::manual_flatten, clippy::manual_hash_one, clippy::manual_main_separator_str, clippy::manual_range_patterns, clippy::manual_rem_euclid, clippy::manual_slice_size_calculation, clippy::manual_split_once, clippy::manual_strip, clippy::manual_swap, clippy::manual_unwrap_or, clippy::map_flatten, clippy::map_identity, clippy::match_as_ref, clippy::match_single_binding, clippy::needless_arbitrary_self_type, clippy::needless_bool, clippy::needless_bool_assign, clippy::needless_borrowed_reference, clippy::needless_if, clippy::needless_lifetimes, clippy::needless_match, clippy::needless_option_as_deref, clippy::needless_option_take, clippy::needless_question_mark, clippy::needless_splitn, clippy::needless_update, clippy::neg_cmp_op_on_partial_ord, clippy::no_effect, clippy::nonminimal_bool, clippy::only_used_in_recursion, clippy::option_as_ref_deref, clippy::option_filter_map, clippy::option_map_unit_fn, clippy::or_then_unwrap, clippy::overflow_check_conditional, clippy::partialeq_ne_impl, clippy::precedence, clippy::ptr_offset_with_cast, clippy::range_zip_with_len, clippy::redundant_as_str, clippy::redundant_async_block, clippy::redundant_at_rest_pattern, clippy::redundant_closure_call, clippy::redundant_guards, clippy::redundant_slicing, clippy::repeat_once, clippy::reserve_after_initialization, clippy::result_filter_map, clippy::result_map_unit_fn, clippy::search_is_some, clippy::seek_from_current, clippy::seek_to_start_instead_of_rewind, clippy::short_circuit_statement, clippy::single_element_loop, clippy::skip_while_next, clippy::string_from_utf8_as_bytes, clippy::strlen_on_c_strings, clippy::temporary_assignment, clippy::too_many_arguments, clippy::transmute_bytes_to_str, clippy::transmute_float_to_int, clippy::transmute_int_to_bool, clippy::transmute_int_to_char, clippy::transmute_int_to_float, clippy::transmute_int_to_non_zero, clippy::transmute_num_to_bytes, clippy::transmute_ptr_to_ref, clippy::transmutes_expressible_as_ptr_casts, clippy::type_complexity, clippy::unit_arg, clippy::unnecessary_cast, clippy::unnecessary_filter_map, clippy::unnecessary_find_map, clippy::unnecessary_literal_unwrap, clippy::unnecessary_map_on_constructor, clippy::unnecessary_operation, clippy::unnecessary_sort_by, clippy::unnecessary_unwrap, clippy::unneeded_wildcard_pattern, clippy::unused_format_specs, clippy::useless_asref, clippy::useless_conversion, clippy::useless_format, clippy::useless_transmute, clippy::vec_box, clippy::while_let_loop, clippy::wildcard_in_or_patterns, clippy::zero_divided_by_zero, clippy::zero_prefixed_literal"##, }, children: &[ "clippy::bind_instead_of_map", @@ -13281,6 +13406,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::flat_map_identity", "clippy::get_last_with_len", "clippy::identity_op", + "clippy::implied_bounds_in_impls", "clippy::inspect_for_each", "clippy::int_plus_one", "clippy::iter_count", @@ -13337,6 +13463,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::redundant_slicing", "clippy::repeat_once", "clippy::reserve_after_initialization", + "clippy::result_filter_map", "clippy::result_map_unit_fn", "clippy::search_is_some", "clippy::seek_from_current", @@ -13383,7 +13510,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::correctness", - description: r##"lint group for: clippy::absurd_extreme_comparisons, clippy::almost_swapped, clippy::approx_constant, clippy::async_yields_async, clippy::bad_bit_mask, clippy::cast_slice_different_sizes, clippy::deprecated_semver, clippy::derive_ord_xor_partial_ord, clippy::derived_hash_with_manual_eq, clippy::enum_clike_unportable_variant, clippy::eq_op, clippy::erasing_op, clippy::fn_address_comparisons, clippy::if_let_mutex, clippy::if_same_then_else, clippy::ifs_same_cond, clippy::impossible_comparisons, clippy::ineffective_bit_mask, clippy::infinite_iter, clippy::inherent_to_string_shadow_display, clippy::inline_fn_without_body, clippy::invalid_null_ptr_usage, clippy::invalid_regex, clippy::invisible_characters, clippy::iter_next_loop, clippy::iter_skip_zero, clippy::iterator_step_by_zero, clippy::let_underscore_lock, clippy::match_str_case_mismatch, clippy::mem_replace_with_uninit, clippy::min_max, clippy::mismatched_target_os, clippy::mistyped_literal_suffixes, clippy::modulo_one, clippy::mut_from_ref, clippy::never_loop, clippy::non_octal_unix_permissions, clippy::nonsensical_open_options, clippy::not_unsafe_ptr_arg_deref, clippy::option_env_unwrap, clippy::out_of_bounds_indexing, clippy::overly_complex_bool_expr, clippy::panicking_unwrap, clippy::possible_missing_comma, clippy::read_line_without_trim, clippy::read_zero_byte_vec, clippy::recursive_format_impl, clippy::redundant_comparisons, clippy::redundant_locals, clippy::reversed_empty_ranges, clippy::self_assignment, clippy::serde_api_misuse, clippy::size_of_in_element_count, clippy::suspicious_splitn, clippy::transmute_null_to_fn, clippy::transmuting_null, clippy::uninit_assumed_init, clippy::uninit_vec, clippy::unit_cmp, clippy::unit_hash, clippy::unit_return_expecting_ord, clippy::unsound_collection_transmute, clippy::unused_io_amount, clippy::useless_attribute, clippy::vec_resize_to_zero, clippy::vtable_address_comparisons, clippy::while_immutable_condition, clippy::wrong_transmute, clippy::zst_offset"##, + description: r##"lint group for: clippy::absurd_extreme_comparisons, clippy::almost_swapped, clippy::approx_constant, clippy::async_yields_async, clippy::bad_bit_mask, clippy::cast_slice_different_sizes, clippy::deprecated_semver, clippy::derive_ord_xor_partial_ord, clippy::derived_hash_with_manual_eq, clippy::enum_clike_unportable_variant, clippy::eq_op, clippy::erasing_op, clippy::fn_address_comparisons, clippy::if_let_mutex, clippy::ifs_same_cond, clippy::impl_hash_borrow_with_str_and_bytes, clippy::impossible_comparisons, clippy::ineffective_bit_mask, clippy::infinite_iter, clippy::inherent_to_string_shadow_display, clippy::inline_fn_without_body, clippy::invalid_null_ptr_usage, clippy::invalid_regex, clippy::invisible_characters, clippy::iter_next_loop, clippy::iter_skip_zero, clippy::iterator_step_by_zero, clippy::let_underscore_lock, clippy::match_str_case_mismatch, clippy::mem_replace_with_uninit, clippy::min_max, clippy::mismatched_target_os, clippy::mistyped_literal_suffixes, clippy::modulo_one, clippy::mut_from_ref, clippy::never_loop, clippy::non_octal_unix_permissions, clippy::nonsensical_open_options, clippy::not_unsafe_ptr_arg_deref, clippy::option_env_unwrap, clippy::out_of_bounds_indexing, clippy::overly_complex_bool_expr, clippy::panicking_unwrap, clippy::possible_missing_comma, clippy::read_line_without_trim, clippy::recursive_format_impl, clippy::redundant_comparisons, clippy::redundant_locals, clippy::reversed_empty_ranges, clippy::self_assignment, clippy::serde_api_misuse, clippy::size_of_in_element_count, clippy::suspicious_splitn, clippy::transmute_null_to_fn, clippy::transmuting_null, clippy::uninit_assumed_init, clippy::uninit_vec, clippy::unit_cmp, clippy::unit_hash, clippy::unit_return_expecting_ord, clippy::unsound_collection_transmute, clippy::unused_io_amount, clippy::useless_attribute, clippy::vec_resize_to_zero, clippy::while_immutable_condition, clippy::wrong_transmute, clippy::zst_offset"##, }, children: &[ "clippy::absurd_extreme_comparisons", @@ -13400,8 +13527,8 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::erasing_op", "clippy::fn_address_comparisons", "clippy::if_let_mutex", - "clippy::if_same_then_else", "clippy::ifs_same_cond", + "clippy::impl_hash_borrow_with_str_and_bytes", "clippy::impossible_comparisons", "clippy::ineffective_bit_mask", "clippy::infinite_iter", @@ -13431,7 +13558,6 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::panicking_unwrap", "clippy::possible_missing_comma", "clippy::read_line_without_trim", - "clippy::read_zero_byte_vec", "clippy::recursive_format_impl", "clippy::redundant_comparisons", "clippy::redundant_locals", @@ -13451,7 +13577,6 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::unused_io_amount", "clippy::useless_attribute", "clippy::vec_resize_to_zero", - "clippy::vtable_address_comparisons", "clippy::while_immutable_condition", "clippy::wrong_transmute", "clippy::zst_offset", @@ -13484,7 +13609,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::nursery", - description: r##"lint group for: clippy::as_ptr_cast_mut, clippy::branches_sharing_code, clippy::clear_with_drain, clippy::cognitive_complexity, clippy::collection_is_never_read, clippy::debug_assert_with_mut_call, clippy::derive_partial_eq_without_eq, clippy::empty_line_after_doc_comments, clippy::empty_line_after_outer_attr, clippy::equatable_if_let, clippy::fallible_impl_from, clippy::future_not_send, clippy::implied_bounds_in_impls, clippy::imprecise_flops, clippy::iter_on_empty_collections, clippy::iter_on_single_items, clippy::iter_with_drain, clippy::large_stack_frames, clippy::manual_clamp, clippy::missing_const_for_fn, clippy::mutex_integer, clippy::needless_collect, clippy::needless_pass_by_ref_mut, clippy::non_send_fields_in_send_ty, clippy::nonstandard_macro_braces, clippy::option_if_let_else, clippy::or_fun_call, clippy::path_buf_push_overwrite, clippy::readonly_write_lock, clippy::redundant_clone, clippy::redundant_pub_crate, clippy::significant_drop_in_scrutinee, clippy::significant_drop_tightening, clippy::string_lit_as_bytes, clippy::suboptimal_flops, clippy::suspicious_operation_groupings, clippy::trailing_empty_array, clippy::trait_duplication_in_bounds, clippy::transmute_undefined_repr, clippy::trivial_regex, clippy::tuple_array_conversions, clippy::type_repetition_in_bounds, clippy::unnecessary_struct_initialization, clippy::unused_peekable, clippy::unused_rounding, clippy::use_self, clippy::useless_let_if_seq"##, + description: r##"lint group for: clippy::as_ptr_cast_mut, clippy::branches_sharing_code, clippy::clear_with_drain, clippy::cognitive_complexity, clippy::collection_is_never_read, clippy::debug_assert_with_mut_call, clippy::derive_partial_eq_without_eq, clippy::empty_line_after_doc_comments, clippy::empty_line_after_outer_attr, clippy::equatable_if_let, clippy::fallible_impl_from, clippy::future_not_send, clippy::imprecise_flops, clippy::iter_on_empty_collections, clippy::iter_on_single_items, clippy::iter_with_drain, clippy::large_stack_frames, clippy::manual_clamp, clippy::missing_const_for_fn, clippy::mutex_integer, clippy::needless_collect, clippy::needless_pass_by_ref_mut, clippy::non_send_fields_in_send_ty, clippy::nonstandard_macro_braces, clippy::option_if_let_else, clippy::or_fun_call, clippy::path_buf_push_overwrite, clippy::read_zero_byte_vec, clippy::readonly_write_lock, clippy::redundant_clone, clippy::redundant_pub_crate, clippy::significant_drop_in_scrutinee, clippy::significant_drop_tightening, clippy::string_lit_as_bytes, clippy::suboptimal_flops, clippy::suspicious_operation_groupings, clippy::trailing_empty_array, clippy::trait_duplication_in_bounds, clippy::transmute_undefined_repr, clippy::trivial_regex, clippy::tuple_array_conversions, clippy::type_repetition_in_bounds, clippy::uninhabited_references, clippy::unnecessary_struct_initialization, clippy::unused_peekable, clippy::unused_rounding, clippy::use_self, clippy::useless_let_if_seq"##, }, children: &[ "clippy::as_ptr_cast_mut", @@ -13499,7 +13624,6 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::equatable_if_let", "clippy::fallible_impl_from", "clippy::future_not_send", - "clippy::implied_bounds_in_impls", "clippy::imprecise_flops", "clippy::iter_on_empty_collections", "clippy::iter_on_single_items", @@ -13515,6 +13639,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::option_if_let_else", "clippy::or_fun_call", "clippy::path_buf_push_overwrite", + "clippy::read_zero_byte_vec", "clippy::readonly_write_lock", "clippy::redundant_clone", "clippy::redundant_pub_crate", @@ -13529,6 +13654,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::trivial_regex", "clippy::tuple_array_conversions", "clippy::type_repetition_in_bounds", + "clippy::uninhabited_references", "clippy::unnecessary_struct_initialization", "clippy::unused_peekable", "clippy::unused_rounding", @@ -13539,7 +13665,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::pedantic", - description: r##"lint group for: clippy::bool_to_int_with_if, clippy::borrow_as_ptr, clippy::case_sensitive_file_extension_comparisons, clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_ptr_alignment, clippy::cast_sign_loss, clippy::checked_conversions, clippy::cloned_instead_of_copied, clippy::copy_iterator, clippy::default_trait_access, clippy::doc_link_with_quotes, clippy::doc_markdown, clippy::empty_enum, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, clippy::explicit_deref_methods, clippy::explicit_into_iter_loop, clippy::explicit_iter_loop, clippy::filter_map_next, clippy::flat_map_option, clippy::float_cmp, clippy::fn_params_excessive_bools, clippy::from_iter_instead_of_collect, clippy::if_not_else, clippy::ignored_unit_patterns, clippy::implicit_clone, clippy::implicit_hasher, clippy::inconsistent_struct_constructor, clippy::index_refutable_slice, clippy::inefficient_to_string, clippy::inline_always, clippy::into_iter_without_iter, clippy::invalid_upcast_comparisons, clippy::items_after_statements, clippy::iter_not_returning_iterator, clippy::iter_without_into_iter, clippy::large_digit_groups, clippy::large_futures, clippy::large_stack_arrays, clippy::large_types_passed_by_value, clippy::linkedlist, clippy::macro_use_imports, clippy::manual_assert, clippy::manual_instant_elapsed, clippy::manual_let_else, clippy::manual_ok_or, clippy::manual_string_new, clippy::many_single_char_names, clippy::map_unwrap_or, clippy::match_bool, clippy::match_on_vec_items, clippy::match_same_arms, clippy::match_wild_err_arm, clippy::match_wildcard_for_single_variants, clippy::maybe_infinite_iter, clippy::mismatching_type_param_order, clippy::missing_errors_doc, clippy::missing_fields_in_debug, clippy::missing_panics_doc, clippy::module_name_repetitions, clippy::must_use_candidate, clippy::mut_mut, clippy::naive_bytecount, clippy::needless_bitwise_bool, clippy::needless_continue, clippy::needless_for_each, clippy::needless_pass_by_value, clippy::needless_raw_string_hashes, clippy::no_effect_underscore_binding, clippy::no_mangle_with_rust_abi, clippy::option_option, clippy::ptr_as_ptr, clippy::ptr_cast_constness, clippy::range_minus_one, clippy::range_plus_one, clippy::redundant_closure_for_method_calls, clippy::redundant_else, clippy::ref_binding_to_reference, clippy::ref_option_ref, clippy::return_self_not_must_use, clippy::same_functions_in_if_condition, clippy::semicolon_if_nothing_returned, clippy::should_panic_without_expect, clippy::similar_names, clippy::single_match_else, clippy::stable_sort_primitive, clippy::string_add_assign, clippy::struct_excessive_bools, clippy::too_many_lines, clippy::transmute_ptr_to_ptr, clippy::trivially_copy_pass_by_ref, clippy::unchecked_duration_subtraction, clippy::unicode_not_nfc, clippy::uninlined_format_args, clippy::unnecessary_box_returns, clippy::unnecessary_join, clippy::unnecessary_wraps, clippy::unnested_or_patterns, clippy::unreadable_literal, clippy::unsafe_derive_deserialize, clippy::unused_async, clippy::unused_self, clippy::used_underscore_binding, clippy::verbose_bit_mask, clippy::wildcard_imports, clippy::zero_sized_map_values"##, + description: r##"lint group for: clippy::bool_to_int_with_if, clippy::borrow_as_ptr, clippy::case_sensitive_file_extension_comparisons, clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_ptr_alignment, clippy::cast_sign_loss, clippy::checked_conversions, clippy::cloned_instead_of_copied, clippy::copy_iterator, clippy::default_trait_access, clippy::doc_link_with_quotes, clippy::doc_markdown, clippy::empty_enum, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, clippy::explicit_deref_methods, clippy::explicit_into_iter_loop, clippy::explicit_iter_loop, clippy::filter_map_next, clippy::flat_map_option, clippy::float_cmp, clippy::fn_params_excessive_bools, clippy::from_iter_instead_of_collect, clippy::if_not_else, clippy::ignored_unit_patterns, clippy::implicit_clone, clippy::implicit_hasher, clippy::inconsistent_struct_constructor, clippy::index_refutable_slice, clippy::inefficient_to_string, clippy::inline_always, clippy::into_iter_without_iter, clippy::invalid_upcast_comparisons, clippy::items_after_statements, clippy::iter_not_returning_iterator, clippy::iter_without_into_iter, clippy::large_digit_groups, clippy::large_futures, clippy::large_stack_arrays, clippy::large_types_passed_by_value, clippy::linkedlist, clippy::macro_use_imports, clippy::manual_assert, clippy::manual_instant_elapsed, clippy::manual_let_else, clippy::manual_ok_or, clippy::manual_string_new, clippy::many_single_char_names, clippy::map_unwrap_or, clippy::match_bool, clippy::match_on_vec_items, clippy::match_same_arms, clippy::match_wild_err_arm, clippy::match_wildcard_for_single_variants, clippy::maybe_infinite_iter, clippy::mismatching_type_param_order, clippy::missing_errors_doc, clippy::missing_fields_in_debug, clippy::missing_panics_doc, clippy::module_name_repetitions, clippy::must_use_candidate, clippy::mut_mut, clippy::naive_bytecount, clippy::needless_bitwise_bool, clippy::needless_continue, clippy::needless_for_each, clippy::needless_pass_by_value, clippy::needless_raw_string_hashes, clippy::no_effect_underscore_binding, clippy::no_mangle_with_rust_abi, clippy::option_option, clippy::ptr_as_ptr, clippy::ptr_cast_constness, clippy::range_minus_one, clippy::range_plus_one, clippy::redundant_closure_for_method_calls, clippy::redundant_else, clippy::ref_binding_to_reference, clippy::ref_option_ref, clippy::return_self_not_must_use, clippy::same_functions_in_if_condition, clippy::semicolon_if_nothing_returned, clippy::should_panic_without_expect, clippy::similar_names, clippy::single_match_else, clippy::stable_sort_primitive, clippy::string_add_assign, clippy::struct_excessive_bools, clippy::struct_field_names, clippy::too_many_lines, clippy::transmute_ptr_to_ptr, clippy::trivially_copy_pass_by_ref, clippy::unchecked_duration_subtraction, clippy::unicode_not_nfc, clippy::uninlined_format_args, clippy::unnecessary_box_returns, clippy::unnecessary_join, clippy::unnecessary_wraps, clippy::unnested_or_patterns, clippy::unreadable_literal, clippy::unsafe_derive_deserialize, clippy::unused_async, clippy::unused_self, clippy::used_underscore_binding, clippy::verbose_bit_mask, clippy::wildcard_imports, clippy::zero_sized_map_values"##, }, children: &[ "clippy::bool_to_int_with_if", @@ -13633,6 +13759,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::stable_sort_primitive", "clippy::string_add_assign", "clippy::struct_excessive_bools", + "clippy::struct_field_names", "clippy::too_many_lines", "clippy::transmute_ptr_to_ptr", "clippy::trivially_copy_pass_by_ref", @@ -13656,7 +13783,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::perf", - description: r##"lint group for: clippy::box_collection, clippy::box_default, clippy::boxed_local, clippy::cmp_owned, clippy::collapsible_str_replace, clippy::drain_collect, clippy::expect_fun_call, clippy::extend_with_drain, clippy::format_collect, clippy::format_in_format_args, clippy::iter_nth, clippy::iter_overeager_cloned, clippy::large_const_arrays, clippy::large_enum_variant, clippy::manual_memcpy, clippy::manual_retain, clippy::manual_str_repeat, clippy::manual_try_fold, clippy::map_entry, clippy::missing_spin_loop, clippy::redundant_allocation, clippy::result_large_err, clippy::single_char_pattern, clippy::slow_vector_initialization, clippy::to_string_in_format_args, clippy::unnecessary_to_owned, clippy::useless_vec, clippy::vec_init_then_push"##, + description: r##"lint group for: clippy::box_collection, clippy::box_default, clippy::boxed_local, clippy::cmp_owned, clippy::collapsible_str_replace, clippy::drain_collect, clippy::expect_fun_call, clippy::extend_with_drain, clippy::format_collect, clippy::format_in_format_args, clippy::iter_nth, clippy::iter_overeager_cloned, clippy::large_const_arrays, clippy::large_enum_variant, clippy::manual_memcpy, clippy::manual_retain, clippy::manual_str_repeat, clippy::manual_try_fold, clippy::map_entry, clippy::missing_spin_loop, clippy::redundant_allocation, clippy::result_large_err, clippy::single_char_pattern, clippy::slow_vector_initialization, clippy::to_string_in_format_args, clippy::unnecessary_to_owned, clippy::useless_vec, clippy::vec_init_then_push, clippy::waker_clone_wake"##, }, children: &[ "clippy::box_collection", @@ -13687,12 +13814,13 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::unnecessary_to_owned", "clippy::useless_vec", "clippy::vec_init_then_push", + "clippy::waker_clone_wake", ], }, LintGroup { lint: Lint { label: "clippy::restriction", - description: r##"lint group for: clippy::absolute_paths, clippy::alloc_instead_of_core, clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::arithmetic_side_effects, clippy::as_conversions, clippy::as_underscore, clippy::assertions_on_result_states, clippy::big_endian_bytes, clippy::clone_on_ref_ptr, clippy::create_dir, clippy::dbg_macro, clippy::decimal_literal_representation, clippy::default_numeric_fallback, clippy::default_union_representation, clippy::deref_by_slicing, clippy::disallowed_script_idents, clippy::else_if_without_else, clippy::empty_drop, clippy::empty_structs_with_brackets, clippy::error_impl_error, clippy::exhaustive_enums, clippy::exhaustive_structs, clippy::exit, clippy::expect_used, clippy::filetype_is_file, clippy::float_arithmetic, clippy::float_cmp_const, clippy::fn_to_numeric_cast_any, clippy::format_push_string, clippy::get_unwrap, clippy::host_endian_bytes, clippy::if_then_some_else_none, clippy::impl_trait_in_params, clippy::implicit_return, clippy::indexing_slicing, clippy::inline_asm_x86_att_syntax, clippy::inline_asm_x86_intel_syntax, clippy::integer_division, clippy::large_include_file, clippy::let_underscore_must_use, clippy::let_underscore_untyped, clippy::little_endian_bytes, clippy::lossy_float_literal, clippy::map_err_ignore, clippy::mem_forget, clippy::min_ident_chars, clippy::missing_assert_message, clippy::missing_asserts_for_indexing, clippy::missing_docs_in_private_items, clippy::missing_enforced_import_renames, clippy::missing_inline_in_public_items, clippy::missing_trait_methods, clippy::mixed_read_write_in_expression, clippy::mod_module_files, clippy::modulo_arithmetic, clippy::multiple_inherent_impl, clippy::multiple_unsafe_ops_per_block, clippy::mutex_atomic, clippy::needless_raw_strings, clippy::non_ascii_literal, clippy::panic, clippy::panic_in_result_fn, clippy::partial_pub_fields, clippy::pattern_type_mismatch, clippy::print_stderr, clippy::print_stdout, clippy::pub_use, clippy::pub_with_shorthand, clippy::pub_without_shorthand, clippy::question_mark_used, clippy::rc_buffer, clippy::rc_mutex, clippy::redundant_type_annotations, clippy::ref_patterns, clippy::rest_pat_in_fully_bound_structs, clippy::same_name_method, clippy::self_named_module_files, clippy::semicolon_inside_block, clippy::semicolon_outside_block, clippy::separated_literal_suffix, clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated, clippy::single_call_fn, clippy::single_char_lifetime_names, clippy::std_instead_of_alloc, clippy::std_instead_of_core, clippy::str_to_string, clippy::string_add, clippy::string_lit_chars_any, clippy::string_slice, clippy::string_to_string, clippy::suspicious_xor_used_as_pow, clippy::tests_outside_test_module, clippy::todo, clippy::try_err, clippy::undocumented_unsafe_blocks, clippy::unimplemented, clippy::unnecessary_safety_comment, clippy::unnecessary_safety_doc, clippy::unnecessary_self_imports, clippy::unneeded_field_pattern, clippy::unreachable, clippy::unseparated_literal_suffix, clippy::unwrap_in_result, clippy::unwrap_used, clippy::use_debug, clippy::verbose_file_reads, clippy::wildcard_enum_match_arm"##, + description: r##"lint group for: clippy::absolute_paths, clippy::alloc_instead_of_core, clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::arithmetic_side_effects, clippy::as_conversions, clippy::as_underscore, clippy::assertions_on_result_states, clippy::big_endian_bytes, clippy::clone_on_ref_ptr, clippy::create_dir, clippy::dbg_macro, clippy::decimal_literal_representation, clippy::default_numeric_fallback, clippy::default_union_representation, clippy::deref_by_slicing, clippy::disallowed_script_idents, clippy::else_if_without_else, clippy::empty_drop, clippy::empty_structs_with_brackets, clippy::error_impl_error, clippy::exhaustive_enums, clippy::exhaustive_structs, clippy::exit, clippy::expect_used, clippy::filetype_is_file, clippy::float_arithmetic, clippy::float_cmp_const, clippy::fn_to_numeric_cast_any, clippy::format_push_string, clippy::get_unwrap, clippy::host_endian_bytes, clippy::if_then_some_else_none, clippy::impl_trait_in_params, clippy::implicit_return, clippy::indexing_slicing, clippy::infinite_loop, clippy::inline_asm_x86_att_syntax, clippy::inline_asm_x86_intel_syntax, clippy::integer_division, clippy::iter_over_hash_type, clippy::large_include_file, clippy::let_underscore_must_use, clippy::let_underscore_untyped, clippy::little_endian_bytes, clippy::lossy_float_literal, clippy::map_err_ignore, clippy::mem_forget, clippy::min_ident_chars, clippy::missing_assert_message, clippy::missing_asserts_for_indexing, clippy::missing_docs_in_private_items, clippy::missing_inline_in_public_items, clippy::missing_trait_methods, clippy::mixed_read_write_in_expression, clippy::mod_module_files, clippy::modulo_arithmetic, clippy::multiple_inherent_impl, clippy::multiple_unsafe_ops_per_block, clippy::mutex_atomic, clippy::needless_raw_strings, clippy::non_ascii_literal, clippy::panic, clippy::panic_in_result_fn, clippy::partial_pub_fields, clippy::pattern_type_mismatch, clippy::print_stderr, clippy::print_stdout, clippy::pub_use, clippy::pub_with_shorthand, clippy::pub_without_shorthand, clippy::question_mark_used, clippy::rc_buffer, clippy::rc_mutex, clippy::redundant_type_annotations, clippy::ref_patterns, clippy::rest_pat_in_fully_bound_structs, clippy::same_name_method, clippy::self_named_module_files, clippy::semicolon_inside_block, clippy::semicolon_outside_block, clippy::separated_literal_suffix, clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated, clippy::single_call_fn, clippy::single_char_lifetime_names, clippy::std_instead_of_alloc, clippy::std_instead_of_core, clippy::str_to_string, clippy::string_add, clippy::string_lit_chars_any, clippy::string_slice, clippy::string_to_string, clippy::suspicious_xor_used_as_pow, clippy::tests_outside_test_module, clippy::todo, clippy::try_err, clippy::undocumented_unsafe_blocks, clippy::unimplemented, clippy::unnecessary_safety_comment, clippy::unnecessary_safety_doc, clippy::unnecessary_self_imports, clippy::unneeded_field_pattern, clippy::unreachable, clippy::unseparated_literal_suffix, clippy::unwrap_in_result, clippy::unwrap_used, clippy::use_debug, clippy::verbose_file_reads, clippy::wildcard_enum_match_arm"##, }, children: &[ "clippy::absolute_paths", @@ -13731,9 +13859,11 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::impl_trait_in_params", "clippy::implicit_return", "clippy::indexing_slicing", + "clippy::infinite_loop", "clippy::inline_asm_x86_att_syntax", "clippy::inline_asm_x86_intel_syntax", "clippy::integer_division", + "clippy::iter_over_hash_type", "clippy::large_include_file", "clippy::let_underscore_must_use", "clippy::let_underscore_untyped", @@ -13745,7 +13875,6 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::missing_assert_message", "clippy::missing_asserts_for_indexing", "clippy::missing_docs_in_private_items", - "clippy::missing_enforced_import_renames", "clippy::missing_inline_in_public_items", "clippy::missing_trait_methods", "clippy::mixed_read_write_in_expression", @@ -13810,12 +13939,12 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::style", - description: r##"lint group for: clippy::assertions_on_constants, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::bool_assert_comparison, clippy::borrow_interior_mutable_const, clippy::builtin_type_shadow, clippy::bytes_nth, clippy::chars_last_cmp, clippy::chars_next_cmp, clippy::cmp_null, clippy::collapsible_else_if, clippy::collapsible_if, clippy::collapsible_match, clippy::comparison_chain, clippy::comparison_to_empty, clippy::declare_interior_mutable_const, clippy::default_instead_of_iter_empty, clippy::disallowed_macros, clippy::disallowed_methods, clippy::disallowed_names, clippy::disallowed_types, clippy::double_must_use, clippy::double_neg, clippy::duplicate_underscore_argument, clippy::enum_variant_names, clippy::err_expect, clippy::excessive_precision, clippy::field_reassign_with_default, clippy::filter_map_bool_then, clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation, clippy::for_kv_map, clippy::from_over_into, clippy::from_str_radix_10, clippy::get_first, clippy::implicit_saturating_add, clippy::implicit_saturating_sub, clippy::inconsistent_digit_grouping, clippy::infallible_destructuring_match, clippy::inherent_to_string, clippy::init_numbered_fields, clippy::into_iter_on_ref, clippy::is_digit_ascii_radix, clippy::items_after_test_module, clippy::iter_cloned_collect, clippy::iter_next_slice, clippy::iter_nth_zero, clippy::iter_skip_next, clippy::just_underscores_and_digits, clippy::len_without_is_empty, clippy::len_zero, clippy::let_and_return, clippy::let_unit_value, clippy::main_recursion, clippy::manual_async_fn, clippy::manual_bits, clippy::manual_is_ascii_check, clippy::manual_is_finite, clippy::manual_is_infinite, clippy::manual_map, clippy::manual_next_back, clippy::manual_non_exhaustive, clippy::manual_range_contains, clippy::manual_saturating_arithmetic, clippy::manual_while_let_some, clippy::map_clone, clippy::map_collect_result_unit, clippy::match_like_matches_macro, clippy::match_overlapping_arm, clippy::match_ref_pats, clippy::match_result_ok, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default, clippy::missing_safety_doc, clippy::mixed_case_hex_literals, clippy::module_inception, clippy::must_use_unit, clippy::mut_mutex_lock, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::needless_doctest_main, clippy::needless_else, clippy::needless_late_init, clippy::needless_parens_on_range_literals, clippy::needless_pub_self, clippy::needless_range_loop, clippy::needless_return, clippy::needless_return_with_question_mark, clippy::neg_multiply, clippy::new_ret_no_self, clippy::new_without_default, clippy::non_minimal_cfg, clippy::obfuscated_if_else, clippy::ok_expect, clippy::op_ref, clippy::option_map_or_none, clippy::partialeq_to_none, clippy::print_literal, clippy::print_with_newline, clippy::println_empty_string, clippy::ptr_arg, clippy::ptr_eq, clippy::question_mark, clippy::redundant_closure, clippy::redundant_field_names, clippy::redundant_pattern, clippy::redundant_pattern_matching, clippy::redundant_static_lifetimes, clippy::result_map_or_into_option, clippy::result_unit_err, clippy::same_item_push, clippy::self_named_constructors, clippy::should_implement_trait, clippy::single_char_add_str, clippy::single_component_path_imports, clippy::single_match, clippy::string_extend_chars, clippy::tabs_in_doc_comments, clippy::to_digit_is_some, clippy::toplevel_ref_arg, clippy::trim_split_whitespace, clippy::unnecessary_fold, clippy::unnecessary_lazy_evaluations, clippy::unnecessary_mut_passed, clippy::unnecessary_owned_empty_strings, clippy::unsafe_removed_from_name, clippy::unused_unit, clippy::unusual_byte_groupings, clippy::unwrap_or_default, clippy::upper_case_acronyms, clippy::while_let_on_iterator, clippy::write_literal, clippy::write_with_newline, clippy::writeln_empty_string, clippy::wrong_self_convention, clippy::zero_ptr"##, + description: r##"lint group for: clippy::assertions_on_constants, clippy::assign_op_pattern, clippy::blocks_in_conditions, clippy::bool_assert_comparison, clippy::borrow_interior_mutable_const, clippy::builtin_type_shadow, clippy::bytes_nth, clippy::chars_last_cmp, clippy::chars_next_cmp, clippy::cmp_null, clippy::collapsible_else_if, clippy::collapsible_if, clippy::collapsible_match, clippy::comparison_chain, clippy::comparison_to_empty, clippy::declare_interior_mutable_const, clippy::default_instead_of_iter_empty, clippy::disallowed_macros, clippy::disallowed_methods, clippy::disallowed_names, clippy::disallowed_types, clippy::double_must_use, clippy::double_neg, clippy::duplicate_underscore_argument, clippy::enum_variant_names, clippy::err_expect, clippy::excessive_precision, clippy::field_reassign_with_default, clippy::filter_map_bool_then, clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation, clippy::for_kv_map, clippy::from_over_into, clippy::from_str_radix_10, clippy::get_first, clippy::if_same_then_else, clippy::implicit_saturating_add, clippy::implicit_saturating_sub, clippy::inconsistent_digit_grouping, clippy::infallible_destructuring_match, clippy::inherent_to_string, clippy::init_numbered_fields, clippy::into_iter_on_ref, clippy::is_digit_ascii_radix, clippy::items_after_test_module, clippy::iter_cloned_collect, clippy::iter_next_slice, clippy::iter_nth_zero, clippy::iter_skip_next, clippy::just_underscores_and_digits, clippy::len_without_is_empty, clippy::len_zero, clippy::let_and_return, clippy::let_unit_value, clippy::main_recursion, clippy::manual_async_fn, clippy::manual_bits, clippy::manual_is_ascii_check, clippy::manual_is_finite, clippy::manual_is_infinite, clippy::manual_map, clippy::manual_next_back, clippy::manual_non_exhaustive, clippy::manual_range_contains, clippy::manual_saturating_arithmetic, clippy::manual_while_let_some, clippy::map_clone, clippy::map_collect_result_unit, clippy::match_like_matches_macro, clippy::match_overlapping_arm, clippy::match_ref_pats, clippy::match_result_ok, clippy::mem_replace_option_with_none, clippy::mem_replace_with_default, clippy::missing_enforced_import_renames, clippy::missing_safety_doc, clippy::mixed_case_hex_literals, clippy::module_inception, clippy::must_use_unit, clippy::mut_mutex_lock, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::needless_doctest_main, clippy::needless_else, clippy::needless_late_init, clippy::needless_parens_on_range_literals, clippy::needless_pub_self, clippy::needless_range_loop, clippy::needless_return, clippy::needless_return_with_question_mark, clippy::neg_multiply, clippy::new_ret_no_self, clippy::new_without_default, clippy::non_minimal_cfg, clippy::obfuscated_if_else, clippy::ok_expect, clippy::op_ref, clippy::option_map_or_err_ok, clippy::option_map_or_none, clippy::partialeq_to_none, clippy::print_literal, clippy::print_with_newline, clippy::println_empty_string, clippy::ptr_arg, clippy::ptr_eq, clippy::question_mark, clippy::redundant_closure, clippy::redundant_field_names, clippy::redundant_pattern, clippy::redundant_pattern_matching, clippy::redundant_static_lifetimes, clippy::result_map_or_into_option, clippy::result_unit_err, clippy::same_item_push, clippy::self_named_constructors, clippy::should_implement_trait, clippy::single_char_add_str, clippy::single_component_path_imports, clippy::single_match, clippy::string_extend_chars, clippy::tabs_in_doc_comments, clippy::to_digit_is_some, clippy::toplevel_ref_arg, clippy::trim_split_whitespace, clippy::unnecessary_fallible_conversions, clippy::unnecessary_fold, clippy::unnecessary_lazy_evaluations, clippy::unnecessary_mut_passed, clippy::unnecessary_owned_empty_strings, clippy::unsafe_removed_from_name, clippy::unused_enumerate_index, clippy::unused_unit, clippy::unusual_byte_groupings, clippy::unwrap_or_default, clippy::upper_case_acronyms, clippy::while_let_on_iterator, clippy::write_literal, clippy::write_with_newline, clippy::writeln_empty_string, clippy::wrong_self_convention, clippy::zero_ptr"##, }, children: &[ "clippy::assertions_on_constants", "clippy::assign_op_pattern", - "clippy::blocks_in_if_conditions", + "clippy::blocks_in_conditions", "clippy::bool_assert_comparison", "clippy::borrow_interior_mutable_const", "clippy::builtin_type_shadow", @@ -13848,6 +13977,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::from_over_into", "clippy::from_str_radix_10", "clippy::get_first", + "clippy::if_same_then_else", "clippy::implicit_saturating_add", "clippy::implicit_saturating_sub", "clippy::inconsistent_digit_grouping", @@ -13886,6 +14016,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::match_result_ok", "clippy::mem_replace_option_with_none", "clippy::mem_replace_with_default", + "clippy::missing_enforced_import_renames", "clippy::missing_safety_doc", "clippy::mixed_case_hex_literals", "clippy::module_inception", @@ -13908,6 +14039,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::obfuscated_if_else", "clippy::ok_expect", "clippy::op_ref", + "clippy::option_map_or_err_ok", "clippy::option_map_or_none", "clippy::partialeq_to_none", "clippy::print_literal", @@ -13934,11 +14066,13 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::to_digit_is_some", "clippy::toplevel_ref_arg", "clippy::trim_split_whitespace", + "clippy::unnecessary_fallible_conversions", "clippy::unnecessary_fold", "clippy::unnecessary_lazy_evaluations", "clippy::unnecessary_mut_passed", "clippy::unnecessary_owned_empty_strings", "clippy::unsafe_removed_from_name", + "clippy::unused_enumerate_index", "clippy::unused_unit", "clippy::unusual_byte_groupings", "clippy::unwrap_or_default", @@ -13954,7 +14088,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ LintGroup { lint: Lint { label: "clippy::suspicious", - description: r##"lint group for: clippy::almost_complete_range, clippy::arc_with_non_send_sync, clippy::await_holding_invalid_type, clippy::await_holding_lock, clippy::await_holding_refcell_ref, clippy::blanket_clippy_restriction_lints, clippy::cast_abs_to_unsigned, clippy::cast_enum_constructor, clippy::cast_enum_truncation, clippy::cast_nan_to_int, clippy::cast_slice_from_raw_parts, clippy::crate_in_macro_def, clippy::drop_non_drop, clippy::duplicate_mod, clippy::empty_loop, clippy::float_equality_without_abs, clippy::forget_non_drop, clippy::four_forward_slashes, clippy::from_raw_with_void_ptr, clippy::iter_out_of_bounds, clippy::let_underscore_future, clippy::lines_filter_map_ok, clippy::maybe_misused_cfg, clippy::misnamed_getters, clippy::misrefactored_assign_op, clippy::multi_assignments, clippy::mut_range_bound, clippy::mutable_key_type, clippy::no_effect_replace, clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, clippy::octal_escapes, clippy::path_ends_with_ext, clippy::permissions_set_readonly_false, clippy::print_in_format_impl, clippy::rc_clone_in_vec_init, clippy::single_range_in_vec_init, clippy::size_of_ref, clippy::suspicious_arithmetic_impl, clippy::suspicious_assignment_formatting, clippy::suspicious_command_arg_space, clippy::suspicious_doc_comments, clippy::suspicious_else_formatting, clippy::suspicious_map, clippy::suspicious_op_assign_impl, clippy::suspicious_to_owned, clippy::suspicious_unary_op_formatting, clippy::swap_ptr_to_ref, clippy::type_id_on_box"##, + description: r##"lint group for: clippy::almost_complete_range, clippy::arc_with_non_send_sync, clippy::await_holding_invalid_type, clippy::await_holding_lock, clippy::await_holding_refcell_ref, clippy::blanket_clippy_restriction_lints, clippy::cast_abs_to_unsigned, clippy::cast_enum_constructor, clippy::cast_enum_truncation, clippy::cast_nan_to_int, clippy::cast_slice_from_raw_parts, clippy::crate_in_macro_def, clippy::drop_non_drop, clippy::duplicate_mod, clippy::empty_loop, clippy::float_equality_without_abs, clippy::forget_non_drop, clippy::four_forward_slashes, clippy::from_raw_with_void_ptr, clippy::ineffective_open_options, clippy::iter_out_of_bounds, clippy::join_absolute_paths, clippy::let_underscore_future, clippy::lines_filter_map_ok, clippy::maybe_misused_cfg, clippy::misnamed_getters, clippy::misrefactored_assign_op, clippy::multi_assignments, clippy::mut_range_bound, clippy::mutable_key_type, clippy::no_effect_replace, clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, clippy::octal_escapes, clippy::path_ends_with_ext, clippy::permissions_set_readonly_false, clippy::print_in_format_impl, clippy::rc_clone_in_vec_init, clippy::repeat_vec_with_capacity, clippy::single_range_in_vec_init, clippy::size_of_ref, clippy::suspicious_arithmetic_impl, clippy::suspicious_assignment_formatting, clippy::suspicious_command_arg_space, clippy::suspicious_doc_comments, clippy::suspicious_else_formatting, clippy::suspicious_map, clippy::suspicious_op_assign_impl, clippy::suspicious_to_owned, clippy::suspicious_unary_op_formatting, clippy::swap_ptr_to_ref, clippy::test_attr_in_doctest, clippy::type_id_on_box, clippy::unconditional_recursion"##, }, children: &[ "clippy::almost_complete_range", @@ -13976,7 +14110,9 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::forget_non_drop", "clippy::four_forward_slashes", "clippy::from_raw_with_void_ptr", + "clippy::ineffective_open_options", "clippy::iter_out_of_bounds", + "clippy::join_absolute_paths", "clippy::let_underscore_future", "clippy::lines_filter_map_ok", "clippy::maybe_misused_cfg", @@ -13993,6 +14129,7 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::permissions_set_readonly_false", "clippy::print_in_format_impl", "clippy::rc_clone_in_vec_init", + "clippy::repeat_vec_with_capacity", "clippy::single_range_in_vec_init", "clippy::size_of_ref", "clippy::suspicious_arithmetic_impl", @@ -14005,7 +14142,9 @@ pub const CLIPPY_LINT_GROUPS: &[LintGroup] = &[ "clippy::suspicious_to_owned", "clippy::suspicious_unary_op_formatting", "clippy::swap_ptr_to_ref", + "clippy::test_attr_in_doctest", "clippy::type_id_on_box", + "clippy::unconditional_recursion", ], }, ]; From f937673ce2b91ae7538941536428f681fad8b5b5 Mon Sep 17 00:00:00 2001 From: Moritz Hedtke Date: Mon, 25 Dec 2023 23:12:45 +0100 Subject: [PATCH 022/118] fix: rename generator to coroutine Follow the rename in nightly (see https://blog.rust-lang.org/inside-rust/2023/10/23/coroutines.html) --- crates/hir-def/src/body.rs | 2 +- crates/hir-def/src/body/lower.rs | 14 ++++----- crates/hir-def/src/body/pretty.rs | 2 +- crates/hir-def/src/hir.rs | 2 +- crates/hir-def/src/lang_item.rs | 4 +-- crates/hir-ty/src/builder.rs | 14 ++++----- crates/hir-ty/src/chalk_db.rs | 18 +++++------ crates/hir-ty/src/db.rs | 6 ++-- crates/hir-ty/src/display.rs | 12 ++++---- crates/hir-ty/src/infer.rs | 2 +- crates/hir-ty/src/infer/closure.rs | 4 +-- crates/hir-ty/src/infer/expr.rs | 12 ++++---- crates/hir-ty/src/mapping.rs | 6 ++-- crates/hir-ty/src/mir.rs | 30 +++++++++---------- crates/hir-ty/src/mir/borrowck.rs | 6 ++-- crates/hir-ty/src/mir/monomorphization.rs | 2 +- crates/hir-ty/src/tests/coercion.rs | 2 +- crates/hir-ty/src/tests/simple.rs | 26 ++++++++-------- .../src/handlers/generate_enum_is_method.rs | 10 +++---- crates/ide-db/src/apply_change.rs | 2 +- crates/ide-db/src/lib.rs | 2 +- crates/test-utils/src/minicore.rs | 22 +++++++------- 22 files changed, 100 insertions(+), 100 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index db28c6731ece1..415800b9b79a7 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -37,7 +37,7 @@ pub struct Body { pub pats: Arena, pub bindings: Arena, pub labels: Arena