diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs index 1fba2e2e4d348..f4564c94bb594 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs @@ -12,9 +12,6 @@ use std::sync::OnceLock; use rustc_hash::FxHashMap; -/// Ignored attribute namespaces used by tools. -pub const TOOL_MODULES: &[&str] = &["rustfmt", "clippy"]; - pub struct BuiltinAttribute { pub name: &'static str, pub template: AttributeTemplate, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 82f89393add6f..c6d9ba6cfe459 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -17,7 +17,7 @@ use syntax::{ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName, RangeItem, SlicePatComponents, }, - AstNode, AstPtr, SyntaxNodePtr, + AstNode, AstPtr, AstToken as _, SyntaxNodePtr, }; use triomphe::Arc; @@ -1577,7 +1577,13 @@ impl ExprCollector<'_> { }); }); let template = f.template(); - let fmt_snippet = template.as_ref().map(ToString::to_string); + let fmt_snippet = template.as_ref().and_then(|it| match it { + ast::Expr::Literal(literal) => match literal.kind() { + ast::LiteralKind::String(s) => Some(s.text().to_owned()), + _ => None, + }, + _ => None, + }); let mut mappings = vec![]; let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { Some((s, is_direct_literal)) => format_args::parse( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs index 4c8a54f7c8c72..e8b26d537345c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs @@ -150,7 +150,7 @@ fn desugar_builtin_format_args() { fn main() { let are = "are"; let count = 10; - builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); + builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); } "#, ); @@ -161,7 +161,7 @@ fn main() { let count = 10; builtin#lang(Arguments::new_v1_formatted)( &[ - "hello ", " ", " friends, we ", " ", "", + "\u{1b}hello ", " ", " friends, we ", " ", "", ], &[ builtin#lang(Argument::new_display)( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs index 985c6387ba0b0..f483efa85179b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs @@ -528,3 +528,65 @@ fn f() {$0 "#]], ) } + +#[test] +fn resolve_extern_prelude_in_block() { + check_at( + r#" +//- /main.rs crate:main deps:core +fn main() { + mod f { + use core::S; + $0 + } +} + +//- /core.rs crate:core +pub struct S; + "#, + expect![[r#" + block scope + f: t + + block scope::f + S: ti vi + + crate + main: v + "#]], + ) +} + +#[test] +fn shadow_extern_prelude_in_block() { + check_at( + r#" +//- /main.rs crate:main deps:core +fn main() { + mod core { pub struct S; } + { + fn inner() {} // forces a block def map + use core::S; // should resolve to the local one + $0 + } +} + +//- /core.rs crate:core +pub const S; + "#, + expect![[r#" + block scope + S: ti vi + inner: v + + block scope + core: t + + block scope::core + S: t v + + crate + main: v + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 4e57845a694d1..d9495d36c0d9f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -14,38 +14,21 @@ use crate::{ nameres::DefMap, path::{ModPath, PathKind}, visibility::{Visibility, VisibilityExplicitness}, - CrateRootModuleId, ModuleDefId, ModuleId, + ImportPathConfig, ModuleDefId, ModuleId, }; /// Find a path that can be used to refer to a certain item. This can depend on /// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path( - db: &dyn DefDatabase, - item: ItemInNs, - from: ModuleId, - prefer_no_std: bool, - prefer_prelude: bool, -) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "find_path").entered(); - find_path_inner(FindPathCtx { db, prefixed: None, prefer_no_std, prefer_prelude }, item, from) -} - -/// Find a path that can be used to refer to a certain item. This can depend on -/// *from where* you're referring to the item, hence the `from` parameter. -pub fn find_path_prefixed( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, prefix_kind: PrefixKind, - prefer_no_std: bool, - prefer_prelude: bool, + ignore_local_imports: bool, + cfg: ImportPathConfig, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "find_path_prefixed").entered(); - find_path_inner( - FindPathCtx { db, prefixed: Some(prefix_kind), prefer_no_std, prefer_prelude }, - item, - from, - ) + let _p = tracing::span!(tracing::Level::INFO, "find_path").entered(); + find_path_inner(FindPathCtx { db, prefix: prefix_kind, cfg, ignore_local_imports }, item, from) } #[derive(Copy, Clone, Debug)] @@ -70,7 +53,7 @@ pub enum PrefixKind { /// This is the same as plain, just that paths will start with `self` prepended if the path /// starts with an identifier that is not a crate. BySelf, - /// Causes paths to ignore imports in the local module. + /// Causes paths to not use a self, super or crate prefix. Plain, /// Causes paths to start with `crate` where applicable, effectively forcing paths to be absolute. ByCrate, @@ -78,48 +61,46 @@ pub enum PrefixKind { impl PrefixKind { #[inline] - fn prefix(self) -> PathKind { + fn path_kind(self) -> PathKind { match self { PrefixKind::BySelf => PathKind::Super(0), PrefixKind::Plain => PathKind::Plain, PrefixKind::ByCrate => PathKind::Crate, } } - - #[inline] - fn is_absolute(&self) -> bool { - self == &PrefixKind::ByCrate - } } #[derive(Copy, Clone)] struct FindPathCtx<'db> { db: &'db dyn DefDatabase, - prefixed: Option, - prefer_no_std: bool, - prefer_prelude: bool, + prefix: PrefixKind, + cfg: ImportPathConfig, + ignore_local_imports: bool, } /// 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()))); + return Some(ModPath::from_segments(PathKind::Plain, iter::once(builtin.as_name()))); } let def_map = from.def_map(ctx.db); - let crate_root = def_map.crate_root(); + let crate_root = from.derive_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( FindPathCtx { - prefer_no_std: ctx.prefer_no_std || ctx.db.crate_supports_no_std(crate_root.krate), + cfg: ImportPathConfig { + prefer_no_std: ctx.cfg.prefer_no_std + || ctx.db.crate_supports_no_std(crate_root.krate), + ..ctx.cfg + }, ..ctx }, &def_map, &mut visited_modules, - crate_root, from, module_id, MAX_PATH_LEN, @@ -127,11 +108,20 @@ fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Opti .map(|(item, _)| item); } - // - if the item is already in scope, return the name under which it is - let scope_name = find_in_scope(ctx.db, &def_map, from, item); - if ctx.prefixed.is_none() { + let prefix = if item.module(ctx.db).is_some_and(|it| it.is_within_block()) { + PrefixKind::Plain + } else { + ctx.prefix + }; + let may_be_in_scope = match prefix { + PrefixKind::Plain | PrefixKind::BySelf => true, + PrefixKind::ByCrate => from.is_crate_root(), + }; + if may_be_in_scope { + // - if the item is already in scope, return the name under which it is + let scope_name = find_in_scope(ctx.db, &def_map, from, item, ctx.ignore_local_imports); if let Some(scope_name) = scope_name { - return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name))); + return Some(ModPath::from_segments(prefix.path_kind(), iter::once(scope_name))); } } @@ -159,16 +149,18 @@ fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Opti calculate_best_path( FindPathCtx { - prefer_no_std: ctx.prefer_no_std || ctx.db.crate_supports_no_std(crate_root.krate), + cfg: ImportPathConfig { + prefer_no_std: ctx.cfg.prefer_no_std + || ctx.db.crate_supports_no_std(crate_root.krate), + ..ctx.cfg + }, ..ctx }, &def_map, &mut visited_modules, - crate_root, MAX_PATH_LEN, item, from, - scope_name, ) .map(|(item, _)| item) } @@ -178,7 +170,6 @@ fn find_path_for_module( ctx: FindPathCtx<'_>, def_map: &DefMap, visited_modules: &mut FxHashSet, - crate_root: CrateRootModuleId, from: ModuleId, module_id: ModuleId, max_len: usize, @@ -187,38 +178,25 @@ fn find_path_for_module( return None; } - // Base cases: - // - if the item is already in scope, return the name under which it is - 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)); - } - } - + let is_crate_root = module_id.as_crate_root(); // - if the item is the crate root, return `crate` - if module_id == crate_root { + if is_crate_root.is_some_and(|it| it == from.derive_crate_root()) { return Some((ModPath::from_segments(PathKind::Crate, None), Stable)); } - // - if relative paths are fine, check if we are searching for a parent - 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)); - } - } - + let root_def_map = from.derive_crate_root().def_map(ctx.db); // - 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(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()); - + if let Some(crate_root) = is_crate_root { + // rev here so we prefer looking at renamed extern decls first + for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude().rev() { + if crate_root != def_id { + continue; + } let name_already_occupied_in_type_ns = def_map .with_ancestor_maps(ctx.db, from.local_id, &mut |def_map, local_id| { def_map[local_id] .scope - .type_(&name) + .type_(name) .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into())) }) .is_some(); @@ -228,24 +206,51 @@ fn find_path_for_module( } else { PathKind::Plain }; - return Some((ModPath::from_segments(kind, Some(name)), Stable)); + return Some((ModPath::from_segments(kind, iter::once(name.clone())), Stable)); + } + } + let prefix = if module_id.is_within_block() { PrefixKind::Plain } else { ctx.prefix }; + let may_be_in_scope = match prefix { + PrefixKind::Plain | PrefixKind::BySelf => true, + PrefixKind::ByCrate => from.is_crate_root(), + }; + if may_be_in_scope { + let scope_name = find_in_scope( + ctx.db, + def_map, + from, + ItemInNs::Types(module_id.into()), + ctx.ignore_local_imports, + ); + if let Some(scope_name) = scope_name { + // - if the item is already in scope, return the name under which it is + return Some(( + ModPath::from_segments(prefix.path_kind(), iter::once(scope_name)), + Stable, + )); } } - if let value @ Some(_) = + // - if the module can be referenced as self, super or crate, do that + if let Some(mod_path) = is_kw_kind_relative_to_from(def_map, module_id, from) { + if ctx.prefix != PrefixKind::ByCrate || mod_path.kind == PathKind::Crate { + return Some((mod_path, Stable)); + } + } + + // - if the module is in the prelude, return it by that path + if let Some(mod_path) = find_in_prelude(ctx.db, &root_def_map, def_map, ItemInNs::Types(module_id.into()), from) { - return value.zip(Some(Stable)); + return Some((mod_path, Stable)); } calculate_best_path( ctx, def_map, visited_modules, - crate_root, max_len, ItemInNs::Types(module_id.into()), from, - scope_name, ) } @@ -255,9 +260,13 @@ fn find_in_scope( def_map: &DefMap, from: ModuleId, item: ItemInNs, + ignore_local_imports: bool, ) -> Option { + // FIXME: We could have multiple applicable names here, but we currently only return the first def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { - def_map[local_id].scope.names_of(item, |name, _, _| Some(name.clone())) + def_map[local_id].scope.names_of(item, |name, _, declared| { + (declared || !ignore_local_imports).then(|| name.clone()) + }) }) } @@ -292,21 +301,32 @@ fn find_in_prelude( }); if found_and_same_def.unwrap_or(true) { - Some(ModPath::from_segments(PathKind::Plain, Some(name.clone()))) + Some(ModPath::from_segments(PathKind::Plain, iter::once(name.clone()))) } else { None } } -fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option { +fn is_kw_kind_relative_to_from( + def_map: &DefMap, + item: ModuleId, + from: ModuleId, +) -> Option { + if item.krate != from.krate || item.is_within_block() || from.is_within_block() { + return None; + } + let item = item.local_id; + let from = from.local_id; if item == from { // - if the item is the module we're in, use `self` Some(ModPath::from_segments(PathKind::Super(0), None)) - } else if let Some(parent_id) = def_map[from.local_id].parent { - // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) - let parent_id = def_map.module_id(parent_id); + } else if let Some(parent_id) = def_map[from].parent { if item == parent_id { - Some(ModPath::from_segments(PathKind::Super(1), None)) + // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) + Some(ModPath::from_segments( + if parent_id == DefMap::ROOT { PathKind::Crate } else { PathKind::Super(1) }, + None, + )) } else { None } @@ -320,11 +340,9 @@ fn calculate_best_path( ctx: FindPathCtx<'_>, def_map: &DefMap, visited_modules: &mut FxHashSet, - crate_root: CrateRootModuleId, max_len: usize, item: ItemInNs, from: ModuleId, - scope_name: Option, ) -> Option<(ModPath, Stability)> { if max_len <= 1 { return None; @@ -346,14 +364,12 @@ fn calculate_best_path( // dependency in this case. 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( ctx, def_map, visited_modules, - crate_root, from, module_id, best_path_len - 1, @@ -361,9 +377,7 @@ fn calculate_best_path( path.0.push_segment(name); let new_path = match best_path.take() { - Some(best_path) => { - select_best_path(best_path, path, ctx.prefer_no_std, ctx.prefer_prelude) - } + Some(best_path) => select_best_path(best_path, path, ctx.cfg), None => path, }; best_path_len = new_path.0.len(); @@ -390,7 +404,6 @@ fn calculate_best_path( ctx, def_map, visited_modules, - crate_root, from, info.container, max_len - 1, @@ -406,31 +419,14 @@ fn calculate_best_path( ); let new_path_with_stab = match best_path.take() { - Some(best_path) => select_best_path( - best_path, - path_with_stab, - ctx.prefer_no_std, - ctx.prefer_prelude, - ), + Some(best_path) => select_best_path(best_path, path_with_stab, ctx.cfg), None => path_with_stab, }; update_best_path(&mut best_path, new_path_with_stab); } } } - 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); - } - } - match prefixed.map(PrefixKind::prefix) { - Some(prefix) => best_path.or_else(|| { - scope_name.map(|scope_name| (ModPath::from_segments(prefix, Some(scope_name)), Stable)) - }), - None => best_path, - } + best_path } /// Select the best (most relevant) path between two paths. @@ -439,8 +435,7 @@ fn calculate_best_path( fn select_best_path( old_path @ (_, old_stability): (ModPath, Stability), new_path @ (_, new_stability): (ModPath, Stability), - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> (ModPath, Stability) { match (old_stability, new_stability) { (Stable, Unstable) => return old_path, @@ -454,7 +449,7 @@ fn select_best_path( let (old_path, _) = &old; let new_has_prelude = new_path.segments().iter().any(|seg| seg == &known::prelude); let old_has_prelude = old_path.segments().iter().any(|seg| seg == &known::prelude); - match (new_has_prelude, old_has_prelude, prefer_prelude) { + match (new_has_prelude, old_has_prelude, cfg.prefer_prelude) { (true, false, true) | (false, true, false) => new, (true, false, false) | (false, true, true) => old, // no prelude difference in the paths, so pick the shorter one @@ -475,7 +470,7 @@ fn select_best_path( match (old_path.0.segments().first(), new_path.0.segments().first()) { (Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => { - let rank = match prefer_no_std { + let rank = match cfg.prefer_no_std { false => |name: &Name| match name { name if name == &known::core => 0, name if name == &known::alloc => 1, @@ -535,7 +530,6 @@ fn find_local_import_locations( if !seen.insert(module) { continue; // already processed this module } - let ext_def_map; let data = if module.krate == from.krate { if module.block.is_some() { @@ -571,7 +565,7 @@ fn find_local_import_locations( // 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_pub_or_explicit || declared { + if declared || is_pub_or_explicit { locations.push((module, name.clone())); } } @@ -590,7 +584,10 @@ fn find_local_import_locations( #[cfg(test)] mod tests { + use expect_test::{expect, Expect}; use hir_expand::db::ExpandDatabase; + use itertools::Itertools; + use stdx::format_to; use syntax::ast::AstNode; use test_fixture::WithFixture; @@ -602,12 +599,7 @@ mod tests { /// item the `path` refers to returns that same path when called from the /// module the cursor is in. #[track_caller] - fn check_found_path_( - ra_fixture: &str, - path: &str, - prefix_kind: Option, - prefer_prelude: bool, - ) { + fn check_found_path_(ra_fixture: &str, path: &str, prefer_prelude: bool, expect: Expect) { let (db, pos) = TestDB::with_position(ra_fixture); let module = db.module_at_position(pos); let parsed_path_file = @@ -628,43 +620,47 @@ mod tests { crate::item_scope::BuiltinShadowMode::Module, None, ) - .0 + .0; + let resolved = resolved .take_types() - .expect("path does not resolve to a type"); + .map(ItemInNs::Types) + .or_else(|| resolved.take_values().map(ItemInNs::Values)) + .expect("path does not resolve to a type or value"); + + let mut res = String::new(); + for (prefix, ignore_local_imports) in + [PrefixKind::Plain, PrefixKind::ByCrate, PrefixKind::BySelf] + .into_iter() + .cartesian_product([false, true]) + { + let found_path = find_path_inner( + FindPathCtx { + db: &db, + prefix, + cfg: ImportPathConfig { prefer_no_std: false, prefer_prelude }, + ignore_local_imports, + }, + resolved, + module, + ); + format_to!( + res, + "{:7}(imports {}): {}\n", + format!("{:?}", prefix), + if ignore_local_imports { '✖' } else { '✔' }, + found_path + .map_or_else(|| "".to_owned(), |it| it.display(&db).to_string()), + ); + } + expect.assert_eq(&res); + } - let found_path = find_path_inner( - FindPathCtx { prefer_no_std: false, db: &db, prefixed: prefix_kind, prefer_prelude }, - ItemInNs::Types(resolved), - module, - ); - assert_eq!(found_path, Some(mod_path), "on kind: {prefix_kind:?}"); + fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) { + check_found_path_(ra_fixture, path, false, expect); } - #[track_caller] - fn check_found_path( - ra_fixture: &str, - unprefixed: &str, - prefixed: &str, - absolute: &str, - self_prefixed: &str, - ) { - check_found_path_(ra_fixture, unprefixed, None, false); - check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), false); - check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), false); - check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), false); - } - - fn check_found_path_prelude( - ra_fixture: &str, - unprefixed: &str, - prefixed: &str, - absolute: &str, - self_prefixed: &str, - ) { - check_found_path_(ra_fixture, unprefixed, None, true); - check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain), true); - check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate), true); - check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf), true); + fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) { + check_found_path_(ra_fixture, path, true, expect); } #[test] @@ -675,9 +671,14 @@ struct S; $0 "#, "S", - "S", - "crate::S", - "self::S", + expect![[r#" + Plain (imports ✔): S + Plain (imports ✖): S + ByCrate(imports ✔): crate::S + ByCrate(imports ✖): crate::S + BySelf (imports ✔): self::S + BySelf (imports ✖): self::S + "#]], ); } @@ -689,9 +690,14 @@ enum E { A } $0 "#, "E::A", - "E::A", - "crate::E::A", - "self::E::A", + expect![[r#" + Plain (imports ✔): E::A + Plain (imports ✖): E::A + ByCrate(imports ✔): crate::E::A + ByCrate(imports ✖): crate::E::A + BySelf (imports ✔): self::E::A + BySelf (imports ✖): self::E::A + "#]], ); } @@ -705,9 +711,14 @@ mod foo { $0 "#, "foo::S", - "foo::S", - "crate::foo::S", - "self::foo::S", + expect![[r#" + Plain (imports ✔): foo::S + Plain (imports ✖): foo::S + ByCrate(imports ✔): crate::foo::S + ByCrate(imports ✖): crate::foo::S + BySelf (imports ✔): self::foo::S + BySelf (imports ✖): self::foo::S + "#]], ); } @@ -724,9 +735,14 @@ struct S; $0 "#, "super::S", - "super::S", - "crate::foo::S", - "super::S", + expect![[r#" + Plain (imports ✔): super::S + Plain (imports ✖): super::S + ByCrate(imports ✔): crate::foo::S + ByCrate(imports ✖): crate::foo::S + BySelf (imports ✔): super::S + BySelf (imports ✖): super::S + "#]], ); } @@ -740,9 +756,14 @@ mod foo; $0 "#, "self", - "self", - "crate::foo", - "self", + expect![[r#" + Plain (imports ✔): self + Plain (imports ✖): self + ByCrate(imports ✔): crate::foo + ByCrate(imports ✖): crate::foo + BySelf (imports ✔): self + BySelf (imports ✖): self + "#]], ); } @@ -756,9 +777,14 @@ mod foo; $0 "#, "crate", - "crate", - "crate", - "crate", + expect![[r#" + Plain (imports ✔): crate + Plain (imports ✖): crate + ByCrate(imports ✔): crate + ByCrate(imports ✖): crate + BySelf (imports ✔): crate + BySelf (imports ✖): crate + "#]], ); } @@ -773,9 +799,14 @@ struct S; $0 "#, "crate::S", - "crate::S", - "crate::S", - "crate::S", + expect![[r#" + Plain (imports ✔): crate::S + Plain (imports ✖): crate::S + ByCrate(imports ✔): crate::S + ByCrate(imports ✖): crate::S + BySelf (imports ✔): crate::S + BySelf (imports ✖): crate::S + "#]], ); } @@ -789,9 +820,14 @@ $0 pub struct S; "#, "std::S", - "std::S", - "std::S", - "std::S", + expect![[r#" + Plain (imports ✔): std::S + Plain (imports ✖): std::S + ByCrate(imports ✔): std::S + ByCrate(imports ✖): std::S + BySelf (imports ✔): std::S + BySelf (imports ✖): std::S + "#]], ); } @@ -806,9 +842,14 @@ $0 pub struct S; "#, "std_renamed::S", - "std_renamed::S", - "std_renamed::S", - "std_renamed::S", + expect![[r#" + Plain (imports ✔): std_renamed::S + Plain (imports ✖): std_renamed::S + ByCrate(imports ✔): std_renamed::S + ByCrate(imports ✖): std_renamed::S + BySelf (imports ✔): std_renamed::S + BySelf (imports ✖): std_renamed::S + "#]], ); } @@ -831,10 +872,15 @@ pub mod ast { } } "#, - "ast::ModuleItem", - "syntax::ast::ModuleItem", - "syntax::ast::ModuleItem", "syntax::ast::ModuleItem", + expect![[r#" + Plain (imports ✔): ast::ModuleItem + Plain (imports ✖): syntax::ast::ModuleItem + ByCrate(imports ✔): crate::ast::ModuleItem + ByCrate(imports ✖): syntax::ast::ModuleItem + BySelf (imports ✔): self::ast::ModuleItem + BySelf (imports ✖): syntax::ast::ModuleItem + "#]], ); check_found_path( @@ -850,9 +896,14 @@ pub mod ast { } "#, "syntax::ast::ModuleItem", - "syntax::ast::ModuleItem", - "syntax::ast::ModuleItem", - "syntax::ast::ModuleItem", + expect![[r#" + Plain (imports ✔): syntax::ast::ModuleItem + Plain (imports ✖): syntax::ast::ModuleItem + ByCrate(imports ✔): syntax::ast::ModuleItem + ByCrate(imports ✖): syntax::ast::ModuleItem + BySelf (imports ✔): syntax::ast::ModuleItem + BySelf (imports ✖): syntax::ast::ModuleItem + "#]], ); } @@ -867,9 +918,14 @@ mod bar { $0 "#, "bar::S", - "bar::S", - "crate::bar::S", - "self::bar::S", + expect![[r#" + Plain (imports ✔): bar::S + Plain (imports ✖): bar::S + ByCrate(imports ✔): crate::bar::S + ByCrate(imports ✖): crate::bar::S + BySelf (imports ✔): self::bar::S + BySelf (imports ✖): self::bar::S + "#]], ); } @@ -884,9 +940,14 @@ mod bar { $0 "#, "bar::U", - "bar::U", - "crate::bar::U", - "self::bar::U", + expect![[r#" + Plain (imports ✔): bar::U + Plain (imports ✖): bar::U + ByCrate(imports ✔): crate::bar::U + ByCrate(imports ✖): crate::bar::U + BySelf (imports ✔): self::bar::U + BySelf (imports ✖): self::bar::U + "#]], ); } @@ -902,9 +963,14 @@ pub use core::S; pub struct S; "#, "std::S", - "std::S", - "std::S", - "std::S", + expect![[r#" + Plain (imports ✔): std::S + Plain (imports ✖): std::S + ByCrate(imports ✔): std::S + ByCrate(imports ✖): std::S + BySelf (imports ✔): std::S + BySelf (imports ✖): std::S + "#]], ); } @@ -922,9 +988,14 @@ pub mod prelude { } "#, "S", - "S", - "S", - "S", + expect![[r#" + Plain (imports ✔): S + Plain (imports ✖): S + ByCrate(imports ✔): S + ByCrate(imports ✖): S + BySelf (imports ✔): S + BySelf (imports ✖): S + "#]], ); } @@ -943,9 +1014,14 @@ pub mod prelude { } "#, "std::prelude::rust_2018::S", - "std::prelude::rust_2018::S", - "std::prelude::rust_2018::S", - "std::prelude::rust_2018::S", + expect![[r#" + Plain (imports ✔): std::prelude::rust_2018::S + Plain (imports ✖): std::prelude::rust_2018::S + ByCrate(imports ✔): std::prelude::rust_2018::S + ByCrate(imports ✖): std::prelude::rust_2018::S + BySelf (imports ✔): std::prelude::rust_2018::S + BySelf (imports ✖): std::prelude::rust_2018::S + "#]], ); } @@ -964,9 +1040,14 @@ pub mod prelude { } "#, "S", - "S", - "S", - "S", + expect![[r#" + Plain (imports ✔): S + Plain (imports ✖): S + ByCrate(imports ✔): crate::S + ByCrate(imports ✖): S + BySelf (imports ✔): self::S + BySelf (imports ✖): S + "#]], ); } @@ -983,8 +1064,30 @@ pub mod prelude { } } "#; - check_found_path(code, "None", "None", "None", "None"); - check_found_path(code, "Some", "Some", "Some", "Some"); + check_found_path( + code, + "None", + expect![[r#" + Plain (imports ✔): None + Plain (imports ✖): None + ByCrate(imports ✔): None + ByCrate(imports ✖): None + BySelf (imports ✔): None + BySelf (imports ✖): None + "#]], + ); + check_found_path( + code, + "Some", + expect![[r#" + Plain (imports ✔): Some + Plain (imports ✖): Some + ByCrate(imports ✔): Some + ByCrate(imports ✖): Some + BySelf (imports ✔): Some + BySelf (imports ✖): Some + "#]], + ); } #[test] @@ -1002,9 +1105,14 @@ pub mod bar { pub struct S; } pub use crate::foo::bar::S; "#, "baz::S", - "baz::S", - "crate::baz::S", - "self::baz::S", + expect![[r#" + Plain (imports ✔): baz::S + Plain (imports ✖): baz::S + ByCrate(imports ✔): crate::baz::S + ByCrate(imports ✖): crate::baz::S + BySelf (imports ✔): self::baz::S + BySelf (imports ✖): self::baz::S + "#]], ); } @@ -1022,9 +1130,14 @@ $0 "#, // crate::S would be shorter, but using private imports seems wrong "crate::bar::S", - "crate::bar::S", - "crate::bar::S", - "crate::bar::S", + expect![[r#" + Plain (imports ✔): crate::bar::S + Plain (imports ✖): crate::bar::S + ByCrate(imports ✔): crate::bar::S + ByCrate(imports ✖): crate::bar::S + BySelf (imports ✔): crate::bar::S + BySelf (imports ✖): crate::bar::S + "#]], ); } @@ -1041,9 +1154,14 @@ pub(crate) use bar::S; $0 "#, "crate::S", - "crate::S", - "crate::S", - "crate::S", + expect![[r#" + Plain (imports ✔): crate::S + Plain (imports ✖): crate::S + ByCrate(imports ✔): crate::S + ByCrate(imports ✖): crate::S + BySelf (imports ✔): crate::S + BySelf (imports ✖): crate::S + "#]], ); } @@ -1063,9 +1181,14 @@ pub mod bar { $0 "#, "super::S", - "super::S", - "crate::bar::S", - "super::S", + expect![[r#" + Plain (imports ✔): super::S + Plain (imports ✖): super::S + ByCrate(imports ✔): crate::bar::S + ByCrate(imports ✖): crate::bar::S + BySelf (imports ✔): super::S + BySelf (imports ✖): super::S + "#]], ); } @@ -1086,9 +1209,14 @@ pub struct S; pub use super::foo; "#, "crate::foo::S", - "crate::foo::S", - "crate::foo::S", - "crate::foo::S", + expect![[r#" + Plain (imports ✔): crate::foo::S + Plain (imports ✖): crate::foo::S + ByCrate(imports ✔): crate::foo::S + ByCrate(imports ✖): crate::foo::S + BySelf (imports ✔): crate::foo::S + BySelf (imports ✖): crate::foo::S + "#]], ); } @@ -1110,9 +1238,14 @@ pub mod sync { } "#, "std::sync::Arc", - "std::sync::Arc", - "std::sync::Arc", - "std::sync::Arc", + expect![[r#" + Plain (imports ✔): std::sync::Arc + Plain (imports ✖): std::sync::Arc + ByCrate(imports ✔): std::sync::Arc + ByCrate(imports ✖): std::sync::Arc + BySelf (imports ✔): std::sync::Arc + BySelf (imports ✖): std::sync::Arc + "#]], ); } @@ -1138,9 +1271,14 @@ pub mod fmt { } "#, "core::fmt::Error", - "core::fmt::Error", - "core::fmt::Error", - "core::fmt::Error", + expect![[r#" + Plain (imports ✔): core::fmt::Error + Plain (imports ✖): core::fmt::Error + ByCrate(imports ✔): core::fmt::Error + ByCrate(imports ✖): core::fmt::Error + BySelf (imports ✔): core::fmt::Error + BySelf (imports ✖): core::fmt::Error + "#]], ); // Should also work (on a best-effort basis) if `no_std` is conditional. @@ -1164,9 +1302,14 @@ pub mod fmt { } "#, "core::fmt::Error", - "core::fmt::Error", - "core::fmt::Error", - "core::fmt::Error", + expect![[r#" + Plain (imports ✔): core::fmt::Error + Plain (imports ✖): core::fmt::Error + ByCrate(imports ✔): core::fmt::Error + ByCrate(imports ✖): core::fmt::Error + BySelf (imports ✔): core::fmt::Error + BySelf (imports ✖): core::fmt::Error + "#]], ); } @@ -1194,9 +1337,14 @@ pub mod sync { } "#, "alloc::sync::Arc", - "alloc::sync::Arc", - "alloc::sync::Arc", - "alloc::sync::Arc", + expect![[r#" + Plain (imports ✔): alloc::sync::Arc + Plain (imports ✖): alloc::sync::Arc + ByCrate(imports ✔): alloc::sync::Arc + ByCrate(imports ✖): alloc::sync::Arc + BySelf (imports ✔): alloc::sync::Arc + BySelf (imports ✖): alloc::sync::Arc + "#]], ); } @@ -1216,9 +1364,14 @@ pub mod sync { pub struct Arc; "#, "megaalloc::Arc", - "megaalloc::Arc", - "megaalloc::Arc", - "megaalloc::Arc", + expect![[r#" + Plain (imports ✔): megaalloc::Arc + Plain (imports ✖): megaalloc::Arc + ByCrate(imports ✔): megaalloc::Arc + ByCrate(imports ✖): megaalloc::Arc + BySelf (imports ✔): megaalloc::Arc + BySelf (imports ✖): megaalloc::Arc + "#]], ); } @@ -1231,8 +1384,30 @@ pub mod primitive { pub use u8; } "#; - check_found_path(code, "u8", "u8", "u8", "u8"); - check_found_path(code, "u16", "u16", "u16", "u16"); + check_found_path( + code, + "u8", + expect![[r#" + Plain (imports ✔): u8 + Plain (imports ✖): u8 + ByCrate(imports ✔): u8 + ByCrate(imports ✖): u8 + BySelf (imports ✔): u8 + BySelf (imports ✖): u8 + "#]], + ); + check_found_path( + code, + "u16", + expect![[r#" + Plain (imports ✔): u16 + Plain (imports ✖): u16 + ByCrate(imports ✔): u16 + ByCrate(imports ✖): u16 + BySelf (imports ✔): u16 + BySelf (imports ✖): u16 + "#]], + ); } #[test] @@ -1245,9 +1420,14 @@ fn main() { } "#, "Inner", - "Inner", - "Inner", - "Inner", + expect![[r#" + Plain (imports ✔): Inner + Plain (imports ✖): Inner + ByCrate(imports ✔): Inner + ByCrate(imports ✖): Inner + BySelf (imports ✔): Inner + BySelf (imports ✖): Inner + "#]], ); } @@ -1263,20 +1443,24 @@ fn main() { } "#, "Struct", - "Struct", - "Struct", - "Struct", + expect![[r#" + Plain (imports ✔): Struct + Plain (imports ✖): Struct + ByCrate(imports ✔): Struct + ByCrate(imports ✖): Struct + BySelf (imports ✔): Struct + BySelf (imports ✖): Struct + "#]], ); } #[test] fn inner_items_from_inner_module() { - cov_mark::check!(prefixed_in_block_expression); check_found_path( r#" fn main() { mod module { - struct Struct {} + pub struct Struct {} } { $0 @@ -1284,9 +1468,14 @@ fn main() { } "#, "module::Struct", - "module::Struct", - "module::Struct", - "module::Struct", + expect![[r#" + Plain (imports ✔): module::Struct + Plain (imports ✖): module::Struct + ByCrate(imports ✔): module::Struct + ByCrate(imports ✖): module::Struct + BySelf (imports ✔): module::Struct + BySelf (imports ✖): module::Struct + "#]], ); } @@ -1303,11 +1492,15 @@ fn main() { $0 } "#, - // FIXME: these could use fewer/better prefixes "module::CompleteMe", - "crate::module::CompleteMe", - "crate::module::CompleteMe", - "crate::module::CompleteMe", + expect![[r#" + Plain (imports ✔): module::CompleteMe + Plain (imports ✖): module::CompleteMe + ByCrate(imports ✔): crate::module::CompleteMe + ByCrate(imports ✖): crate::module::CompleteMe + BySelf (imports ✔): self::module::CompleteMe + BySelf (imports ✖): self::module::CompleteMe + "#]], ) } @@ -1328,9 +1521,14 @@ mod bar { } "#, "crate::baz::Foo", - "crate::baz::Foo", - "crate::baz::Foo", - "crate::baz::Foo", + expect![[r#" + Plain (imports ✔): crate::baz::Foo + Plain (imports ✖): crate::baz::Foo + ByCrate(imports ✔): crate::baz::Foo + ByCrate(imports ✖): crate::baz::Foo + BySelf (imports ✔): crate::baz::Foo + BySelf (imports ✖): crate::baz::Foo + "#]], ) } @@ -1350,15 +1548,19 @@ mod bar { } "#, "crate::baz::Foo", - "crate::baz::Foo", - "crate::baz::Foo", - "crate::baz::Foo", + expect![[r#" + Plain (imports ✔): crate::baz::Foo + Plain (imports ✖): crate::baz::Foo + ByCrate(imports ✔): crate::baz::Foo + ByCrate(imports ✖): crate::baz::Foo + BySelf (imports ✔): crate::baz::Foo + BySelf (imports ✖): crate::baz::Foo + "#]], ) } #[test] fn recursive_pub_mod_reexport() { - cov_mark::check!(recursive_imports); check_found_path( r#" fn main() { @@ -1378,9 +1580,14 @@ pub mod name { } "#, "name::AsName", - "name::AsName", - "crate::name::AsName", - "self::name::AsName", + expect![[r#" + Plain (imports ✔): name::AsName + Plain (imports ✖): name::AsName + ByCrate(imports ✔): crate::name::AsName + ByCrate(imports ✖): crate::name::AsName + BySelf (imports ✔): self::name::AsName + BySelf (imports ✖): self::name::AsName + "#]], ); } @@ -1393,9 +1600,14 @@ $0 //- /dep.rs crate:dep "#, "dep", - "dep", - "dep", - "dep", + expect![[r#" + Plain (imports ✔): dep + Plain (imports ✖): dep + ByCrate(imports ✔): dep + ByCrate(imports ✖): dep + BySelf (imports ✔): dep + BySelf (imports ✖): dep + "#]], ); check_found_path( @@ -1408,9 +1620,14 @@ fn f() { //- /dep.rs crate:dep "#, "dep", - "dep", - "dep", - "dep", + expect![[r#" + Plain (imports ✔): dep + Plain (imports ✖): dep + ByCrate(imports ✔): dep + ByCrate(imports ✖): dep + BySelf (imports ✔): dep + BySelf (imports ✖): dep + "#]], ); } @@ -1432,9 +1649,14 @@ pub mod prelude { } "#, "None", - "None", - "None", - "None", + expect![[r#" + Plain (imports ✔): None + Plain (imports ✖): None + ByCrate(imports ✔): None + ByCrate(imports ✖): None + BySelf (imports ✔): None + BySelf (imports ✖): None + "#]], ); } @@ -1450,9 +1672,14 @@ pub extern crate std as std_renamed; pub struct S; "#, "intermediate::std_renamed::S", - "intermediate::std_renamed::S", - "intermediate::std_renamed::S", - "intermediate::std_renamed::S", + expect![[r#" + Plain (imports ✔): intermediate::std_renamed::S + Plain (imports ✖): intermediate::std_renamed::S + ByCrate(imports ✔): intermediate::std_renamed::S + ByCrate(imports ✖): intermediate::std_renamed::S + BySelf (imports ✔): intermediate::std_renamed::S + BySelf (imports ✖): intermediate::std_renamed::S + "#]], ); } @@ -1470,9 +1697,14 @@ pub extern crate std as longer; pub struct S; "#, "intermediate::longer::S", - "intermediate::longer::S", - "intermediate::longer::S", - "intermediate::longer::S", + expect![[r#" + Plain (imports ✔): intermediate::longer::S + Plain (imports ✖): intermediate::longer::S + ByCrate(imports ✔): intermediate::longer::S + ByCrate(imports ✖): intermediate::longer::S + BySelf (imports ✔): intermediate::longer::S + BySelf (imports ✖): intermediate::longer::S + "#]], ); } @@ -1493,9 +1725,14 @@ pub mod ops { } "#, "std::ops::Deref", - "std::ops::Deref", - "std::ops::Deref", - "std::ops::Deref", + expect![[r#" + Plain (imports ✔): std::ops::Deref + Plain (imports ✖): std::ops::Deref + ByCrate(imports ✔): std::ops::Deref + ByCrate(imports ✖): std::ops::Deref + BySelf (imports ✔): std::ops::Deref + BySelf (imports ✖): std::ops::Deref + "#]], ); } @@ -1518,9 +1755,14 @@ pub mod error { } "#, "std::error::Error", - "std::error::Error", - "std::error::Error", - "std::error::Error", + expect![[r#" + Plain (imports ✔): std::error::Error + Plain (imports ✖): std::error::Error + ByCrate(imports ✔): std::error::Error + ByCrate(imports ✖): std::error::Error + BySelf (imports ✔): std::error::Error + BySelf (imports ✖): std::error::Error + "#]], ); } @@ -1541,16 +1783,26 @@ pub mod foo { check_found_path( ra_fixture, "krate::foo::Foo", - "krate::foo::Foo", - "krate::foo::Foo", - "krate::foo::Foo", + expect![[r#" + Plain (imports ✔): krate::foo::Foo + Plain (imports ✖): krate::foo::Foo + ByCrate(imports ✔): krate::foo::Foo + ByCrate(imports ✖): krate::foo::Foo + BySelf (imports ✔): krate::foo::Foo + BySelf (imports ✖): krate::foo::Foo + "#]], ); check_found_path_prelude( ra_fixture, "krate::prelude::Foo", - "krate::prelude::Foo", - "krate::prelude::Foo", - "krate::prelude::Foo", + expect![[r#" + Plain (imports ✔): krate::prelude::Foo + Plain (imports ✖): krate::prelude::Foo + ByCrate(imports ✔): krate::prelude::Foo + ByCrate(imports ✖): krate::prelude::Foo + BySelf (imports ✔): krate::prelude::Foo + BySelf (imports ✖): krate::prelude::Foo + "#]], ); } @@ -1582,9 +1834,40 @@ pub mod prelude { } "#, "petgraph::graph::NodeIndex", - "petgraph::graph::NodeIndex", - "petgraph::graph::NodeIndex", - "petgraph::graph::NodeIndex", + expect![[r#" + Plain (imports ✔): petgraph::graph::NodeIndex + Plain (imports ✖): petgraph::graph::NodeIndex + ByCrate(imports ✔): petgraph::graph::NodeIndex + ByCrate(imports ✖): petgraph::graph::NodeIndex + BySelf (imports ✔): petgraph::graph::NodeIndex + BySelf (imports ✖): petgraph::graph::NodeIndex + "#]], + ); + } + + #[test] + fn regression_17271() { + check_found_path( + r#" +//- /lib.rs crate:main +mod foo; + +//- /foo.rs +mod bar; + +pub fn b() {$0} +//- /foo/bar.rs +pub fn c() {} +"#, + "bar::c", + expect![[r#" + Plain (imports ✔): bar::c + Plain (imports ✖): bar::c + ByCrate(imports ✔): crate::foo::bar::c + ByCrate(imports ✖): crate::foo::bar::c + BySelf (imports ✔): self::bar::c + BySelf (imports ✖): self::bar::c + "#]], ); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index b097a721c7586..de0fa982c86a2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -175,7 +175,13 @@ pub(crate) fn parse( mut synth: impl FnMut(Name) -> ExprId, mut record_usage: impl FnMut(Name, Option), ) -> FormatArgs { - let text = s.text_without_quotes(); + let Ok(text) = s.value() else { + return FormatArgs { + template: Default::default(), + arguments: args.finish(), + orphans: vec![], + }; + }; let str_style = match s.quote_offsets() { Some(offsets) => { let raw = usize::from(offsets.quotes.0.len()) - 1; @@ -186,7 +192,7 @@ pub(crate) fn parse( }; let mut parser = - parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format); + parse::Parser::new(&text, str_style, fmt_snippet, false, parse::ParseMode::Format); let mut pieces = Vec::new(); while let Some(piece) = parser.next() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index faa1eed15a45a..6e40293dbf8e6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -1,13 +1,12 @@ //! A map of all publicly exported items in a crate. -use std::{fmt, hash::BuildHasherDefault}; +use std::fmt; use base_db::CrateId; use fst::{raw::IndexedValue, Automaton, Streamer}; use hir_expand::name::Name; -use indexmap::IndexMap; use itertools::Itertools; -use rustc_hash::{FxHashSet, FxHasher}; +use rustc_hash::FxHashSet; use smallvec::SmallVec; use stdx::{format_to, TupleExt}; use triomphe::Arc; @@ -17,7 +16,7 @@ use crate::{ item_scope::{ImportOrExternCrate, ItemInNs}, nameres::DefMap, visibility::Visibility, - AssocItemId, ModuleDefId, ModuleId, TraitId, + AssocItemId, FxIndexMap, ModuleDefId, ModuleId, TraitId, }; /// Item import details stored in the `ImportMap`. @@ -58,7 +57,6 @@ enum IsTraitAssocItem { No, } -type FxIndexMap = IndexMap>; type ImportMapIndex = FxIndexMap, IsTraitAssocItem)>; impl ImportMap { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 54cd57110ea84..9c7dfa05b0e4f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -295,7 +295,7 @@ impl ItemScope { pub(crate) fn names_of( &self, item: ItemInNs, - mut cb: impl FnMut(&Name, Visibility, bool) -> Option, + mut cb: impl FnMut(&Name, Visibility, /*declared*/ bool) -> Option, ) -> Option { match item { ItemInNs::Macros(def) => self diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 88d4572196cde..682d169adb11b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -106,6 +106,18 @@ use crate::{ }, }; +type FxIndexMap = + indexmap::IndexMap>; +/// A wrapper around two booleans, [`ImportPathConfig::prefer_no_std`] and [`ImportPathConfig::prefer_prelude`]. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub struct ImportPathConfig { + /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate + /// over the std. + pub prefer_no_std: bool, + /// If true, prefer import paths containing a prelude module. + pub prefer_prelude: bool, +} + #[derive(Debug)] pub struct ItemLoc { pub container: ModuleId, @@ -455,6 +467,26 @@ impl ModuleId { pub fn is_block_module(self) -> bool { self.block.is_some() && self.local_id == DefMap::ROOT } + + pub fn is_within_block(self) -> bool { + self.block.is_some() + } + + pub fn as_crate_root(&self) -> Option { + if self.local_id == DefMap::ROOT && self.block.is_none() { + Some(CrateRootModuleId { krate: self.krate }) + } else { + None + } + } + + pub fn derive_crate_root(&self) -> CrateRootModuleId { + CrateRootModuleId { krate: self.krate } + } + + fn is_crate_root(&self) -> bool { + self.local_id == DefMap::ROOT && self.block.is_none() + } } impl PartialEq for ModuleId { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index a528c4cc6972d..a3eab22fc49c4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -81,9 +81,17 @@ use crate::{ per_ns::PerNs, visibility::{Visibility, VisibilityExplicitness}, AstId, BlockId, BlockLoc, CrateRootModuleId, EnumId, EnumVariantId, ExternCrateId, FunctionId, - LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, + FxIndexMap, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; +const PREDEFINED_TOOLS: &[SmolStr] = &[ + SmolStr::new_static("clippy"), + SmolStr::new_static("rustfmt"), + SmolStr::new_static("diagnostic"), + SmolStr::new_static("miri"), + SmolStr::new_static("rust_analyzer"), +]; + /// Contains the results of (early) name resolution. /// /// A `DefMap` stores the module tree and the definitions that are in scope in every module after @@ -129,7 +137,7 @@ pub struct DefMap { #[derive(Clone, Debug, PartialEq, Eq)] struct DefMapCrateData { /// The extern prelude which contains all root modules of external crates that are in scope. - extern_prelude: FxHashMap)>, + extern_prelude: FxIndexMap)>, /// Side table for resolving derive helpers. exported_derives: FxHashMap>, @@ -155,12 +163,12 @@ struct DefMapCrateData { impl DefMapCrateData { fn new(edition: Edition) -> Self { Self { - extern_prelude: FxHashMap::default(), + extern_prelude: FxIndexMap::default(), exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), proc_macro_loading_error: None, registered_attrs: Vec::new(), - registered_tools: Vec::new(), + registered_tools: PREDEFINED_TOOLS.into(), unstable_features: FxHashSet::default(), rustc_coherence_is_core: false, no_core: false, @@ -578,7 +586,8 @@ impl DefMap { pub(crate) fn extern_prelude( &self, - ) -> impl Iterator))> + '_ { + ) -> impl DoubleEndedIterator))> + '_ + { self.data.extern_prelude.iter().map(|(name, &def)| (name, def)) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index eb7f4c05ae217..3cb0666edf9c3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -10,7 +10,7 @@ use syntax::{ast, SmolStr}; use triomphe::Arc; use crate::{ - attr::builtin::{find_builtin_attr_idx, TOOL_MODULES}, + attr::builtin::find_builtin_attr_idx, db::DefDatabase, item_scope::BuiltinShadowMode, nameres::path_resolution::ResolveMode, @@ -82,8 +82,7 @@ impl DefMap { let name = name.to_smol_str(); let pred = |n: &_| *n == name; - let registered = self.data.registered_tools.iter().map(SmolStr::as_str); - let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred); + let is_tool = self.data.registered_tools.iter().map(SmolStr::as_str).any(pred); // FIXME: tool modules can be shadowed by actual modules if is_tool { return true; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 262bc538b9415..587997c47361a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -395,6 +395,8 @@ impl DefCollector<'_> { .cfg() .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)); if is_cfg_enabled { + self.inject_prelude(); + ModCollector { def_collector: self, macro_depth: 0, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index ee29b89f3d3b5..d621f3a360a96 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -221,7 +221,7 @@ impl DefMap { None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment); - self.resolve_name_in_crate_root_or_extern_prelude(db, segment) + self.resolve_name_in_crate_root_or_extern_prelude(db, original_module, segment) } PathKind::Plain => { let (_, segment) = match segments.next() { @@ -470,9 +470,9 @@ impl DefMap { }; let extern_prelude = || { - if self.block.is_some() { - // Don't resolve extern prelude in block `DefMap`s, defer it to the crate def map so - // that blocks can properly shadow them + if self.block.is_some() && module == DefMap::ROOT { + // Don't resolve extern prelude in pseudo-modules of blocks, because + // they might been shadowed by local names. return PerNs::none(); } self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| { @@ -505,6 +505,7 @@ impl DefMap { fn resolve_name_in_crate_root_or_extern_prelude( &self, db: &dyn DefDatabase, + module: LocalModuleId, name: &Name, ) -> PerNs { let from_crate_root = match self.block { @@ -515,8 +516,8 @@ impl DefMap { None => self[Self::ROOT].scope.get(name), }; let from_extern_prelude = || { - if self.block.is_some() { - // Don't resolve extern prelude in block `DefMap`s. + if self.block.is_some() && module == DefMap::ROOT { + // Don't resolve extern prelude in pseudo-module of a block. return PerNs::none(); } self.data.extern_prelude.get(name).copied().map_or( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 1602b173858d9..9794963203bca 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1,12 +1,11 @@ //! Name resolution façade. -use std::{fmt, hash::BuildHasherDefault, iter, mem}; +use std::{fmt, iter, mem}; use base_db::CrateId; use hir_expand::{ name::{name, Name}, MacroDefId, }; -use indexmap::IndexMap; use intern::Interned; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; @@ -27,10 +26,10 @@ use crate::{ type_ref::LifetimeRef, visibility::{RawVisibility, Visibility}, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, - ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, - ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, - MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, - TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, + ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, + ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, + MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, + TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, }; #[derive(Debug, Clone)] @@ -957,7 +956,6 @@ fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { Some((res, import)) } -type FxIndexMap = IndexMap>; #[derive(Default)] struct ScopeNames { map: FxIndexMap>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index c010f5d22b681..5a9621bb69a47 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -13,7 +13,7 @@ use either::Either; use hir_def::{ data::adt::VariantData, db::DefDatabase, - find_path, + find_path::{self, PrefixKind}, generics::{TypeOrConstParamData, TypeParamProvenance}, item_scope::ItemInNs, lang_item::{LangItem, LangItemTarget}, @@ -21,7 +21,8 @@ use hir_def::{ path::{Path, PathKind}, type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, + HasModule, ImportPathConfig, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::name::Name; use intern::{Internable, Interned}; @@ -999,8 +1000,9 @@ impl HirDisplay for Ty { db.upcast(), ItemInNs::Types((*def_id).into()), module_id, + PrefixKind::Plain, false, - true, + ImportPathConfig { prefer_no_std: false, prefer_prelude: true }, ) { write!(f, "{}", path.display(f.db.upcast()))?; } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 38076fce8f8f5..4c12786362f03 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -933,8 +933,24 @@ impl InferenceContext<'_> { let prev_ret_coercion = mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone()))); + // FIXME: We should handle async blocks like we handle closures + let expected = &Expectation::has_type(ret_ty); let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - this.infer_block(tgt_expr, *id, statements, *tail, None, &Expectation::has_type(ret_ty)) + let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected); + if let Some(target) = expected.only_has_type(&mut this.table) { + match this.coerce(Some(tgt_expr), &ty, &target) { + Ok(res) => res, + Err(_) => { + this.result.type_mismatches.insert( + tgt_expr.into(), + TypeMismatch { expected: target.clone(), actual: ty.clone() }, + ); + target + } + } + } else { + ty + } }); self.diverges = prev_diverges; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index b68fefc5150b1..36e3a458898da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -797,19 +797,22 @@ impl<'a> InferenceTable<'a> { }) .build(); - let projection = { - let b = TyBuilder::subst_for_def(self.db, fn_once_trait, None); - if b.remaining() != 2 { - return None; - } - let fn_once_subst = b.push(ty.clone()).push(arg_ty).build(); + let b = TyBuilder::trait_ref(self.db, fn_once_trait); + if b.remaining() != 2 { + return None; + } + let mut trait_ref = b.push(ty.clone()).push(arg_ty).build(); - TyBuilder::assoc_type_projection(self.db, output_assoc_type, Some(fn_once_subst)) - .build() + let projection = { + TyBuilder::assoc_type_projection( + self.db, + output_assoc_type, + Some(trait_ref.substitution.clone()), + ) + .build() }; let trait_env = self.trait_env.env.clone(); - let mut trait_ref = projection.trait_ref(self.db); let obligation = InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 6c1eccb75e631..392bda51b528d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,7 +1,7 @@ use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; -use project_model::target_data_layout::RustcDataLayoutConfig; +use project_model::{target_data_layout::RustcDataLayoutConfig, Sysroot}; use rustc_hash::FxHashMap; use test_fixture::WithFixture; use triomphe::Arc; @@ -17,7 +17,7 @@ mod closure; fn current_machine_data_layout() -> String { project_model::target_data_layout::get( - RustcDataLayoutConfig::Rustc(None), + RustcDataLayoutConfig::Rustc(&Sysroot::empty()), None, &FxHashMap::default(), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 1727cec9893b2..26a839f0e9fae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -570,6 +570,10 @@ impl CallableSig { } } + pub fn abi(&self) -> FnAbi { + self.abi + } + pub fn params(&self) -> &[Ty] { &self.params_and_return[0..self.params_and_return.len() - 1] } @@ -892,20 +896,16 @@ where Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) } } -pub fn callable_sig_from_fnonce( - mut self_ty: &Ty, - env: Arc, +pub fn callable_sig_from_fn_trait( + self_ty: &Ty, + trait_env: Arc, db: &dyn HirDatabase, -) -> Option { - if let Some((ty, _, _)) = self_ty.as_reference() { - // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment - self_ty = ty; - } - let krate = env.krate; +) -> Option<(FnTrait, CallableSig)> { + let krate = trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; - let mut table = InferenceTable::new(db, env); + let mut table = InferenceTable::new(db, trait_env.clone()); let b = TyBuilder::trait_ref(db, fn_once_trait); if b.remaining() != 2 { return None; @@ -915,23 +915,56 @@ pub fn callable_sig_from_fnonce( // - Self: FnOnce // - >::Output == ?ret_ty let args_ty = table.new_type_var(); - let trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build(); + let mut trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build(); let projection = TyBuilder::assoc_type_projection( db, output_assoc_type, Some(trait_ref.substitution.clone()), ) .build(); - table.register_obligation(trait_ref.cast(Interner)); - let ret_ty = table.normalize_projection_ty(projection); - - let ret_ty = table.resolve_completely(ret_ty); - let args_ty = table.resolve_completely(args_ty); - let params = - args_ty.as_tuple()?.iter(Interner).map(|it| it.assert_ty_ref(Interner)).cloned().collect(); - - Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe, FnAbi::RustCall)) + let block = trait_env.block; + let trait_env = trait_env.env.clone(); + let obligation = + InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() }; + let canonical = table.canonicalize(obligation.clone()); + if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + table.register_obligation(obligation.goal); + let return_ty = table.normalize_projection_ty(projection); + for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { + let fn_x_trait = fn_x.get_id(db, krate)?; + trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); + let obligation: chalk_ir::InEnvironment> = InEnvironment { + goal: trait_ref.clone().cast(Interner), + environment: trait_env.clone(), + }; + let canonical = table.canonicalize(obligation.clone()); + if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + let ret_ty = table.resolve_completely(return_ty); + let args_ty = table.resolve_completely(args_ty); + let params = args_ty + .as_tuple()? + .iter(Interner) + .map(|it| it.assert_ty_ref(Interner)) + .cloned() + .collect(); + + return Some(( + fn_x, + CallableSig::from_params_and_return( + params, + ret_ty, + false, + Safety::Safe, + FnAbi::RustCall, + ), + )); + } + } + unreachable!("It should at least implement FnOnce at this point"); + } else { + None + } } struct PlaceholderCollector<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 930bc7df5e01e..02f2cd761599a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -1,5 +1,6 @@ //! Trait solving using Chalk. +use core::fmt; use std::env::var; use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData}; @@ -209,7 +210,25 @@ pub enum FnTrait { Fn, } +impl fmt::Display for FnTrait { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FnTrait::FnOnce => write!(f, "FnOnce"), + FnTrait::FnMut => write!(f, "FnMut"), + FnTrait::Fn => write!(f, "Fn"), + } + } +} + impl FnTrait { + pub const fn function_name(&self) -> &'static str { + match self { + FnTrait::FnOnce => "call_once", + FnTrait::FnMut => "call_mut", + FnTrait::Fn => "call", + } + } + const fn lang_item(self) -> LangItem { match self { FnTrait::FnOnce => LangItem::FnOnce, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 85f33a10fcb2d..777be711a5e6e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -35,7 +35,7 @@ pub mod term_search; mod display; -use std::{iter, mem::discriminant, ops::ControlFlow}; +use std::{mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId}; @@ -52,7 +52,6 @@ use hir_def::{ path::ImportAlias, per_ns::PerNs, resolver::{HasResolver, Resolver}, - src::HasSource as _, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, @@ -69,7 +68,7 @@ use hir_ty::{ diagnostics::BodyValidationDiagnostic, error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, - method_resolution::{self, TyFingerprint}, + method_resolution::{self}, mir::{interpret_mir, MutBorrowKind}, primitive::UintTy, traits::FnTrait, @@ -100,6 +99,7 @@ pub use crate::{ VisibleTraits, }, }; +pub use hir_ty::method_resolution::TyFingerprint; // Be careful with these re-exports. // @@ -123,6 +123,7 @@ pub use { per_ns::Namespace, type_ref::{Mutability, TypeRef}, visibility::Visibility, + ImportPathConfig, // FIXME: This is here since some queries take it as input that are used // outside of hir. {AdtId, MacroId, ModuleDefId}, @@ -141,7 +142,7 @@ pub use { display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, layout::LayoutError, mir::{MirEvalError, MirLowerError}, - PointerCast, Safety, + FnAbi, PointerCast, Safety, }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, @@ -789,40 +790,32 @@ impl Module { /// Finds a path that can be used to refer to the given item from within /// this module, if possible. - pub fn find_use_path( + pub fn find_path( self, db: &dyn DefDatabase, item: impl Into, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Option { hir_def::find_path::find_path( db, item.into().into(), self.into(), - prefer_no_std, - prefer_prelude, + PrefixKind::Plain, + false, + cfg, ) } /// Finds a path that can be used to refer to the given item from within /// this module, if possible. This is used for returning import paths for use-statements. - pub fn find_use_path_prefixed( + pub fn find_use_path( self, db: &dyn DefDatabase, item: impl Into, prefix_kind: PrefixKind, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Option { - hir_def::find_path::find_path_prefixed( - db, - item.into().into(), - self.into(), - prefix_kind, - prefer_no_std, - prefer_prelude, - ) + hir_def::find_path::find_path(db, item.into().into(), self.into(), prefix_kind, true, cfg) } } @@ -1965,7 +1958,7 @@ impl Function { .enumerate() .map(|(idx, ty)| { let ty = Type { env: environment.clone(), ty: ty.clone() }; - Param { func: self, ty, idx } + Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx } }) .collect() } @@ -1991,7 +1984,7 @@ impl Function { .skip(skip) .map(|(idx, ty)| { let ty = Type { env: environment.clone(), ty: ty.clone() }; - Param { func: self, ty, idx } + Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx } }) .collect() } @@ -2037,7 +2030,7 @@ impl Function { .skip(skip) .map(|(idx, ty)| { let ty = Type { env: environment.clone(), ty: ty.clone() }; - Param { func: self, ty, idx } + Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx } }) .collect() } @@ -2167,17 +2160,24 @@ impl From for Access { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct Param { - func: Function, + func: Callee, /// The index in parameter list, including self parameter. idx: usize, ty: Type, } impl Param { - pub fn parent_fn(&self) -> Function { - self.func + pub fn parent_fn(&self) -> Option { + match self.func { + Callee::Def(CallableDefId::FunctionId(f)) => Some(f.into()), + _ => None, + } } + // pub fn parent_closure(&self) -> Option { + // self.func.as_ref().right().cloned() + // } + pub fn index(&self) -> usize { self.idx } @@ -2191,7 +2191,11 @@ impl Param { } pub fn as_local(&self, db: &dyn HirDatabase) -> Option { - let parent = DefWithBodyId::FunctionId(self.func.into()); + let parent = match self.func { + Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it), + Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0, + _ => return None, + }; let body = db.body(parent); if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { Some(Local { parent, binding_id: self_param }) @@ -2205,18 +2209,45 @@ impl Param { } pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option { - self.source(db).and_then(|p| p.value.pat()) + self.source(db).and_then(|p| p.value.right()?.pat()) } - pub fn source(&self, db: &dyn HirDatabase) -> Option> { - let InFile { file_id, value } = self.func.source(db)?; - let params = value.param_list()?; - if params.self_param().is_some() { - params.params().nth(self.idx.checked_sub(params.self_param().is_some() as usize)?) - } else { - params.params().nth(self.idx) + pub fn source( + &self, + db: &dyn HirDatabase, + ) -> Option>> { + match self.func { + Callee::Def(CallableDefId::FunctionId(func)) => { + let InFile { file_id, value } = Function { id: func }.source(db)?; + let params = value.param_list()?; + if let Some(self_param) = params.self_param() { + if let Some(idx) = self.idx.checked_sub(1) { + params.params().nth(idx).map(Either::Right) + } else { + Some(Either::Left(self_param)) + } + } else { + params.params().nth(self.idx).map(Either::Right) + } + .map(|value| InFile { file_id, value }) + } + Callee::Closure(closure, _) => { + let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into()); + let (_, source_map) = db.body_with_source_map(owner); + let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?; + let root = db.parse_or_expand(file_id); + match value.to_node(&root) { + ast::Expr::ClosureExpr(it) => it + .param_list()? + .params() + .nth(self.idx) + .map(Either::Right) + .map(|value| InFile { file_id: ast.file_id, value }), + _ => None, + } + } + _ => None, } - .map(|value| InFile { file_id, value }) } } @@ -3372,34 +3403,21 @@ impl BuiltinAttr { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ToolModule { - krate: Option, + krate: CrateId, idx: u32, } impl ToolModule { - // FIXME: consider crates\hir_def\src\nameres\attr_resolution.rs? pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option { - if let builtin @ Some(_) = Self::builtin(name) { - return builtin; - } + let krate = krate.id; let idx = - db.crate_def_map(krate.id).registered_tools().iter().position(|it| it == name)? as u32; - Some(ToolModule { krate: Some(krate.id), idx }) - } - - fn builtin(name: &str) -> Option { - hir_def::attr::builtin::TOOL_MODULES - .iter() - .position(|&tool| tool == name) - .map(|idx| ToolModule { krate: None, idx: idx as u32 }) + db.crate_def_map(krate).registered_tools().iter().position(|it| it == name)? as u32; + Some(ToolModule { krate, idx }) } pub fn name(&self, db: &dyn HirDatabase) -> SmolStr { // FIXME: Return a `Name` here - match self.krate { - Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(), - None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]), - } + db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone() } } @@ -4292,27 +4310,37 @@ impl Type { } pub fn as_callable(&self, db: &dyn HirDatabase) -> Option { - let mut the_ty = &self.ty; let callee = match self.ty.kind(Interner) { - TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => { - the_ty = ty; - Callee::Closure(ty.as_closure().unwrap()) - } - TyKind::Closure(id, _) => Callee::Closure(*id), + TyKind::Closure(id, subst) => Callee::Closure(*id, subst.clone()), TyKind::Function(_) => Callee::FnPtr, TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?), - _ => { - let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?; + kind => { + // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment + let (ty, kind) = if let TyKind::Ref(_, _, ty) = kind { + (ty, ty.kind(Interner)) + } else { + (&self.ty, kind) + }; + if let TyKind::Closure(closure, subst) = kind { + let sig = ty.callable_sig(db)?; + return Some(Callable { + ty: self.clone(), + sig, + callee: Callee::Closure(*closure, subst.clone()), + is_bound_method: false, + }); + } + let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(ty, self.env.clone(), db)?; return Some(Callable { ty: self.clone(), sig, - callee: Callee::Other, + callee: Callee::FnImpl(fn_trait), is_bound_method: false, }); } }; - let sig = the_ty.callable_sig(db)?; + let sig = self.ty.callable_sig(db)?; Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false }) } @@ -4929,37 +4957,39 @@ pub struct Callable { sig: CallableSig, callee: Callee, /// Whether this is a method that was called with method call syntax. - pub(crate) is_bound_method: bool, + is_bound_method: bool, } -#[derive(Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] enum Callee { Def(CallableDefId), - Closure(ClosureId), + Closure(ClosureId, Substitution), FnPtr, - Other, + FnImpl(FnTrait), } pub enum CallableKind { Function(Function), TupleStruct(Struct), TupleEnumVariant(Variant), - Closure, + Closure(Closure), FnPtr, - /// Some other type that implements `FnOnce`. - Other, + FnImpl(FnTrait), } impl Callable { pub fn kind(&self) -> CallableKind { - use Callee::*; match self.callee { - Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), - Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), - Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), - Closure(_) => CallableKind::Closure, - FnPtr => CallableKind::FnPtr, - Other => CallableKind::Other, + Callee::Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), + Callee::Def(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), + Callee::Def(CallableDefId::EnumVariantId(it)) => { + CallableKind::TupleEnumVariant(it.into()) + } + Callee::Closure(id, ref subst) => { + CallableKind::Closure(Closure { id, subst: subst.clone() }) + } + Callee::FnPtr => CallableKind::FnPtr, + Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_), } } pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> { @@ -4973,43 +5003,15 @@ impl Callable { pub fn n_params(&self) -> usize { self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } } - pub fn params( - &self, - db: &dyn HirDatabase, - ) -> Vec<(Option>, Type)> { - let types = self - .sig + pub fn params(&self) -> Vec { + self.sig .params() .iter() + .enumerate() .skip(if self.is_bound_method { 1 } else { 0 }) - .map(|ty| self.ty.derived(ty.clone())); - let map_param = |it: ast::Param| it.pat().map(Either::Right); - let patterns = match self.callee { - Callee::Def(CallableDefId::FunctionId(func)) => { - let src = func.lookup(db.upcast()).source(db.upcast()); - src.value.param_list().map(|param_list| { - param_list - .self_param() - .map(|it| Some(Either::Left(it))) - .filter(|_| !self.is_bound_method) - .into_iter() - .chain(param_list.params().map(map_param)) - }) - } - Callee::Closure(closure_id) => match closure_source(db, closure_id) { - Some(src) => src.param_list().map(|param_list| { - param_list - .self_param() - .map(|it| Some(Either::Left(it))) - .filter(|_| !self.is_bound_method) - .into_iter() - .chain(param_list.params().map(map_param)) - }), - None => None, - }, - _ => None, - }; - patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect() + .map(|(idx, ty)| (idx, self.ty.derived(ty.clone()))) + .map(|(idx, ty)| Param { func: self.callee.clone(), idx, ty }) + .collect() } pub fn return_type(&self) -> Type { self.ty.derived(self.sig.ret().clone()) @@ -5017,17 +5019,9 @@ impl Callable { pub fn sig(&self) -> &CallableSig { &self.sig } -} -fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option { - let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into()); - let (_, source_map) = db.body_with_source_map(owner); - let ast = source_map.expr_syntax(expr_id).ok()?; - let root = ast.file_syntax(db.upcast()); - let expr = ast.value.to_node(&root); - match expr { - ast::Expr::ClosureExpr(it) => Some(it), - _ => None, + pub fn ty(&self) -> &Type { + &self.ty } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 057b03baef07a..d22958406420e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -307,7 +307,8 @@ impl SourceAnalyzer { db: &dyn HirDatabase, call: &ast::Expr, ) -> Option { - self.type_of_expr(db, &call.clone())?.0.as_callable(db) + let (orig, adjusted) = self.type_of_expr(db, &call.clone())?; + adjusted.unwrap_or(orig).as_callable(db) } pub(crate) fn resolve_field( diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs index 9f56a1ee55d3b..8173427cd907a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs @@ -1,6 +1,6 @@ //! Type tree for term search -use hir_def::find_path::PrefixKind; +use hir_def::ImportPathConfig; use hir_expand::mod_path::ModPath; use hir_ty::{ db::HirDatabase, @@ -17,42 +17,20 @@ use crate::{ fn mod_item_path( sema_scope: &SemanticsScope<'_>, def: &ModuleDef, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Option { let db = sema_scope.db; - // Account for locals shadowing items from module - let name_hit_count = def.name(db).map(|def_name| { - let mut name_hit_count = 0; - sema_scope.process_all_names(&mut |name, _| { - if name == def_name { - name_hit_count += 1; - } - }); - name_hit_count - }); - let m = sema_scope.module(); - match name_hit_count { - Some(0..=1) | None => m.find_use_path(db.upcast(), *def, prefer_no_std, prefer_prelude), - Some(_) => m.find_use_path_prefixed( - db.upcast(), - *def, - PrefixKind::ByCrate, - prefer_no_std, - prefer_prelude, - ), - } + m.find_path(db.upcast(), *def, cfg) } /// Helper function to get path to `ModuleDef` as string fn mod_item_path_str( sema_scope: &SemanticsScope<'_>, def: &ModuleDef, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Result { - let path = mod_item_path(sema_scope, def, prefer_no_std, prefer_prelude); + let path = mod_item_path(sema_scope, def, cfg); path.map(|it| it.display(sema_scope.db.upcast()).to_string()) .ok_or(DisplaySourceCodeError::PathNotFound) } @@ -61,8 +39,7 @@ fn mod_item_path_str( fn type_path( sema_scope: &SemanticsScope<'_>, ty: &Type, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Result { let db = sema_scope.db; let m = sema_scope.module(); @@ -71,9 +48,7 @@ fn type_path( Some(adt) => { let ty_name = ty.display_source_code(db, m.id, true)?; - let mut path = - mod_item_path(sema_scope, &ModuleDef::Adt(adt), prefer_no_std, prefer_prelude) - .unwrap(); + let mut path = mod_item_path(sema_scope, &ModuleDef::Adt(adt), cfg).unwrap(); path.pop_segment(); let path = path.display(db.upcast()).to_string(); let res = match path.is_empty() { @@ -158,11 +133,10 @@ impl Expr { &self, sema_scope: &SemanticsScope<'_>, many_formatter: &mut dyn FnMut(&Type) -> String, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Result { let db = sema_scope.db; - let mod_item_path_str = |s, def| mod_item_path_str(s, def, prefer_no_std, prefer_prelude); + let mod_item_path_str = |s, def| mod_item_path_str(s, def, cfg); match self { Expr::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)), Expr::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)), @@ -172,9 +146,7 @@ impl Expr { Expr::Function { func, params, .. } => { let args = params .iter() - .map(|f| { - f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude) - }) + .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg)) .collect::, DisplaySourceCodeError>>()? .into_iter() .join(", "); @@ -188,14 +160,10 @@ impl Expr { crate::AssocItemContainer::Impl(imp) => { let self_ty = imp.self_ty(db); // Should it be guaranteed that `mod_item_path` always exists? - match self_ty.as_adt().and_then(|adt| { - mod_item_path( - sema_scope, - &adt.into(), - prefer_no_std, - prefer_prelude, - ) - }) { + match self_ty + .as_adt() + .and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg)) + { Some(path) => path.display(sema_scope.db.upcast()).to_string(), None => self_ty.display(db).to_string(), } @@ -217,17 +185,10 @@ impl Expr { let func_name = func.name(db).display(db.upcast()).to_string(); let self_param = func.self_param(db).unwrap(); - let target_str = target.gen_source_code( - sema_scope, - many_formatter, - prefer_no_std, - prefer_prelude, - )?; + let target_str = target.gen_source_code(sema_scope, many_formatter, cfg)?; let args = params .iter() - .map(|f| { - f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude) - }) + .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg)) .collect::, DisplaySourceCodeError>>()? .into_iter() .join(", "); @@ -259,7 +220,7 @@ impl Expr { false => { let generics = generics .iter() - .map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude)) + .map(|it| type_path(sema_scope, it, cfg)) .collect::, DisplaySourceCodeError>>()? .into_iter() .join(", "); @@ -270,14 +231,7 @@ impl Expr { StructKind::Tuple => { let args = params .iter() - .map(|f| { - f.gen_source_code( - sema_scope, - many_formatter, - prefer_no_std, - prefer_prelude, - ) - }) + .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg)) .collect::, DisplaySourceCodeError>>()? .into_iter() .join(", "); @@ -292,12 +246,7 @@ impl Expr { let tmp = format!( "{}: {}", f.name(db).display(db.upcast()), - a.gen_source_code( - sema_scope, - many_formatter, - prefer_no_std, - prefer_prelude - )? + a.gen_source_code(sema_scope, many_formatter, cfg)? ); Ok(tmp) }) @@ -318,14 +267,7 @@ impl Expr { StructKind::Tuple => { let args = params .iter() - .map(|a| { - a.gen_source_code( - sema_scope, - many_formatter, - prefer_no_std, - prefer_prelude, - ) - }) + .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg)) .collect::, DisplaySourceCodeError>>()? .into_iter() .join(", "); @@ -340,12 +282,7 @@ impl Expr { let tmp = format!( "{}: {}", f.name(db).display(db.upcast()), - a.gen_source_code( - sema_scope, - many_formatter, - prefer_no_std, - prefer_prelude - )? + a.gen_source_code(sema_scope, many_formatter, cfg)? ); Ok(tmp) }) @@ -359,7 +296,7 @@ impl Expr { false => { let generics = generics .iter() - .map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude)) + .map(|it| type_path(sema_scope, it, cfg)) .collect::, DisplaySourceCodeError>>()? .into_iter() .join(", "); @@ -374,9 +311,7 @@ impl Expr { Expr::Tuple { params, .. } => { let args = params .iter() - .map(|a| { - a.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude) - }) + .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg)) .collect::, DisplaySourceCodeError>>()? .into_iter() .join(", "); @@ -388,12 +323,7 @@ impl Expr { return Ok(many_formatter(&expr.ty(db))); } - let strukt = expr.gen_source_code( - sema_scope, - many_formatter, - prefer_no_std, - prefer_prelude, - )?; + let strukt = expr.gen_source_code(sema_scope, many_formatter, cfg)?; let field = field.name(db).display(db.upcast()).to_string(); Ok(format!("{strukt}.{field}")) } @@ -402,12 +332,7 @@ impl Expr { return Ok(many_formatter(&expr.ty(db))); } - let inner = expr.gen_source_code( - sema_scope, - many_formatter, - prefer_no_std, - prefer_prelude, - )?; + let inner = expr.gen_source_code(sema_scope, many_formatter, cfg)?; Ok(format!("&{inner}")) } Expr::Many(ty) => Ok(many_formatter(ty)), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 5ef374506ecc2..22a4674fd46c7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,7 +1,7 @@ use std::iter::{self, Peekable}; use either::Either; -use hir::{Adt, Crate, HasAttrs, HasSource, ModuleDef, Semantics}; +use hir::{Adt, Crate, HasAttrs, HasSource, ImportPathConfig, ModuleDef, Semantics}; use ide_db::RootDatabase; use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; use itertools::Itertools; @@ -71,6 +71,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .filter(|pat| !matches!(pat, Pat::WildcardPat(_))) .collect(); + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + let module = ctx.sema.scope(expr.syntax())?.module(); let (mut missing_pats, is_non_exhaustive, has_hidden_variants): ( Peekable>>, @@ -88,13 +93,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|variant| { Some(( - build_pat( - ctx.db(), - module, - variant, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - )?, + build_pat(ctx.db(), module, variant, cfg)?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -145,15 +144,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants.into_iter().filter_map(|variant| { - build_pat( - ctx.db(), - module, - variant, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) - }); + let patterns = variants + .into_iter() + .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) }) @@ -184,15 +177,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants.into_iter().filter_map(|variant| { - build_pat( - ctx.db(), - module, - variant, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) - }); + let patterns = variants + .into_iter() + .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); @@ -457,18 +444,11 @@ fn build_pat( db: &RootDatabase, module: hir::Module, var: ExtendedVariant, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Option { match var { ExtendedVariant::Variant(var) => { - let path = mod_path_to_ast(&module.find_use_path( - db, - ModuleDef::from(var), - prefer_no_std, - prefer_prelude, - )?); - + let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?); // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though Some(match var.source(db)?.value.kind() { ast::StructKind::Tuple(field_list) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index b90bccb48ede4..3bd003a267a2b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -1,6 +1,6 @@ use std::cmp::Reverse; -use hir::{db::HirDatabase, Module}; +use hir::{db::HirDatabase, ImportPathConfig, Module}; use ide_db::{ helpers::mod_path_to_ast, imports::{ @@ -90,14 +90,14 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; let mut proposed_imports: Vec<_> = import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_no_std, - ) + .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) .collect(); if proposed_imports.is_empty() { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs index fd3a0506ab677..71436e6580471 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs @@ -1,4 +1,4 @@ -use hir::ModuleDef; +use hir::{ImportPathConfig, ModuleDef}; use ide_db::{ assists::{AssistId, AssistKind}, defs::Definition, @@ -326,6 +326,11 @@ fn augment_references_with_imports( ) -> Vec { let mut visited_modules = FxHashSet::default(); + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + references .into_iter() .filter_map(|FileReference { range, name, .. }| { @@ -341,12 +346,11 @@ fn augment_references_with_imports( let import_scope = ImportScope::find_insert_use_container(name.syntax(), &ctx.sema); let path = ref_module - .find_use_path_prefixed( + .find_use_path( ctx.sema.db, ModuleDef::Module(*target_module), ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + cfg, ) .map(|mod_path| { make::path_concat(mod_path_to_ast(&mod_path), make::path_from_text("Bool")) @@ -1521,7 +1525,7 @@ mod foo { } "#, r#" -use crate::foo::Bool; +use foo::Bool; fn main() { use foo::FOO; @@ -1602,7 +1606,7 @@ pub mod bar { "#, r#" //- /main.rs -use crate::foo::bar::Bool; +use foo::bar::Bool; mod foo; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index d649f13d6ee35..be433c333385b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -1,3 +1,4 @@ +use hir::ImportPathConfig; use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast, traits::resolve_target_trait}; use syntax::ast::{self, AstNode, HasName}; @@ -43,19 +44,18 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - return None; } + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + let src_type_path = { let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?; let src_type_def = match ctx.sema.resolve_path(&src_type_path) { Some(hir::PathResolution::Def(module_def)) => module_def, _ => return None, }; - - mod_path_to_ast(&module.find_use_path( - ctx.db(), - src_type_def, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - )?) + mod_path_to_ast(&module.find_path(ctx.db(), src_type_def, cfg)?) }; let dest_type = match &ast_trait { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 41366658a74bf..241fc3b7a379c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::ModuleDef; +use hir::{ImportPathConfig, ModuleDef}; use ide_db::{ assists::{AssistId, AssistKind}, defs::Definition, @@ -183,6 +183,11 @@ fn augment_references_with_imports( ) -> Vec<(ast::NameLike, Option<(ImportScope, ast::Path)>)> { let mut visited_modules = FxHashSet::default(); + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + references .iter() .filter_map(|FileReference { name, .. }| { @@ -201,12 +206,11 @@ fn augment_references_with_imports( let import_scope = ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); let path = ref_module - .find_use_path_prefixed( + .find_use_path( ctx.sema.db, ModuleDef::Module(*target_module), ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + cfg, ) .map(|mod_path| { make::path_concat( @@ -811,7 +815,7 @@ pub mod bar { "#, r#" //- /main.rs -use crate::foo::bar::BarResult; +use foo::bar::BarResult; mod foo; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index c1a3f9302650e..9adbdd220c2b7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -1,4 +1,4 @@ -use hir::HasVisibility; +use hir::{HasVisibility, ImportPathConfig}; use ide_db::{ assists::{AssistId, AssistKind}, defs::Definition, @@ -87,15 +87,15 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) -> Op FamousDefs(&ctx.sema, module.krate()).core_ops_ControlFlow(); if let Some(control_flow_enum) = control_flow_enum { - let mod_path = module.find_use_path_prefixed( + let mod_path = module.find_use_path( ctx.sema.db, ModuleDef::from(control_flow_enum), ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ); if let Some(mod_path) = mod_path { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 81a639e0b9f3d..3c6d73b62e736 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -1,7 +1,7 @@ use std::iter; use either::Either; -use hir::{Module, ModuleDef, Name, Variant}; +use hir::{ImportPathConfig, Module, ModuleDef, Name, Variant}; use ide_db::{ defs::Definition, helpers::mod_path_to_ast, @@ -386,12 +386,14 @@ fn process_references( let segment = builder.make_mut(segment); let scope_node = builder.make_syntax_mut(scope_node); if !visited_modules.contains(&module) { - let mod_path = module.find_use_path_prefixed( + let mod_path = module.find_use_path( ctx.sema.db, *enum_module_def, ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ); if let Some(mut mod_path) = mod_path { mod_path.pop_segment(); @@ -881,7 +883,7 @@ fn another_fn() { r#"use my_mod::my_other_mod::MyField; mod my_mod { - use self::my_other_mod::MyField; + use my_other_mod::MyField; fn another_fn() { let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 473c699b595c6..9a441fc5ebcd0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use hir::{ModPath, ModuleDef}; +use hir::{ImportPathConfig, ModPath, ModuleDef}; use ide_db::{famous_defs::FamousDefs, RootDatabase}; use syntax::{ ast::{self, HasName}, @@ -58,11 +58,13 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = module.find_use_path( + let trait_path = module.find_path( ctx.db(), ModuleDef::Trait(trait_), - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, )?; let field_type = field.ty()?; @@ -103,11 +105,13 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = module.find_use_path( + let trait_path = module.find_path( ctx.db(), ModuleDef::Trait(trait_), - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, )?; let field_type = field.ty()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 22c75cd5eefc9..52007e0e297bc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -1,3 +1,4 @@ +use hir::ImportPathConfig; use ide_db::{ imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor, }; @@ -58,11 +59,13 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?)); - let type_path = current_module.find_use_path( + let type_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, )?; let expr = use_trivial_constructor( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index ff65aac82e070..5d1140d57ac9f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -1,4 +1,7 @@ -use hir::{db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef}; +use hir::{ + db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ImportPathConfig, ItemInNs, + ModuleDef, +}; use ide_db::assists::{AssistId, AssistKind}; use syntax::{ast, AstNode}; @@ -44,11 +47,13 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> let current_module = ctx.sema.scope(call.syntax())?.module(); let target_module_def = ModuleDef::from(resolved_call); let item_in_ns = ItemInNs::from(target_module_def); - let receiver_path = current_module.find_use_path( + let receiver_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, )?; let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 63a09ce2e976d..978b719c30a00 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -1,6 +1,6 @@ use std::iter; -use hir::AsAssocItem; +use hir::{AsAssocItem, ImportPathConfig}; use ide_db::RootDatabase; use ide_db::{ helpers::mod_path_to_ast, @@ -37,9 +37,13 @@ use crate::{ // ``` pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; - let mut proposed_imports: Vec<_> = import_assets - .search_for_relative_paths(&ctx.sema, ctx.config.prefer_no_std, ctx.config.prefer_prelude) - .collect(); + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + + let mut proposed_imports: Vec<_> = + import_assets.search_for_relative_paths(&ctx.sema, cfg).collect(); if proposed_imports.is_empty() { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index 0f0f13bbc80b1..b653f3b6650e3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -776,6 +776,40 @@ mod z { ); } + #[test] + fn remove_unused_fixes_nested_self() { + check_assist( + remove_unused_imports, + r#" +mod inner { + pub struct X(); + pub struct Y(); +} + +mod z { + use super::inner::{self, X}$0; + + fn f() { + let y = inner::Y(); + } +} +"#, + r#"mod inner { + pub struct X(); + pub struct Y(); +} + +mod z { + use super::inner::{self}; + + fn f() { + let y = inner::Y(); + } +} +"#, + ); + } + #[test] fn dont_remove_used_glob() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 3420d906dea5a..e792debaa517e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1,4 +1,4 @@ -use hir::{InFile, MacroFileIdExt, ModuleDef}; +use hir::{ImportPathConfig, InFile, MacroFileIdExt, ModuleDef}; use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator}; use itertools::Itertools; use syntax::{ @@ -83,11 +83,13 @@ pub(crate) fn replace_derive_with_manual_impl( }) .flat_map(|trait_| { current_module - .find_use_path( + .find_path( ctx.sema.db, hir::ModuleDef::Trait(trait_), - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ) .as_ref() .map(mod_path_to_ast) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 7f3b0d758839f..37ea5123a71e6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -114,10 +114,10 @@ pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<' let callable = ctx.sema.resolve_method_call_as_callable(&call)?; let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?; let n_params = callable.n_params() + 1; - let params = callable.params(ctx.sema.db); + let params = callable.params(); // FIXME: Check that the arg is of the form `() -> T` - if !params.first()?.1.impls_fnonce(ctx.sema.db) { + if !params.first()?.ty().impls_fnonce(ctx.sema.db) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index ba1c25fa5a749..188165e776455 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -1,4 +1,4 @@ -use hir::AsAssocItem; +use hir::{AsAssocItem, ImportPathConfig}; use ide_db::{ helpers::mod_path_to_ast, imports::insert_use::{insert_use, ImportScope}, @@ -63,12 +63,14 @@ pub(crate) fn replace_qualified_name_with_use( ); let path_to_qualifier = starts_with_name_ref .then(|| { - ctx.sema.scope(path.syntax())?.module().find_use_path_prefixed( + ctx.sema.scope(path.syntax())?.module().find_use_path( ctx.sema.db, module, ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ) }) .flatten(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index d0c6ae21988d8..ffd1508ccbdc8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs @@ -1,5 +1,8 @@ //! Term search assist -use hir::term_search::{TermSearchConfig, TermSearchCtx}; +use hir::{ + term_search::{TermSearchConfig, TermSearchCtx}, + ImportPathConfig, +}; use ide_db::{ assists::{AssistId, AssistKind, GroupLabel}, famous_defs::FamousDefs, @@ -50,8 +53,10 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< path.gen_source_code( &scope, &mut formatter, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ) .ok() }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs index 1859825b3d65e..23a06404f3010 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs @@ -253,11 +253,8 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option pat, - _ => return None, - }; + let param = func.params().into_iter().nth(idx)?; + let pat = param.source(sema.db)?.value.right()?.pat()?; let name = var_name_from_pat(&pat)?; normalize(&name.to_string()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 1ea7220960d25..11ffc8bc4414f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -24,7 +24,7 @@ pub(crate) mod vis; use std::iter; -use hir::{known, HasAttrs, ScopeDef, Variant}; +use hir::{known, HasAttrs, ImportPathConfig, ScopeDef, Variant}; use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind}; use syntax::{ast, SmolStr}; @@ -633,11 +633,13 @@ fn enum_variants_with_paths( } for variant in variants { - if let Some(path) = ctx.module.find_use_path( + if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 1e31d65fddfe6..ddb1aeb3711d8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -1,6 +1,6 @@ //! Completion of names from the current scope in expression position. -use hir::ScopeDef; +use hir::{ImportPathConfig, ScopeDef}; use syntax::ast; use crate::{ @@ -171,11 +171,13 @@ pub(crate) fn complete_expr_path( hir::Adt::Struct(strukt) => { let path = ctx .module - .find_use_path( + .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ) .filter(|it| it.len() > 1); @@ -194,11 +196,13 @@ pub(crate) fn complete_expr_path( hir::Adt::Union(un) => { let path = ctx .module - .find_use_path( + .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index bf6747d71b7a5..d5a4e9ecdcc13 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -1,5 +1,5 @@ //! See [`import_on_the_fly`]. -use hir::{ItemInNs, ModuleDef}; +use hir::{ImportPathConfig, ItemInNs, ModuleDef}; use ide_db::imports::{ import_assets::{ImportAssets, LocatedImport}, insert_use::ImportScope, @@ -257,13 +257,13 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); + let import_cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) + .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) .filter(ns_filter) .filter(|import| { let original_item = &import.original_item; @@ -308,13 +308,13 @@ fn import_on_the_fly_pat_( }; let user_input_lowercased = potential_import_name.to_lowercase(); + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) + .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) .filter(ns_filter) .filter(|import| { let original_item = &import.original_item; @@ -355,13 +355,13 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) + .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) .filter(|import| { !ctx.is_item_hidden(&import.item_to_import) && !ctx.is_item_hidden(&import.original_item) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index c6e243b31a791..a230edd39b6df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -2,7 +2,7 @@ mod format_like; -use hir::ItemInNs; +use hir::{ImportPathConfig, ItemInNs}; use ide_db::{ documentation::{Documentation, HasDocs}, imports::insert_use::ImportScope, @@ -60,15 +60,17 @@ pub(crate) fn complete_postfix( None => return, }; + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { if let Some(drop_fn) = ctx.famous_defs().core_mem_drop() { - if let Some(path) = ctx.module.find_use_path( - ctx.db, - ItemInNs::Values(drop_fn.into()), - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) { + if let Some(path) = + ctx.module.find_path(ctx.db, ItemInNs::Values(drop_fn.into()), cfg) + { cov_mark::hit!(postfix_drop_completion); let mut item = postfix_snippet( "drop", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index d89cfc8b6cb83..a83fa28b8758e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -12,6 +12,7 @@ mod snippet; #[cfg(test)] mod tests; +use hir::ImportPathConfig; use ide_db::{ base_db::FilePosition, helpers::mod_path_to_ast, @@ -251,6 +252,11 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); + let cfg = ImportPathConfig { + prefer_no_std: config.prefer_no_std, + prefer_prelude: config.prefer_prelude, + }; + imports.into_iter().for_each(|(full_import_path, imported_name)| { let items_with_name = items_locator::items_with_name( &sema, @@ -260,13 +266,7 @@ pub fn resolve_completion_edits( ); let import = items_with_name .filter_map(|candidate| { - current_module.find_use_path_prefixed( - db, - candidate, - config.insert_use.prefix_kind, - config.prefer_no_std, - config.prefer_prelude, - ) + current_module.find_use_path(db, candidate, config.insert_use.prefix_kind, cfg) }) .find(|mod_path| mod_path.display(db).to_string() == full_import_path); if let Some(import_path) = import { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 7fa31e2757db3..8f563790737e5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -10,7 +10,7 @@ pub(crate) mod type_alias; pub(crate) mod union_literal; pub(crate) mod variant; -use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type}; +use hir::{AsAssocItem, HasAttrs, HirDisplay, ImportPathConfig, ModuleDef, ScopeDef, Type}; use ide_db::{ documentation::{Documentation, HasDocs}, helpers::item_name, @@ -295,14 +295,12 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let label = expr - .gen_source_code( - &ctx.scope, - &mut label_formatter, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) - .ok()?; + let cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + + let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg).ok()?; let source_range = match ctx.original_token.parent() { Some(node) => match node.ancestors().find_map(ast::Path::cast) { @@ -314,16 +312,8 @@ pub(crate) fn render_expr( let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label); - let snippet = format!( - "{}$0", - expr.gen_source_code( - &ctx.scope, - &mut snippet_formatter, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude - ) - .ok()? - ); + let snippet = + format!("{}$0", expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg).ok()?); let edit = TextEdit::replace(source_range, snippet); item.snippet_edit(ctx.config.snippet_cap?, edit); item.documentation(Documentation::new(String::from("Autogenerated expression by term search"))); @@ -333,12 +323,7 @@ pub(crate) fn render_expr( }); for trait_ in expr.traits_used(ctx.db) { let trait_item = hir::ItemInNs::from(hir::ModuleDef::from(trait_)); - let Some(path) = ctx.module.find_use_path( - ctx.db, - trait_item, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ) else { + let Some(path) = ctx.module.find_path(ctx.db, trait_item, cfg) else { continue; }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 7d710f1e1308c..07836040b4878 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -100,6 +100,7 @@ // } // ---- +use hir::ImportPathConfig; use ide_db::imports::import_assets::LocatedImport; use itertools::Itertools; use syntax::{ast, AstNode, GreenNode, SyntaxNode}; @@ -168,18 +169,22 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option> { + let import_cfg = ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }; + let resolve = |import: &GreenNode| { let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?; let item = match ctx.scope.speculative_resolve(&path)? { hir::PathResolution::Def(def) => def.into(), _ => return None, }; - let path = ctx.module.find_use_path_prefixed( + let path = ctx.module.find_use_path( ctx.db, item, ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + import_cfg, )?; Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item))) }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 70e0aa4e9a919..c1a67315b75ce 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -66,11 +66,10 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { enable_self_on_the_fly: true, enable_private_editable: false, enable_term_search: true, + term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), - prefer_no_std: false, - prefer_prelude: true, insert_use: InsertUseConfig { granularity: ImportGranularity::Crate, prefix_kind: PrefixKind::Plain, @@ -78,9 +77,10 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { group: true, skip_glob_imports: true, }, + prefer_no_std: false, + prefer_prelude: true, snippets: Vec::new(), limit: None, - term_search_fuel: 200, }; pub(crate) fn completion_list(ra_fixture: &str) -> String { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index d2227d23cd77f..abffa73c3b4c6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -472,6 +472,47 @@ fn main() { ); } +#[test] +fn trait_completions_handle_associated_types() { + let fixture = r#" +//- /foo.rs crate:foo +pub trait NotInScope { + fn not_in_scope(&self); +} + +pub trait Wrapper { + type Inner: NotInScope; + fn inner(&self) -> Self::Inner; +} + +//- /main.rs crate:main deps:foo +use foo::Wrapper; + +fn completion(whatever: T) { + whatever.inner().$0 +} +"#; + + check( + fixture, + expect![[r#" + me not_in_scope() (use foo::NotInScope) fn(&self) + "#]], + ); + + check_edit( + "not_in_scope", + fixture, + r#" +use foo::{NotInScope, Wrapper}; + +fn completion(whatever: T) { + whatever.inner().not_in_scope()$0 +} +"#, + ); +} + #[test] fn trait_method_fuzzy_completion_aware_of_unit_type() { let fixture = r#" diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 5780b5a5bb93c..98d2e81754611 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -1,7 +1,7 @@ //! This module provides functionality for querying callable information about a token. use either::Either; -use hir::{Semantics, Type}; +use hir::{InFile, Semantics, Type}; use parser::T; use syntax::{ ast::{self, HasArgList, HasName}, @@ -13,7 +13,7 @@ use crate::RootDatabase; #[derive(Debug)] pub struct ActiveParameter { pub ty: Type, - pub pat: Option>, + pub src: Option>>, } impl ActiveParameter { @@ -22,18 +22,18 @@ impl ActiveParameter { let (signature, active_parameter) = callable_for_token(sema, token)?; let idx = active_parameter?; - let mut params = signature.params(sema.db); + let mut params = signature.params(); if idx >= params.len() { cov_mark::hit!(too_many_arguments); return None; } - let (pat, ty) = params.swap_remove(idx); - Some(ActiveParameter { ty, pat }) + let param = params.swap_remove(idx); + Some(ActiveParameter { ty: param.ty().clone(), src: param.source(sema.db) }) } pub fn ident(&self) -> Option { - self.pat.as_ref().and_then(|param| match param { - Either::Right(ast::Pat::IdentPat(ident)) => ident.name(), + self.src.as_ref().and_then(|param| match param.value.as_ref().right()?.pat()? { + ast::Pat::IdentPat(ident) => ident.name(), _ => None, }) } @@ -60,10 +60,7 @@ pub fn callable_for_node( token: &SyntaxToken, ) -> Option<(hir::Callable, Option)> { let callable = match calling_node { - ast::CallableExpr::Call(call) => { - let expr = call.expr()?; - sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db) - } + ast::CallableExpr::Call(call) => sema.resolve_expr_as_callable(&call.expr()?), ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call), }?; let active_param = calling_node.arg_list().map(|arg_list| { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 766bfcf4d0947..4caecb3f2fe10 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -1,9 +1,9 @@ //! Look up accessible paths for items. use hir::{ - db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ItemInNs, - ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, - SemanticsScope, Trait, Type, + db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig, + ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, + SemanticsScope, Trait, TyFingerprint, Type, }; use itertools::{EitherOrBoth, Itertools}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -205,24 +205,22 @@ impl ImportAssets { pub fn search_for_imports( &self, sema: &Semantics<'_, RootDatabase>, + cfg: ImportPathConfig, prefix_kind: PrefixKind, - prefer_no_std: bool, - prefer_prelude: bool, ) -> impl Iterator { let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for_imports").entered(); - self.search_for(sema, Some(prefix_kind), prefer_no_std, prefer_prelude) + self.search_for(sema, Some(prefix_kind), cfg) } /// This may return non-absolute paths if a part of the returned path is already imported into scope. pub fn search_for_relative_paths( &self, sema: &Semantics<'_, RootDatabase>, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> impl Iterator { let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for_relative_paths") .entered(); - self.search_for(sema, None, prefer_no_std, prefer_prelude) + self.search_for(sema, None, cfg) } /// Requires imports to by prefix instead of fuzzily. @@ -259,8 +257,7 @@ impl ImportAssets { &self, sema: &Semantics<'_, RootDatabase>, prefixed: Option, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> impl Iterator { let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for").entered(); @@ -277,8 +274,7 @@ impl ImportAssets { item_for_path_search(sema.db, item)?, &self.module_with_candidate, prefixed, - prefer_no_std, - prefer_prelude, + cfg, ) .filter(|path| path.len() > 1) }; @@ -549,6 +545,15 @@ fn trait_applicable_items( let Some(receiver) = trait_candidate.receiver_ty.fingerprint_for_trait_impl() else { return false; }; + + // in order to handle implied bounds through an associated type, keep any + // method receiver that matches `TyFingerprint::Unnameable`. this receiver + // won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual + // implementations. + if matches!(receiver, TyFingerprint::Unnameable) { + return true; + } + let definitions_exist_in_trait_crate = db .trait_impls_in_crate(defining_crate_for_trait.into()) .has_impls_for_trait_and_self_ty(candidate_trait_id, receiver); @@ -634,19 +639,12 @@ fn get_mod_path( item_to_search: ItemInNs, module_with_candidate: &Module, prefixed: Option, - prefer_no_std: bool, - prefer_prelude: bool, + cfg: ImportPathConfig, ) -> Option { if let Some(prefix_kind) = prefixed { - module_with_candidate.find_use_path_prefixed( - db, - item_to_search, - prefix_kind, - prefer_no_std, - prefer_prelude, - ) + module_with_candidate.find_use_path(db, item_to_search, prefix_kind, cfg) } else { - module_with_candidate.find_use_path(db, item_to_search, prefer_no_std, prefer_prelude) + module_with_candidate.find_path(db, item_to_search, cfg) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 7e1811b4cacb6..7c11dd3e2a4e3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -2,7 +2,7 @@ use crate::helpers::mod_path_to_ast; use either::Either; -use hir::{AsAssocItem, HirDisplay, ModuleDef, SemanticsScope}; +use hir::{AsAssocItem, HirDisplay, ImportPathConfig, ModuleDef, SemanticsScope}; use itertools::Itertools; use rustc_hash::FxHashMap; use syntax::{ @@ -308,11 +308,12 @@ impl Ctx<'_> { parent.segment()?.name_ref()?, ) .and_then(|trait_ref| { - let found_path = self.target_module.find_use_path( + let cfg = + ImportPathConfig { prefer_no_std: false, prefer_prelude: true }; + let found_path = self.target_module.find_path( self.source_scope.db.upcast(), hir::ModuleDef::Trait(trait_ref), - false, - true, + cfg, )?; match make::ty_path(mod_path_to_ast(&found_path)) { ast::Type::PathType(path_ty) => Some(path_ty), @@ -347,12 +348,9 @@ impl Ctx<'_> { } } - let found_path = self.target_module.find_use_path( - self.source_scope.db.upcast(), - def, - false, - true, - )?; + let cfg = ImportPathConfig { prefer_no_std: false, prefer_prelude: true }; + let found_path = + self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?; let res = mod_path_to_ast(&found_path).clone_for_update(); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) { if let Some(segment) = res.segment() { @@ -385,11 +383,11 @@ impl Ctx<'_> { if let Some(adt) = ty.as_adt() { if let ast::Type::PathType(path_ty) = &ast_ty { - let found_path = self.target_module.find_use_path( + let cfg = ImportPathConfig { prefer_no_std: false, prefer_prelude: true }; + let found_path = self.target_module.find_path( self.source_scope.db.upcast(), ModuleDef::from(adt), - false, - true, + cfg, )?; if let Some(qual) = mod_path_to_ast(&found_path).qualifier() { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index 8302b015ddafd..92478ef480d6a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs @@ -41,6 +41,7 @@ pub enum FormatSpecifier { Escape, } +// FIXME: Remove this, we can use rustc_format_parse instead pub fn lex_format_specifiers( string: &ast::String, mut callback: &mut dyn FnMut(TextRange, FormatSpecifier), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index b3dde977b1c4f..132b93df10aba 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -1,7 +1,7 @@ //! This diagnostic provides an assist for creating a struct definition from a JSON //! example. -use hir::{PathResolution, Semantics}; +use hir::{ImportPathConfig, PathResolution, Semantics}; use ide_db::{ base_db::{FileId, FileRange}, helpers::mod_path_to_ast, @@ -142,14 +142,19 @@ pub(crate) fn json_in_items( ImportScope::Block(it) => ImportScope::Block(scb.make_mut(it)), }; let current_module = semantics_scope.module(); + + let cfg = ImportPathConfig { + prefer_no_std: config.prefer_no_std, + prefer_prelude: config.prefer_prelude, + }; + if !scope_has("Serialize") { if let Some(PathResolution::Def(it)) = serialize_resolved { - if let Some(it) = current_module.find_use_path_prefixed( + if let Some(it) = current_module.find_use_path( sema.db, it, config.insert_use.prefix_kind, - config.prefer_no_std, - config.prefer_prelude, + cfg, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } @@ -157,12 +162,11 @@ pub(crate) fn json_in_items( } if !scope_has("Deserialize") { if let Some(PathResolution::Def(it)) = deserialize_resolved { - if let Some(it) = current_module.find_use_path_prefixed( + if let Some(it) = current_module.find_use_path( sema.db, it, config.insert_use.prefix_kind, - config.prefer_no_std, - config.prefer_prelude, + cfg, ) { insert_use(&scope, mod_path_to_ast(&it), &config.insert_use); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index f92ba576d3ab5..9eff84b89879b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -1,7 +1,7 @@ use either::Either; use hir::{ db::{ExpandDatabase, HirDatabase}, - known, AssocItem, HirDisplay, HirFileIdExt, InFile, Type, + known, AssocItem, HirDisplay, HirFileIdExt, ImportPathConfig, InFile, Type, }; use ide_db::{ assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search, @@ -122,11 +122,13 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option Option { let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?)); - let type_path = current_module?.find_use_path( + let type_path = current_module?.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, )?; use_trivial_constructor( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index 656d79dc7324f..9651ce6106b71 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -1,7 +1,7 @@ use hir::{ db::ExpandDatabase, term_search::{term_search, TermSearchConfig, TermSearchCtx}, - ClosureStyle, HirDisplay, + ClosureStyle, HirDisplay, ImportPathConfig, }; use ide_db::{ assists::{Assist, AssistId, AssistKind, GroupLabel}, @@ -59,8 +59,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option path.gen_source_code( &scope, &mut formatter, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, + ImportPathConfig { + prefer_no_std: ctx.config.prefer_no_std, + prefer_prelude: ctx.config.prefer_prelude, + }, ) .ok() }) @@ -368,6 +370,7 @@ fn main() { ); } + // FIXME #[test] fn local_shadow_fn() { check_fixes_unordered( @@ -385,7 +388,7 @@ fn f() { r#" fn f() { let f: i32 = 0; - crate::f() + f() }"#, ], ); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index cfda1c692aea8..cf7e7e08bc52a 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -6,7 +6,7 @@ use crate::{ resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo}, SsrMatches, }; -use hir::Semantics; +use hir::{ImportPathConfig, Semantics}; use ide_db::{base_db::FileRange, FxHashMap}; use std::{cell::Cell, iter::Peekable}; use syntax::{ @@ -663,10 +663,10 @@ impl Match { .module(); for (path, resolved_path) in &template.resolved_paths { if let hir::PathResolution::Def(module_def) = resolved_path.resolution { - let mod_path = - module.find_use_path(sema.db, module_def, false, true).ok_or_else(|| { - match_error!("Failed to render template path `{}` at match location") - })?; + let cfg = ImportPathConfig { prefer_no_std: false, prefer_prelude: true }; + let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| { + match_error!("Failed to render template path `{}` at match location") + })?; self.rendered_template_paths.insert(path.clone(), mod_path); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 458b852e2a1e7..654a1cd316475 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -109,12 +109,12 @@ pub(crate) fn outgoing_calls( let expr = call.expr()?; let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?; match callable.kind() { - hir::CallableKind::Function(it) => { - let range = expr.syntax().text_range(); - it.try_to_nav(db).zip(Some(range)) - } + hir::CallableKind::Function(it) => it.try_to_nav(db), + hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db), + hir::CallableKind::TupleStruct(it) => it.try_to_nav(db), _ => None, } + .zip(Some(expr.syntax().text_range())) } ast::CallableExpr::MethodCall(expr) => { let range = expr.name_ref()?.syntax().text_range(); diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 8d765dfc91b41..f42613637b257 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -487,19 +487,23 @@ fn get_doc_base_urls( let system_doc = sysroot .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/")) .and_then(|it| Url::parse(&it).ok()); + let krate = def.krate(db); + let channel = krate + .and_then(|krate| db.toolchain_channel(krate.into())) + .unwrap_or(ReleaseChannel::Nightly) + .as_str(); // special case base url of `BuiltinType` to core // https://github.com/rust-lang/rust-analyzer/issues/12250 if let Definition::BuiltinType(..) = def { - let web_link = Url::parse("https://doc.rust-lang.org/nightly/core/").ok(); + let web_link = Url::parse(&format!("https://doc.rust-lang.org/{channel}/core/")).ok(); let system_link = system_doc.and_then(|it| it.join("core/").ok()); return (web_link, system_link); }; - let Some(krate) = def.krate(db) else { return Default::default() }; + let Some(krate) = krate else { return Default::default() }; let Some(display_name) = krate.display_name(db) else { return Default::default() }; let crate_data = &db.crate_graph()[krate.into()]; - let channel = db.toolchain_channel(krate.into()).unwrap_or(ReleaseChannel::Nightly).as_str(); let (web_base, local_base) = match &crate_data.origin { // std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself. diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 0cb8c485b2f27..3311bb48ad656 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1120,4 +1120,30 @@ fn test() { "#, ); } + + #[test] + fn type_hints_async_block() { + check_types( + r#" +//- minicore: future +async fn main() { + let _x = async { 8_i32 }; + //^^ impl Future +}"#, + ); + } + + #[test] + fn type_hints_async_block_with_tail_return_exp() { + check_types( + r#" +//- minicore: future +async fn main() { + let _x = async { + //^^ impl Future + return 8_i32; + }; +}"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 96e845b2f3235..fb50c49a3ae88 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -24,34 +24,29 @@ pub(super) fn hints( let (callable, arg_list) = get_callable(sema, &expr)?; let hints = callable - .params(sema.db) + .params() .into_iter() .zip(arg_list.args()) - .filter_map(|((param, _ty), arg)| { + .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; - let (param_name, name_syntax) = match param.as_ref()? { + let source = p.source(sema.db)?; + let (param_name, name_syntax) = match source.value.as_ref() { Either::Left(pat) => (pat.name()?, pat.name()), - Either::Right(pat) => match pat { + Either::Right(param) => match param.pat()? { ast::Pat::IdentPat(it) => (it.name()?, it.name()), _ => return None, }, }; + // make sure the file is cached so we can map out of macros + sema.parse_or_expand(source.file_id); Some((name_syntax, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { !should_hide_param_name_hint(sema, &callable, ¶m_name.text(), arg) }) .map(|(param, param_name, _, FileRange { range, .. })| { - let mut linked_location = None; - if let Some(name) = param { - if let hir::CallableKind::Function(f) = callable.kind() { - // assert the file is cached so we can map out of macros - if sema.source(f).is_some() { - linked_location = sema.original_range_opt(name.syntax()); - } - } - } + let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax())); let colon = if config.render_colons { ":" } else { "" }; let label = diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 64ffa59101765..2feea09840fa1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -79,7 +79,7 @@ impl RunnableKind { impl Runnable { // test package::module::testname - pub fn label(&self, target: Option) -> String { + pub fn label(&self, target: Option<&str>) -> String { match &self.kind { RunnableKind::Test { test_id, .. } => format!("test {test_id}"), RunnableKind::TestMod { path } => format!("test-mod {path}"), diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index b2eb5a5fff1e3..378a38892c7df 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -201,7 +201,21 @@ fn signature_help_for_call( variant.name(db).display(db) ); } - hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (), + hir::CallableKind::Closure(closure) => { + let fn_trait = closure.fn_trait(db); + format_to!(res.signature, "impl {fn_trait}") + } + hir::CallableKind::FnPtr => format_to!(res.signature, "fn"), + hir::CallableKind::FnImpl(fn_trait) => match callable.ty().as_adt() { + // FIXME: Render docs of the concrete trait impl function + Some(adt) => format_to!( + res.signature, + "<{} as {fn_trait}>::{}", + adt.name(db).display(db), + fn_trait.function_name() + ), + None => format_to!(res.signature, "impl {fn_trait}"), + }, } res.signature.push('('); @@ -210,12 +224,15 @@ fn signature_help_for_call( format_to!(res.signature, "{}", self_param.display(db)) } let mut buf = String::new(); - for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() { + for (idx, p) in callable.params().into_iter().enumerate() { buf.clear(); - if let Some(pat) = pat { - match pat { - Either::Left(_self) => format_to!(buf, "self: "), - Either::Right(pat) => format_to!(buf, "{}: ", pat), + if let Some(param) = p.source(sema.db) { + match param.value { + Either::Right(param) => match param.pat() { + Some(pat) => format_to!(buf, "{}: ", pat), + None => format_to!(buf, "?: "), + }, + Either::Left(_) => format_to!(buf, "self: "), } } // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is @@ -223,9 +240,9 @@ fn signature_help_for_call( // In that case, fall back to render definitions of the respective parameters. // This is overly conservative: we do not substitute known type vars // (see FIXME in tests::impl_trait) and falling back on any unknowns. - match (ty.contains_unknown(), fn_params.as_deref()) { + match (p.ty().contains_unknown(), fn_params.as_deref()) { (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)), - _ => format_to!(buf, "{}", ty.display(db)), + _ => format_to!(buf, "{}", p.ty().display(db)), } res.push_call_param(&buf); } @@ -242,9 +259,9 @@ fn signature_help_for_call( render(func.ret_type(db)) } hir::CallableKind::Function(_) - | hir::CallableKind::Closure + | hir::CallableKind::Closure(_) | hir::CallableKind::FnPtr - | hir::CallableKind::Other => render(callable.return_type()), + | hir::CallableKind::FnImpl(_) => render(callable.return_type()), hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} } Some(res) @@ -1345,15 +1362,43 @@ fn test() { S.foo($0); } r#" struct S; fn foo(s: S) -> i32 { 92 } +fn main() { + let _move = S; + (|s| {{_move}; foo(s)})($0) +} + "#, + expect![[r#" + impl FnOnce(s: S) -> i32 + ^^^^ + "#]], + ); + check( + r#" +struct S; +fn foo(s: S) -> i32 { 92 } fn main() { (|s| foo(s))($0) } "#, expect![[r#" - (s: S) -> i32 - ^^^^ + impl Fn(s: S) -> i32 + ^^^^ "#]], - ) + ); + check( + r#" +struct S; +fn foo(s: S) -> i32 { 92 } +fn main() { + let mut mutate = 0; + (|s| { mutate = 1; foo(s) })($0) +} + "#, + expect![[r#" + impl FnMut(s: S) -> i32 + ^^^^ + "#]], + ); } #[test] @@ -1383,12 +1428,81 @@ fn main(f: fn(i32, f64) -> char) { } "#, expect![[r#" - (i32, f64) -> char - --- ^^^ + fn(i32, f64) -> char + --- ^^^ "#]], ) } + #[test] + fn call_info_for_fn_impl() { + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnMut<(i32, f64)> for S {} +impl core::ops::Fn<(i32, f64)> for S {} +fn main() { + S($0); +} + "#, + expect![[r#" + ::call(i32, f64) -> char + ^^^ --- + "#]], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnMut<(i32, f64)> for S {} +impl core::ops::Fn<(i32, f64)> for S {} +fn main() { + S(1, $0); +} + "#, + expect![[r#" + ::call(i32, f64) -> char + --- ^^^ + "#]], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnOnce<(char, char)> for S { + type Output = f64; +} +fn main() { + S($0); +} + "#, + expect![""], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnOnce<(char, char)> for S { + type Output = f64; +} +fn main() { + // FIXME: The ide layer loses the calling info here so we get an ambiguous trait solve result + S(0i32, $0); +} + "#, + expect![""], + ); + } + #[test] fn call_info_for_unclosed_call() { check( @@ -1794,19 +1908,19 @@ fn f i32>(f: F) { } "#, expect![[r#" - (u8, u16) -> i32 - ^^ --- + impl FnOnce(u8, u16) -> i32 + ^^ --- "#]], ); check( r#" -fn f &T>(f: F) { +fn f &T>(f: F) { f($0) } "#, expect![[r#" - (&T, u16) -> &T - ^^ --- + impl FnMut(&T, u16) -> &T + ^^ --- "#]], ); } @@ -1826,7 +1940,7 @@ fn take( } "#, expect![[r#" - () -> i32 + impl Fn() -> i32 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs index 0439e509d21dd..2f387968c96a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs @@ -9,8 +9,9 @@ pub(super) fn highlight_escape_string( string: &T, start: TextSize, ) { + let text = string.text(); string.escaped_char_ranges(&mut |piece_range, char| { - if string.text()[piece_range.start().into()..].starts_with('\\') { + if text[piece_range.start().into()..].starts_with('\\') { let highlight = match char { Ok(_) => HlTag::EscapeSequence, Err(_) => HlTag::InvalidEscapeSequence, @@ -33,17 +34,15 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: } let text = char.text(); - if !text.starts_with('\'') || !text.ends_with('\'') { + let Some(text) = text + .strip_prefix('\'') + .and_then(|it| it.strip_suffix('\'')) + .filter(|it| it.starts_with('\\')) + else { return; - } - - let text = &text[1..text.len() - 1]; - if !text.starts_with('\\') { - return; - } + }; - let range = - TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1)); + let range = TextRange::at(start + TextSize::from(1), TextSize::from(text.len() as u32)); stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) } @@ -54,16 +53,14 @@ pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start: } let text = byte.text(); - if !text.starts_with("b'") || !text.ends_with('\'') { + let Some(text) = text + .strip_prefix("b'") + .and_then(|it| it.strip_suffix('\'')) + .filter(|it| it.starts_with('\\')) + else { return; - } - - let text = &text[2..text.len() - 1]; - if !text.starts_with('\\') { - return; - } + }; - let range = - TextRange::new(start + TextSize::from(2), start + TextSize::from(text.len() as u32 + 2)); + let range = TextRange::at(start + TextSize::from(2), TextSize::from(text.len() as u32)); stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) } diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 8e1f7fdcded7c..d2f423590e2c4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -65,7 +65,7 @@ impl WorkspaceBuildScripts { allowed_features: &FxHashSet, manifest_path: &ManifestPath, toolchain: Option<&Version>, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, ) -> io::Result { const RUST_1_75: Version = Version::new(1, 75, 0); let mut cmd = match config.run_build_script_command.as_deref() { @@ -75,7 +75,7 @@ impl WorkspaceBuildScripts { cmd } _ => { - let mut cmd = Sysroot::tool(sysroot, Tool::Cargo); + let mut cmd = sysroot.tool(Tool::Cargo); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&config.extra_args); @@ -149,7 +149,7 @@ impl WorkspaceBuildScripts { workspace: &CargoWorkspace, progress: &dyn Fn(String), toolchain: Option<&Version>, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, ) -> io::Result { let current_dir = match &config.invocation_location { InvocationLocation::Root(root) if config.run_build_script_command.is_some() => { @@ -195,7 +195,7 @@ impl WorkspaceBuildScripts { // This is not gonna be used anyways, so just construct a dummy here &ManifestPath::try_from(workspace_root.clone()).unwrap(), None, - None, + &Sysroot::empty(), )?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are @@ -412,7 +412,7 @@ impl WorkspaceBuildScripts { rustc: &CargoWorkspace, current_dir: &AbsPath, extra_env: &FxHashMap, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, ) -> Self { let mut bs = WorkspaceBuildScripts::default(); for p in rustc.packages() { @@ -420,7 +420,7 @@ impl WorkspaceBuildScripts { } let res = (|| { let target_libdir = (|| { - let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); + let mut cargo_config = sysroot.tool(Tool::Cargo); cargo_config.envs(extra_env); cargo_config .current_dir(current_dir) @@ -429,7 +429,7 @@ impl WorkspaceBuildScripts { if let Ok(it) = utf8_stdout(cargo_config) { return Ok(it); } - let mut cmd = Sysroot::tool(sysroot, Tool::Rustc); + let mut cmd = sysroot.tool(Tool::Rustc); cmd.envs(extra_env); cmd.args(["--print", "target-libdir"]); utf8_stdout(cmd) diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 9955f2687c917..632ba1cacf213 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -258,12 +258,12 @@ impl CargoWorkspace { cargo_toml: &ManifestPath, current_dir: &AbsPath, config: &CargoConfig, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, progress: &dyn Fn(String), ) -> anyhow::Result { let targets = find_list_of_build_targets(config, cargo_toml, sysroot); - let cargo = Sysroot::tool(sysroot, Tool::Cargo); + let cargo = sysroot.tool(Tool::Cargo); let mut meta = MetadataCommand::new(); meta.cargo_path(cargo.get_program()); cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default())); @@ -536,7 +536,7 @@ impl CargoWorkspace { fn find_list_of_build_targets( config: &CargoConfig, cargo_toml: &ManifestPath, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, ) -> Vec { if let Some(target) = &config.target { return [target.into()].to_vec(); @@ -553,9 +553,9 @@ fn find_list_of_build_targets( fn rustc_discover_host_triple( cargo_toml: &ManifestPath, extra_env: &FxHashMap, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, ) -> Option { - let mut rustc = Sysroot::tool(sysroot, Tool::Rustc); + let mut rustc = sysroot.tool(Tool::Rustc); rustc.envs(extra_env); rustc.current_dir(cargo_toml.parent()).arg("-vV"); tracing::debug!("Discovering host platform by {:?}", rustc); @@ -581,9 +581,9 @@ fn rustc_discover_host_triple( fn cargo_config_build_target( cargo_toml: &ManifestPath, extra_env: &FxHashMap, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, ) -> Vec { - let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); + let mut cargo_config = sysroot.tool(Tool::Cargo); cargo_config.envs(extra_env); cargo_config .current_dir(cargo_toml.parent()) diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index 5520cdaff6b00..88fb10a68c617 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -62,9 +62,9 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe pub(crate) fn cargo_config_env( manifest: &ManifestPath, extra_env: &FxHashMap, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, ) -> FxHashMap { - let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); + let mut cargo_config = sysroot.tool(Tool::Cargo); cargo_config.envs(extra_env); cargo_config .current_dir(manifest.parent()) diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs index 4f69b2b96f0c0..26499308ce96c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs @@ -10,10 +10,10 @@ use crate::{cfg::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; pub(crate) enum RustcCfgConfig<'a> { /// Use `rustc --print cfg`, either from with the binary from the sysroot or by discovering via /// [`toolchain::rustc`]. - Rustc(Option<&'a Sysroot>), + Rustc(&'a Sysroot), /// Use `cargo --print cfg`, either from with the binary from the sysroot or by discovering via /// [`toolchain::cargo`]. - Cargo(Option<&'a Sysroot>, &'a ManifestPath), + Cargo(&'a Sysroot, &'a ManifestPath), } pub(crate) fn get( @@ -65,7 +65,7 @@ fn get_rust_cfgs( ) -> anyhow::Result { let sysroot = match config { RustcCfgConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = Sysroot::tool(sysroot, Tool::Cargo); + let mut cmd = sysroot.tool(Tool::Cargo); cmd.envs(extra_env); cmd.current_dir(cargo_toml.parent()) @@ -86,7 +86,7 @@ fn get_rust_cfgs( RustcCfgConfig::Rustc(sysroot) => sysroot, }; - let mut cmd = Sysroot::tool(sysroot, Tool::Rustc); + let mut cmd = sysroot.tool(Tool::Rustc); cmd.envs(extra_env); cmd.args(["--print", "cfg", "-O"]); if let Some(target) = target { diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index e6bbe6ede8a0e..653e7157bcb3c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -4,7 +4,7 @@ //! but we can't process `.rlib` and need source code instead. The source code //! is typically installed with `rustup component add rust-src` command. -use std::{env, fs, ops, process::Command, sync::Arc}; +use std::{env, fs, ops, process::Command}; use anyhow::{format_err, Result}; use base_db::CrateName; @@ -16,30 +16,19 @@ use toolchain::{probe_for_binary, Tool}; use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Sysroot { - root: AbsPathBuf, - src_root: Option>>, + root: Option, + src_root: Option, mode: SysrootMode, -} - -impl Eq for Sysroot {} -impl PartialEq for Sysroot { - fn eq(&self, other: &Self) -> bool { - self.root == other.root - && self.mode == other.mode - && match (&self.src_root, &other.src_root) { - (Some(Ok(this)), Some(Ok(other))) => this == other, - (None, None) | (Some(Err(_)), Some(Err(_))) => true, - _ => false, - } - } + error: Option, } #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) enum SysrootMode { Workspace(CargoWorkspace), Stitched(Stitched), + Empty, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -89,70 +78,40 @@ pub(crate) struct SysrootCrateData { } impl Sysroot { + pub const fn empty() -> Sysroot { + Sysroot { root: None, src_root: None, mode: SysrootMode::Empty, error: None } + } + /// 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 + pub fn root(&self) -> Option<&AbsPath> { + self.root.as_deref() } /// 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) -> Option<&AbsPath> { - self.src_root.as_ref()?.as_deref().ok() + self.src_root.as_deref() } pub fn is_empty(&self) -> bool { match &self.mode { SysrootMode::Workspace(ws) => ws.packages().next().is_none(), SysrootMode::Stitched(stitched) => stitched.crates.is_empty(), + SysrootMode::Empty => true, } } - pub fn loading_warning(&self) -> Option { - let src_root = match &self.src_root { - None => return Some(format!("sysroot at `{}` has no library sources", self.root)), - Some(Ok(src_root)) => src_root, - Some(Err(e)) => return Some(e.to_string()), - }; - 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 { - " try running `rustup component add rust-src` to possible fix this" - }; - Some(format!("could not find libcore in loaded sysroot at `{}`{var_note}", src_root,)) - } else { - None - } - } - - pub fn check_has_core(&self) -> Result<(), String> { - let Some(Ok(src_root)) = &self.src_root else { return Ok(()) }; - 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 { - " try running `rustup component add rust-src` to possible fix this" - }; - Err(format!("could not find libcore in loaded sysroot at `{}`{var_note}", src_root,)) - } else { - Ok(()) - } + pub fn error(&self) -> Option<&str> { + self.error.as_deref() } pub fn num_packages(&self) -> usize { match &self.mode { SysrootMode::Workspace(ws) => ws.packages().count(), SysrootMode::Stitched(c) => c.crates().count(), + SysrootMode::Empty => 0, } } @@ -168,63 +127,50 @@ impl Sysroot { 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, Some(sysroot_src_dir), metadata)) - } - - pub fn discover_no_source( - dir: &AbsPath, - extra_env: &FxHashMap, - ) -> 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, Some(sysroot_src_dir), false)) + ) -> Sysroot { + let sysroot_dir = discover_sysroot_dir(dir, extra_env); + let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| { + discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env) + }); + Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir, metadata) } pub fn discover_with_src_override( current_dir: &AbsPath, extra_env: &FxHashMap, - src: AbsPathBuf, + sysroot_src_dir: 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, Some(Ok(src)), metadata)) + ) -> Sysroot { + let sysroot_dir = discover_sysroot_dir(current_dir, extra_env); + Sysroot::load_core_check(Some(sysroot_dir), Some(Ok(sysroot_src_dir)), metadata) } - pub fn discover_rustc_src(&self) -> Option { - get_rustc_src(&self.root) + pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf, metadata: bool) -> Sysroot { + let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir) + .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}")); + Sysroot::load_core_check(Some(Ok(sysroot_dir)), Some(sysroot_src_dir), metadata) } - 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, Some(sysroot_src_dir), metadata)) + pub fn discover_rustc_src(&self) -> Option { + get_rustc_src(self.root()?) } /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists. - pub fn tool(sysroot: Option<&Self>, tool: Tool) -> Command { - match sysroot { - Some(sysroot) => { + pub fn tool(&self, tool: Tool) -> Command { + match self.root() { + Some(root) => { // special case rustc, we can look that up directly in the sysroot's bin folder // as it should never invoke another cargo binary if let Tool::Rustc = tool { if let Some(path) = - probe_for_binary(sysroot.root.join("bin").join(Tool::Rustc.name()).into()) + probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into()) { return Command::new(path); } } let mut cmd = Command::new(tool.prefer_proxy()); - cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(&sysroot.root)); + cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root)); cmd } _ => Command::new(tool.path()), @@ -232,35 +178,89 @@ impl Sysroot { } pub fn discover_proc_macro_srv(&self) -> anyhow::Result { + let Some(root) = self.root() else { + return Err(anyhow::format_err!("no sysroot",)); + }; ["libexec", "lib"] .into_iter() - .map(|segment| self.root().join(segment).join("rust-analyzer-proc-macro-srv")) + .map(|segment| root.join(segment).join("rust-analyzer-proc-macro-srv")) .find_map(|server_path| probe_for_binary(server_path.into())) .map(AbsPathBuf::assert) .ok_or_else(|| { - anyhow::format_err!("cannot find proc-macro server in sysroot `{}`", self.root()) + anyhow::format_err!("cannot find proc-macro server in sysroot `{}`", root) }) } pub fn load( - sysroot_dir: AbsPathBuf, + sysroot_dir: Option, + sysroot_src_dir: Option, + metadata: bool, + ) -> Sysroot { + Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), metadata) + } + + fn load_core_check( + sysroot_dir: Option>, + sysroot_src_dir: Option>, + metadata: bool, + ) -> Sysroot { + let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, metadata); + if sysroot.error.is_none() { + if let Some(src_root) = &sysroot.src_root { + let has_core = match &sysroot.mode { + SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(), + SysrootMode::Empty => true, + }; + if !has_core { + let var_note = if env::var_os("RUST_SRC_PATH").is_some() { + " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" + } else { + ", try running `rustup component add rust-src` to possibly fix this" + }; + sysroot.error = Some(format!( + "sysroot at `{}` is missing a `core` library{var_note}", + src_root, + )); + } + } + } + sysroot + } + + fn load_( + sysroot_dir: Option>, sysroot_src_dir: Option>, metadata: bool, ) -> Sysroot { + let sysroot_dir = match sysroot_dir { + Some(Ok(sysroot_dir)) => Some(sysroot_dir), + Some(Err(e)) => { + return Sysroot { + root: None, + src_root: None, + mode: SysrootMode::Empty, + error: Some(e.to_string()), + } + } + None => None, + }; let sysroot_src_dir = match sysroot_src_dir { Some(Ok(sysroot_src_dir)) => sysroot_src_dir, Some(Err(e)) => { return Sysroot { root: sysroot_dir, - src_root: Some(Err(Arc::new(e))), - mode: SysrootMode::Stitched(Stitched { crates: Arena::default() }), + src_root: None, + mode: SysrootMode::Empty, + error: Some(e.to_string()), } } None => { return Sysroot { root: sysroot_dir, src_root: None, - mode: SysrootMode::Stitched(Stitched { crates: Arena::default() }), + mode: SysrootMode::Empty, + error: None, } } }; @@ -284,7 +284,7 @@ impl Sysroot { &sysroot_cargo_toml, ¤t_dir, &cargo_config, - None, + &Sysroot::empty(), &|_| (), ) .map_err(|e| { @@ -368,8 +368,9 @@ impl Sysroot { let cargo_workspace = CargoWorkspace::new(res, sysroot_cargo_toml); Some(Sysroot { root: sysroot_dir.clone(), - src_root: Some(Ok(sysroot_src_dir.clone())), + src_root: Some(sysroot_src_dir.clone()), mode: SysrootMode::Workspace(cargo_workspace), + error: None, }) })(); if let Some(sysroot) = sysroot { @@ -420,8 +421,9 @@ impl Sysroot { } Sysroot { root: sysroot_dir, - src_root: Some(Ok(sysroot_src_dir)), + src_root: Some(sysroot_src_dir), mode: SysrootMode::Stitched(stitched), + error: None, } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs index 4e810a0232ea9..8a8a2d32558b3 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs @@ -9,10 +9,10 @@ use crate::{utf8_stdout, ManifestPath, Sysroot}; pub enum RustcDataLayoutConfig<'a> { /// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via /// [`toolchain::rustc`]. - Rustc(Option<&'a Sysroot>), + Rustc(&'a Sysroot), /// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via /// [`toolchain::cargo`]. - Cargo(Option<&'a Sysroot>, &'a ManifestPath), + Cargo(&'a Sysroot, &'a ManifestPath), } pub fn get( @@ -28,7 +28,7 @@ pub fn get( }; let sysroot = match config { RustcDataLayoutConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = Sysroot::tool(sysroot, Tool::Cargo); + let mut cmd = sysroot.tool(Tool::Cargo); cmd.envs(extra_env); cmd.current_dir(cargo_toml.parent()) .args([ diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index 3d5a934fc92e7..a6730863d6b2d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -34,7 +34,7 @@ fn load_cargo_with_overrides( cargo_config_extra_env: Default::default(), }, cfg_overrides, - sysroot: Err(None), + sysroot: Sysroot::empty(), rustc_cfg: Vec::new(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), @@ -57,7 +57,7 @@ fn load_cargo_with_fake_sysroot( rustc: Err(None), cargo_config_extra_env: Default::default(), }, - sysroot: Ok(get_fake_sysroot()), + sysroot: get_fake_sysroot(), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, @@ -77,7 +77,7 @@ fn load_cargo_with_fake_sysroot( fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { let data = get_test_json_file(file); let project = rooted_project_json(data); - let sysroot = Ok(get_fake_sysroot()); + let sysroot = get_fake_sysroot(); let project_workspace = ProjectWorkspace { kind: ProjectWorkspaceKind::Json(project), sysroot, @@ -144,7 +144,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, Some(Ok(sysroot_src_dir)), false) + Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), false) } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { @@ -281,12 +281,11 @@ fn smoke_test_real_sysroot_cargo() { let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let sysroot = Ok(Sysroot::discover( + let sysroot = Sysroot::discover( AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), true, - ) - .unwrap()); + ); let project_workspace = ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 85621444e3395..0d2174073a2a9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -48,7 +48,7 @@ pub struct PackageRoot { pub struct ProjectWorkspace { pub kind: ProjectWorkspaceKind, /// The sysroot loaded for this workspace. - pub sysroot: Result>, + pub sysroot: Sysroot, /// Holds cfg flags for the current target. We get those by running /// `rustc --print cfg`. // FIXME: make this a per-crate map, as, eg, build.rs might have a @@ -112,7 +112,7 @@ impl fmt::Debug for ProjectWorkspace { .debug_struct("Cargo") .field("root", &cargo.workspace_root().file_name()) .field("n_packages", &cargo.packages().len()) - .field("sysroot", &sysroot.is_ok()) + .field("n_sysroot_crates", &sysroot.num_packages()) .field( "n_rustc_compiler_crates", &rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(rc, _)| rc.packages().len()), @@ -125,11 +125,9 @@ impl fmt::Debug for ProjectWorkspace { .finish(), ProjectWorkspaceKind::Json(project) => { 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.num_packages()); - } debug_struct + .field("n_crates", &project.n_crates()) + .field("n_sysroot_crates", &sysroot.num_packages()) .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) @@ -144,7 +142,7 @@ impl fmt::Debug for ProjectWorkspace { .debug_struct("DetachedFiles") .field("file", &file) .field("cargo_script", &cargo_script.is_some()) - .field("sysroot", &sysroot.is_ok()) + .field("n_sysroot_crates", &sysroot.num_packages()) .field("cargo_script", &cargo_script.is_some()) .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) @@ -158,7 +156,7 @@ impl fmt::Debug for ProjectWorkspace { fn get_toolchain_version( current_dir: &AbsPath, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, tool: Tool, extra_env: &FxHashMap, prefix: &str, @@ -213,41 +211,37 @@ impl ProjectWorkspace { } ProjectManifest::CargoToml(cargo_toml) => { let sysroot = match (&config.sysroot, &config.sysroot_src) { - (Some(RustLibSource::Path(path)), None) => { - 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, 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(), Some(Ok(sysroot_src.clone())), config.sysroot_query_metadata)) - } + (Some(RustLibSource::Discover), None) => Sysroot::discover( + cargo_toml.parent(), + &config.extra_env, + config.sysroot_query_metadata, + ), (Some(RustLibSource::Discover), Some(sysroot_src)) => { Sysroot::discover_with_src_override( cargo_toml.parent(), &config.extra_env, - 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}")) - }) + sysroot_src.clone(), + config.sysroot_query_metadata, + ) } - (None, _) => Err(None), + (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir( + path.clone(), + config.sysroot_query_metadata, + ), + (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load( + Some(sysroot.clone()), + Some(sysroot_src.clone()), + config.sysroot_query_metadata, + ), + (None, _) => Sysroot::empty(), }; - let sysroot_ref = sysroot.as_ref().ok(); - - if let Ok(sysroot) = &sysroot { - tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); - } + tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); let rustc_dir = match &config.rustc_source { Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), Some(RustLibSource::Discover) => { - sysroot_ref.and_then(Sysroot::discover_rustc_src).ok_or_else(|| { + sysroot.discover_rustc_src().ok_or_else(|| { Some("Failed to discover rustc source for sysroot.".to_owned()) }) } @@ -263,7 +257,7 @@ impl ProjectWorkspace { features: crate::CargoFeatures::default(), ..config.clone() }, - sysroot_ref, + &sysroot, progress, ) { Ok(meta) => { @@ -272,7 +266,7 @@ impl ProjectWorkspace { &workspace, cargo_toml.parent(), &config.extra_env, - sysroot_ref + &sysroot ); Ok(Box::new((workspace, buildscripts))) } @@ -290,7 +284,7 @@ impl ProjectWorkspace { let toolchain = get_toolchain_version( cargo_toml.parent(), - sysroot_ref, + &sysroot, Tool::Cargo, &config.extra_env, "cargo ", @@ -298,12 +292,12 @@ impl ProjectWorkspace { let rustc_cfg = rustc_cfg::get( config.target.as_deref(), &config.extra_env, - RustcCfgConfig::Cargo(sysroot_ref, cargo_toml), + RustcCfgConfig::Cargo(&sysroot, cargo_toml), ); let cfg_overrides = config.cfg_overrides.clone(); let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Cargo(sysroot_ref, cargo_toml), + RustcDataLayoutConfig::Cargo(&sysroot, cargo_toml), config.target.as_deref(), &config.extra_env, ); @@ -315,7 +309,7 @@ impl ProjectWorkspace { cargo_toml, cargo_toml.parent(), config, - sysroot_ref, + &sysroot, progress, ) .with_context(|| { @@ -326,7 +320,7 @@ impl ProjectWorkspace { let cargo = CargoWorkspace::new(meta, cargo_toml.clone()); let cargo_config_extra_env = - cargo_config_env(cargo_toml, &config.extra_env, sysroot_ref); + cargo_config_env(cargo_toml, &config.extra_env, &sysroot); ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { cargo, @@ -354,32 +348,13 @@ impl ProjectWorkspace { extra_env: &FxHashMap, cfg_overrides: &CfgOverrides, ) -> ProjectWorkspace { - let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { - (Some(sysroot), Some(sysroot_src)) => { - Ok(Sysroot::load(sysroot, Some(Ok(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, Some(Ok(sysroot_src)), false)) - } - (None, Some(sysroot_src)) => { - // assume sysroot is structured like rustup's and guess `sysroot` - let mut sysroot = sysroot_src.clone(); - for _ in 0..5 { - sysroot.pop(); - } - Ok(Sysroot::load(sysroot, Some(Ok(sysroot_src)), false)) - } - (None, None) => Err(None), - }; - let sysroot_ref = sysroot.as_ref().ok(); - let cfg_config = RustcCfgConfig::Rustc(sysroot_ref); - let data_layout_config = RustcDataLayoutConfig::Rustc(sysroot_ref); + let sysroot = + Sysroot::load(project_json.sysroot.clone(), project_json.sysroot_src.clone(), false); + let cfg_config = RustcCfgConfig::Rustc(&sysroot); + let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot); let toolchain = match get_toolchain_version( project_json.path(), - sysroot_ref, + &sysroot, Tool::Rustc, extra_env, "rustc ", @@ -410,24 +385,16 @@ impl ProjectWorkspace { let dir = detached_file.parent(); let sysroot = match &config.sysroot { 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}"))) + Sysroot::discover_sysroot_src_dir(path.clone(), config.sysroot_query_metadata) + } + Some(RustLibSource::Discover) => { + Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata) } - Some(RustLibSource::Discover) => 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), + None => Sysroot::empty(), }; - let sysroot_ref = sysroot.as_ref().ok(); let toolchain = - match get_toolchain_version(dir, sysroot_ref, Tool::Rustc, &config.extra_env, "rustc ") - { + match get_toolchain_version(dir, &sysroot, Tool::Rustc, &config.extra_env, "rustc ") { Ok(it) => it, Err(e) => { tracing::error!("{e}"); @@ -435,25 +402,24 @@ impl ProjectWorkspace { } }; - let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref)); + let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(&sysroot)); let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(sysroot_ref), + RustcDataLayoutConfig::Rustc(&sysroot), None, &config.extra_env, ); let cargo_script = - CargoWorkspace::fetch_metadata(detached_file, dir, config, sysroot_ref, &|_| ()) - .ok() - .map(|ws| { + CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, &|_| ()).ok().map( + |ws| { ( CargoWorkspace::new(ws, detached_file.clone()), WorkspaceBuildScripts::default(), ) - }); + }, + ); - let cargo_config_extra_env = - cargo_config_env(detached_file, &config.extra_env, sysroot_ref); + let cargo_config_extra_env = cargo_config_env(detached_file, &config.extra_env, &sysroot); Ok(ProjectWorkspace { kind: ProjectWorkspaceKind::DetachedFile { file: detached_file.to_owned(), @@ -489,7 +455,7 @@ impl ProjectWorkspace { cargo, progress, self.toolchain.as_ref(), - self.sysroot.as_ref().ok(), + &self.sysroot, ) .with_context(|| { format!("Failed to run build scripts for {}", cargo.workspace_root()) @@ -562,17 +528,7 @@ impl ProjectWorkspace { } pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { - match &self.sysroot { - Ok(sysroot) => sysroot.discover_proc_macro_srv(), - Err(None) => Err(anyhow::format_err!( - "cannot find proc-macro server, the workspace `{}` is missing a sysroot", - self.manifest_or_root() - )), - Err(Some(e)) => Err(anyhow::format_err!( - "cannot find proc-macro server, the workspace `{}` is missing a sysroot: {e}", - self.manifest_or_root() - )), - } + self.sysroot.discover_proc_macro_srv() } /// Returns the roots for the current `ProjectWorkspace` @@ -580,39 +536,37 @@ impl ProjectWorkspace { /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec { let mk_sysroot = || { - self.sysroot.as_ref().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![], - }; + let mut r = match self.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(_) | SysrootMode::Empty => vec![], + }; - r.push(PackageRoot { - is_local: false, - include: sysroot.src_root().map(|it| it.to_path_buf()).into_iter().collect(), - exclude: Vec::new(), - }); - r - }) + r.push(PackageRoot { + is_local: false, + include: self.sysroot.src_root().map(|it| it.to_path_buf()).into_iter().collect(), + exclude: Vec::new(), + }); + r }; match &self.kind { ProjectWorkspaceKind::Json(project) => project @@ -731,19 +685,15 @@ impl ProjectWorkspace { } pub fn n_packages(&self) -> usize { + let sysroot_package_len = self.sysroot.num_packages(); match &self.kind { - ProjectWorkspaceKind::Json(project) => { - let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); - sysroot_package_len + project.n_crates() - } + ProjectWorkspaceKind::Json(project) => sysroot_package_len + project.n_crates(), ProjectWorkspaceKind::Cargo { cargo, rustc, .. } => { let rustc_package_len = rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(it, _)| it.packages().len()); - let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); cargo.packages().len() + sysroot_package_len + rustc_package_len } ProjectWorkspaceKind::DetachedFile { cargo: cargo_script, .. } => { - let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); sysroot_package_len + cargo_script.as_ref().map_or(1, |(cargo, _)| cargo.packages().len()) } @@ -764,7 +714,7 @@ impl ProjectWorkspace { rustc_cfg.clone(), load, project, - sysroot.as_ref().ok(), + sysroot, extra_env, cfg_overrides, ), @@ -780,7 +730,7 @@ impl ProjectWorkspace { load, rustc.as_ref().map(|a| a.as_ref()).ok(), cargo, - sysroot.as_ref().ok(), + sysroot, rustc_cfg.clone(), cfg_overrides, build_scripts, @@ -793,7 +743,7 @@ impl ProjectWorkspace { &mut |path| load(path), None, cargo, - sysroot.as_ref().ok(), + sysroot, rustc_cfg.clone(), cfg_overrides, build_scripts, @@ -803,7 +753,7 @@ impl ProjectWorkspace { rustc_cfg.clone(), load, file, - sysroot.as_ref().ok(), + sysroot, cfg_overrides, ) }, @@ -811,9 +761,7 @@ impl ProjectWorkspace { ), }; - if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Stitched(_))) - && crate_graph.patch_cfg_if() - { + if matches!(sysroot.mode(), SysrootMode::Stitched(_)) && crate_graph.patch_cfg_if() { tracing::debug!("Patched std to depend on cfg-if") } else { tracing::debug!("Did not patch std to depend on cfg-if") @@ -892,15 +840,14 @@ fn project_json_to_crate_graph( rustc_cfg: Vec, load: FileLoader<'_>, project: &ProjectJson, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, extra_env: &FxHashMap, override_cfg: &CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { let mut res = (CrateGraph::default(), ProcMacroPaths::default()); let (crate_graph, proc_macros) = &mut res; - let sysroot_deps = sysroot - .as_ref() - .map(|sysroot| sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load)); + let (public_deps, libproc_macro) = + sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load); let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned()); let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); @@ -978,11 +925,9 @@ fn project_json_to_crate_graph( for (from_idx, krate) in project.crates() { if let Some(&from) = idx_to_crate_id.get(&from_idx) { - if let Some((public_deps, libproc_macro)) = &sysroot_deps { - public_deps.add_to_crate_graph(crate_graph, from); - if let Some(proc_macro) = libproc_macro { - add_proc_macro_dep(crate_graph, from, *proc_macro, krate.is_proc_macro); - } + public_deps.add_to_crate_graph(crate_graph, from); + if let Some(proc_macro) = libproc_macro { + add_proc_macro_dep(crate_graph, from, proc_macro, krate.is_proc_macro); } for dep in &krate.deps { @@ -999,7 +944,7 @@ fn cargo_to_crate_graph( load: FileLoader<'_>, rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, rustc_cfg: Vec, override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, @@ -1008,10 +953,8 @@ fn cargo_to_crate_graph( let mut res = (CrateGraph::default(), ProcMacroPaths::default()); let crate_graph = &mut res.0; let proc_macros = &mut res.1; - let (public_deps, libproc_macro) = match sysroot { - Some(sysroot) => sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load), - None => (SysrootPublicDeps::default(), None), - }; + let (public_deps, libproc_macro) = + sysroot_to_crate_graph(crate_graph, sysroot, rustc_cfg.clone(), load); let cfg_options = CfgOptions::from_iter(rustc_cfg); @@ -1188,15 +1131,13 @@ fn detached_file_to_crate_graph( rustc_cfg: Vec, load: FileLoader<'_>, detached_file: &ManifestPath, - sysroot: Option<&Sysroot>, + sysroot: &Sysroot, override_cfg: &CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "detached_file_to_crate_graph").entered(); let mut crate_graph = CrateGraph::default(); - let (public_deps, _libproc_macro) = match sysroot { - Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load), - None => (SysrootPublicDeps::default(), None), - }; + let (public_deps, _libproc_macro) = + sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load); let mut cfg_options = CfgOptions::from_iter(rustc_cfg); cfg_options.insert_atom("test".into()); @@ -1431,7 +1372,7 @@ fn sysroot_to_crate_graph( load, None, cargo, - None, + &Sysroot::empty(), rustc_cfg, &CfgOverrides { global: CfgDiff::new( @@ -1554,6 +1495,7 @@ fn sysroot_to_crate_graph( stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); (public_deps, libproc_macro) } + SysrootMode::Empty => (SysrootPublicDeps { deps: vec![] }, None), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 5208aa9bf08c3..bded41932c038 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -8,7 +8,8 @@ use std::{ use hir::{ db::{DefDatabase, ExpandDatabase, HirDatabase}, - Adt, AssocItem, Crate, DefWithBody, HasSource, HirDisplay, HirFileIdExt, ModuleDef, Name, + Adt, AssocItem, Crate, DefWithBody, HasSource, HirDisplay, HirFileIdExt, ImportPathConfig, + ModuleDef, Name, }; use hir_def::{ body::{BodySourceMap, SyntheticSyntax}, @@ -438,8 +439,13 @@ impl flags::AnalysisStats { let mut formatter = |_: &hir::Type| todo.clone(); let mut syntax_hit_found = false; for term in found_terms { - let generated = - term.gen_source_code(&scope, &mut formatter, false, true).unwrap(); + let generated = term + .gen_source_code( + &scope, + &mut formatter, + ImportPathConfig { prefer_no_std: false, prefer_prelude: true }, + ) + .unwrap(); syntax_hit_found |= trim(&original_text) == trim(&generated); // Validate if type-checks diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 85f964b1dd931..e9a4db7a2b0dd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -69,11 +69,9 @@ impl Tester { let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; - let sysroot = - Ok(Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env, false) - .unwrap()); + let sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env, false); let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(sysroot.as_ref().ok()), + RustcDataLayoutConfig::Rustc(&sysroot), None, &cargo_config.extra_env, ); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 6c332ae1cb2fe..a8d1e72aed9f8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1031,6 +1031,8 @@ impl Config { && completion_item_edit_resolve(&self.caps), enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), + enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), + term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, full_function_signatures: self .completion_fullFunctionSignatures_enable(source_root) .to_owned(), @@ -1039,8 +1041,6 @@ impl Config { CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), CallableCompletionDef::None => None, }, - insert_use: self.insert_use_config(source_root), - prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), snippet_cap: SnippetCap::new(try_or_def!( self.caps .text_document @@ -1051,11 +1051,11 @@ impl Config { .as_ref()? .snippet_support? )), + insert_use: self.insert_use_config(source_root), + prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), + prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), snippets: self.snippets.clone().to_vec(), limit: self.completion_limit(source_root).to_owned(), - enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), - term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, - prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index f64e66183d1b4..79b87ecd58ff4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -87,6 +87,7 @@ pub(crate) struct GlobalState { pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, pub(crate) last_flycheck_error: Option, + pub(crate) diagnostics_received: bool, // Test explorer pub(crate) test_run_session: Option>, @@ -224,6 +225,7 @@ impl GlobalState { flycheck_sender, flycheck_receiver, last_flycheck_error: None, + diagnostics_received: false, test_run_session: None, test_run_sender, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 5ee0456c15df8..1e24bf3aae3b0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -860,6 +860,11 @@ pub(crate) fn handle_runnables( if cmd == "run" && spec.target_kind != TargetKind::Bin { continue; } + let cwd = if cmd != "test" || spec.target_kind == TargetKind::Bin { + spec.workspace_root.clone() + } else { + spec.cargo_toml.parent().to_path_buf() + }; let mut cargo_args = vec![cmd.to_owned(), "--package".to_owned(), spec.package.clone()]; let all_targets = cmd != "run" && !is_crate_no_std; @@ -876,6 +881,7 @@ pub(crate) fn handle_runnables( kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::CargoRunnable { workspace_root: Some(spec.workspace_root.clone().into()), + cwd: Some(cwd.into()), override_cargo: config.override_cargo.clone(), cargo_args, cargo_extra_args: config.cargo_extra_args.clone(), @@ -893,6 +899,7 @@ pub(crate) fn handle_runnables( kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::CargoRunnable { workspace_root: None, + cwd: None, override_cargo: config.override_cargo, cargo_args: vec!["check".to_owned(), "--workspace".to_owned()], cargo_extra_args: config.cargo_extra_args, @@ -1783,18 +1790,18 @@ pub(crate) fn handle_open_docs( let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match &ws.kind { ProjectWorkspaceKind::Cargo { cargo, .. } | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => { - Some((cargo, ws.sysroot.as_ref().ok())) + Some((cargo, &ws.sysroot)) } ProjectWorkspaceKind::Json { .. } => None, ProjectWorkspaceKind::DetachedFile { .. } => None, }); let (cargo, sysroot) = match ws_and_sysroot { - Some((ws, sysroot)) => (Some(ws), sysroot), + Some((ws, sysroot)) => (Some(ws), Some(sysroot)), _ => (None, None), }; - let sysroot = sysroot.map(|p| p.root().as_str()); + let sysroot = sysroot.and_then(|p| p.root()).map(|it| it.as_str()); let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_str()); let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index cc83d6246bf55..5d617780b6df6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -139,6 +139,7 @@ fn integrated_completion_benchmark() { enable_self_on_the_fly: true, enable_private_editable: true, enable_term_search: true, + term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), @@ -149,11 +150,10 @@ fn integrated_completion_benchmark() { group: true, skip_glob_imports: true, }, - snippets: Vec::new(), prefer_no_std: false, prefer_prelude: true, + snippets: Vec::new(), limit: None, - term_search_fuel: 200, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -184,6 +184,7 @@ fn integrated_completion_benchmark() { enable_self_on_the_fly: true, enable_private_editable: true, enable_term_search: true, + term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), @@ -194,11 +195,10 @@ fn integrated_completion_benchmark() { group: true, skip_glob_imports: true, }, - snippets: Vec::new(), prefer_no_std: false, prefer_prelude: true, + snippets: Vec::new(), limit: None, - term_search_fuel: 200, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -227,6 +227,7 @@ fn integrated_completion_benchmark() { enable_self_on_the_fly: true, enable_private_editable: true, enable_term_search: true, + term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), snippet_cap: SnippetCap::new(true), @@ -237,11 +238,10 @@ fn integrated_completion_benchmark() { group: true, skip_glob_imports: true, }, - snippets: Vec::new(), prefer_no_std: false, prefer_prelude: true, + snippets: Vec::new(), limit: None, - term_search_fuel: 200, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 2cf9b53f7c8d2..aa75633ac3561 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -441,6 +441,8 @@ pub struct CargoRunnable { pub override_cargo: Option, #[serde(skip_serializing_if = "Option::is_none")] pub workspace_root: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cwd: Option, // command, --package and --lib stuff pub cargo_args: Vec, // user-specified additional cargo args, like `--release`. @@ -500,7 +502,6 @@ pub struct ServerStatusParams { pub health: Health, pub quiescent: bool, pub message: Option, - pub workspace_info: Option, } #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 03daccc99c45e..86368c9eea87f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1360,10 +1360,14 @@ pub(crate) fn runnable( let config = snap.config.runnables(); let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?; let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); - let target = spec.as_ref().map(|s| s.target.clone()); + let cwd = match runnable.kind { + ide::RunnableKind::Bin { .. } => workspace_root.clone().map(|it| it.into()), + _ => spec.as_ref().map(|it| it.cargo_toml.parent().into()), + }; + let target = spec.as_ref().map(|s| s.target.as_str()); + let label = runnable.label(target); let (cargo_args, executable_args) = CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg); - let label = runnable.label(target); let location = location_link(snap, None, runnable.nav)?; Ok(lsp_ext::Runnable { @@ -1372,6 +1376,7 @@ pub(crate) fn runnable( kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::CargoRunnable { workspace_root: workspace_root.map(|it| it.into()), + cwd, override_cargo: config.override_cargo, cargo_args, cargo_extra_args: config.cargo_extra_args, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 5435be3dc272e..7acd302867c3b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -804,6 +804,10 @@ impl GlobalState { fn handle_flycheck_msg(&mut self, message: flycheck::Message) { match message { flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => { + if !self.diagnostics_received { + self.diagnostics.clear_check(id); + self.diagnostics_received = true; + } let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( &self.config.diagnostics_map(), @@ -832,7 +836,7 @@ impl GlobalState { flycheck::Message::Progress { id, progress } => { let (state, message) = match progress { flycheck::Progress::DidStart => { - self.diagnostics.clear_check(id); + self.diagnostics_received = false; (Progress::Begin, None) } flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), @@ -848,6 +852,9 @@ impl GlobalState { flycheck::Progress::DidFinish(result) => { self.last_flycheck_error = result.err().map(|err| format!("cargo check failed to start: {err}")); + if !self.diagnostics_received { + self.diagnostics.clear_check(id); + } (Progress::End, None) } }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index fd14efa1da568..627be7e951adc 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -103,7 +103,6 @@ impl GlobalState { health: lsp_ext::Health::Ok, quiescent: self.is_quiescent(), message: None, - workspace_info: None, }; let mut message = String::new(); @@ -164,53 +163,37 @@ impl GlobalState { let proc_macro_clients = self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None)); - let mut workspace_info = "Loaded workspaces:\n".to_owned(); for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) { - format_to!(workspace_info, "- `{}`\n", ws.manifest_or_root()); - format_to!(workspace_info, " - sysroot:"); - - match ws.sysroot.as_ref() { - Err(None) => format_to!(workspace_info, " None"), - Err(Some(e)) => { - status.health |= lsp_ext::Health::Warning; - format_to!(workspace_info, " {e}"); - } - Ok(s) => { - format_to!(workspace_info, " `{}`", s.root().to_string()); - if let Some(err) = s - .check_has_core() - .err() - .inspect(|_| status.health |= lsp_ext::Health::Warning) - { - format_to!(workspace_info, " ({err})"); - } - if let Some(src_root) = s.src_root() { - format_to!( - workspace_info, - "\n - sysroot source: `{}`", - src_root - ); - } - format_to!(workspace_info, "\n"); - } + if let Some(err) = ws.sysroot.error() { + status.health |= lsp_ext::Health::Warning; + format_to!( + message, + "Workspace `{}` has sysroot errors: ", + ws.manifest_or_root() + ); + message.push_str(err); + message.push_str("\n\n"); } - - if let ProjectWorkspaceKind::Cargo { rustc: Err(Some(e)), .. } = &ws.kind { + if let ProjectWorkspaceKind::Cargo { rustc: Err(Some(err)), .. } = &ws.kind { status.health |= lsp_ext::Health::Warning; - format_to!(workspace_info, " - rustc workspace: {e}\n"); + format_to!( + message, + "Failed loading rustc_private crates for workspace `{}`: ", + ws.manifest_or_root() + ); + message.push_str(err); + message.push_str("\n\n"); }; - if let Some(proc_macro_client) = proc_macro_client { - format_to!(workspace_info, " - proc-macro server: "); - match proc_macro_client { - Ok(it) => format_to!(workspace_info, "`{}`\n", it.path()), - Err(e) => { - status.health |= lsp_ext::Health::Warning; - format_to!(workspace_info, "{e}\n") - } - } + if let Some(Err(err)) = proc_macro_client { + status.health |= lsp_ext::Health::Warning; + format_to!( + message, + "Failed spawning proc-macro server for workspace `{}`: {err}", + ws.manifest_or_root() + ); + message.push_str("\n\n"); } } - status.workspace_info = Some(workspace_info); } if !message.is_empty() { @@ -534,8 +517,8 @@ impl GlobalState { .map(|(a, b)| (a.clone(), b.clone())) .chain( ws.sysroot - .as_ref() - .map(|it| ("RUSTUP_TOOLCHAIN".to_owned(), it.root().to_string())), + .root() + .map(|it| ("RUSTUP_TOOLCHAIN".to_owned(), it.to_string())), ) .collect(), @@ -719,7 +702,7 @@ impl GlobalState { } ProjectWorkspaceKind::DetachedFile { .. } => return None, }, - ws.sysroot.as_ref().ok().map(|sysroot| sysroot.root().to_owned()), + ws.sysroot.root().map(ToOwned::to_owned), )) }) .map(|(id, (root, manifest_path), sysroot_root)| { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs index 59b229cd06454..66481d3d7f5e1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs @@ -21,7 +21,7 @@ fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace { rustc: Err(None), cargo_config_extra_env: Default::default(), }, - sysroot: Ok(get_fake_sysroot()), + sysroot: get_fake_sysroot(), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, @@ -69,7 +69,7 @@ fn get_fake_sysroot() -> Sysroot { // fake sysroot, so we give them both the same path: let sysroot_dir = AbsPathBuf::assert_utf8(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(sysroot_dir, Some(Ok(sysroot_src_dir)), false) + Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), false) } #[test] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 5a1397bbb0e77..43a8305010582 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -260,6 +260,7 @@ fn main() {} "executableArgs": ["test_eggs", "--exact", "--show-output"], "cargoExtraArgs": [], "overrideCargo": null, + "cwd": server.path().join("foo"), "workspaceRoot": server.path().join("foo") }, "kind": "cargo", @@ -279,6 +280,7 @@ fn main() {} { "args": { "overrideCargo": null, + "cwd": server.path().join("foo"), "workspaceRoot": server.path().join("foo"), "cargoArgs": [ "test", @@ -325,6 +327,7 @@ fn main() {} "executableArgs": [], "cargoExtraArgs": [], "overrideCargo": null, + "cwd": server.path().join("foo"), "workspaceRoot": server.path().join("foo") }, "kind": "cargo", @@ -336,6 +339,7 @@ fn main() {} "executableArgs": [], "cargoExtraArgs": [], "overrideCargo": null, + "cwd": server.path().join("foo"), "workspaceRoot": server.path().join("foo") }, "kind": "cargo", @@ -415,6 +419,7 @@ mod tests { "args": { "overrideCargo": null, "workspaceRoot": server.path().join(runnable), + "cwd": server.path().join(runnable), "cargoArgs": [ "test", "--package", @@ -432,6 +437,94 @@ mod tests { } } +// The main fn in packages should be run from the workspace root +#[test] +fn test_runnables_cwd() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /foo/Cargo.toml +[workspace] +members = ["mainpkg", "otherpkg"] + +//- /foo/mainpkg/Cargo.toml +[package] +name = "mainpkg" +version = "0.1.0" + +//- /foo/mainpkg/src/main.rs +fn main() {} + +//- /foo/otherpkg/Cargo.toml +[package] +name = "otherpkg" +version = "0.1.0" + +//- /foo/otherpkg/src/lib.rs +#[test] +fn otherpkg() {} +"#, + ) + .root("foo") + .server() + .wait_until_workspace_is_loaded(); + + server.request::( + RunnablesParams { text_document: server.doc_id("foo/mainpkg/src/main.rs"), position: None }, + json!([ + "{...}", + { + "label": "cargo test -p mainpkg --all-targets", + "kind": "cargo", + "args": { + "overrideCargo": null, + "workspaceRoot": server.path().join("foo"), + "cwd": server.path().join("foo"), + "cargoArgs": [ + "test", + "--package", + "mainpkg", + "--all-targets" + ], + "cargoExtraArgs": [], + "executableArgs": [] + }, + }, + "{...}", + "{...}" + ]), + ); + + server.request::( + RunnablesParams { text_document: server.doc_id("foo/otherpkg/src/lib.rs"), position: None }, + json!([ + "{...}", + { + "label": "cargo test -p otherpkg --all-targets", + "kind": "cargo", + "args": { + "overrideCargo": null, + "workspaceRoot": server.path().join("foo"), + "cwd": server.path().join("foo").join("otherpkg"), + "cargoArgs": [ + "test", + "--package", + "otherpkg", + "--all-targets" + ], + "cargoExtraArgs": [], + "executableArgs": [] + }, + }, + "{...}", + "{...}" + ]), + ); +} + #[test] fn test_format_document() { if skip_slow_tests() { @@ -1059,11 +1152,11 @@ fn resolve_proc_macro() { return; } - let sysroot = project_model::Sysroot::discover_no_source( + let sysroot = project_model::Sysroot::discover( &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()), &Default::default(), - ) - .unwrap(); + false, + ); let proc_macro_server_path = sysroot.discover_proc_macro_srv().unwrap(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index c3d6f50e6b080..b0fbe7101c113 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -378,9 +378,26 @@ impl ast::UseTreeList { /// Remove the unnecessary braces in current `UseTreeList` pub fn remove_unnecessary_braces(mut self) { + // Returns true iff there is a single subtree and it is not the self keyword. The braces in + // `use x::{self};` are necessary and so we should not remove them. + let has_single_subtree_that_is_not_self = |u: &ast::UseTreeList| { + if let Some((single_subtree,)) = u.use_trees().collect_tuple() { + // We have a single subtree, check whether it is self. + + let is_self = single_subtree.path().as_ref().map_or(false, |path| { + path.segment().and_then(|seg| seg.self_token()).is_some() + && path.qualifier().is_none() + }); + + !is_self + } else { + // Not a single subtree + false + } + }; + let remove_brace_in_use_tree_list = |u: &ast::UseTreeList| { - let use_tree_count = u.use_trees().count(); - if use_tree_count == 1 { + if has_single_subtree_that_is_not_self(u) { if let Some(a) = u.l_curly_token() { ted::remove(a) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index 16599881d6439..1ce548f8fc703 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -8,6 +8,7 @@ use std::{ use rustc_lexer::unescape::{ unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode, }; +use stdx::always; use crate::{ ast::{self, AstToken}, @@ -181,25 +182,25 @@ pub trait IsString: AstToken { self.quote_offsets().map(|it| it.quotes.1) } fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result)) { - let text_range_no_quotes = match self.text_range_between_quotes() { - Some(it) => it, - None => return, - }; + let Some(text_range_no_quotes) = self.text_range_between_quotes() else { return }; let start = self.syntax().text_range().start(); let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; unescape_unicode(text, Self::MODE, &mut |range, unescaped_char| { - let text_range = - TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()); - cb(text_range + offset, unescaped_char); + if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) { + cb(TextRange::new(s, e) + offset, unescaped_char); + } }); } fn map_range_up(&self, range: TextRange) -> Option { let contents_range = self.text_range_between_quotes()?; - assert!(TextRange::up_to(contents_range.len()).contains_range(range)); - Some(range + contents_range.start()) + if always!(TextRange::up_to(contents_range.len()).contains_range(range)) { + Some(range + contents_range.start()) + } else { + None + } } } diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 46c1ccb79bf9b..1c91e856e72e4 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@