diff --git a/Cargo.lock b/Cargo.lock index 3a7ebb9da443..f92668a6a978 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -523,6 +523,7 @@ dependencies = [ "hir-def", "hir-expand", "hir-ty", + "indexmap", "intern", "itertools", "rustc-hash 2.0.0", @@ -1377,6 +1378,7 @@ version = "0.0.0" dependencies = [ "expect-test", "intern", + "libc", "libloading", "memmap2", "object 0.33.0", @@ -1434,7 +1436,7 @@ dependencies = [ "libc", "perf-event", "tikv-jemalloc-ctl", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1513,9 +1515,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.87.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b782af0a7a8df16ddf43cd70da9f17bc3b1ce712c9e4992b6edb16f5f53632" +checksum = "d5246e9e1f450333a990877eabbc36fe0567e7cedd56d5365db319e14079cf2a" dependencies = [ "bitflags 2.7.0", "ra-ap-rustc_index", @@ -1524,9 +1526,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.87.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce5742f134960482f543b35ecebec3cacc6d79a9a685713518b4d8d70c5f9aa8" +checksum = "59fd8e4f5b34c434ec111efb0e0614954db048b9307d3b2e4cc3c915da9d2160" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1534,9 +1536,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.87.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ea011fcf68309a8835ad01d91c032cb18444617b00e2cab21d45b208164441" +checksum = "2d34973fe081392bd1edb022e865e9952fcaa093f9cdae183edce64472e5e889" dependencies = [ "proc-macro2", "quote", @@ -1545,9 +1547,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.87.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb76f0a4d4c20859e41f0a23bff0f37ab9ca9171c214a6c7dd72ea69434865dc" +checksum = "52fa42c582e21b35e8f61a5afe3c63a9c722d995826762eb19b18beeccf5157f" dependencies = [ "unicode-properties", "unicode-xid", @@ -1555,9 +1557,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.87.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06080bd35078305421a62da77f3c128482d8d44441b6da8ce9d146d1cd9cdb5b" +checksum = "740383328d7033393e5385f4a6073b880d5811b0fc0fd2559e481f905940f2f8" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1565,9 +1567,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.87.0" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a3154fe4c20c177d7b3c678a2d3a97aba0cca156ddef88959915041889daf0" +checksum = "c39f544728f32cebffb1a8b92ba3c1f3dcb4144081438d192137ed197d479a9d" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.0.0", @@ -1719,7 +1721,7 @@ dependencies = [ "vfs", "vfs-notify", "walkdir", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "xflags", "xshell", ] @@ -1942,7 +1944,7 @@ dependencies = [ "jod-thread", "libc", "miow", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ab00d0173956..1029844cd3ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.82" +rust-version = "1.83" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] @@ -87,11 +87,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.87", default-features = false } -ra-ap-rustc_parse_format = { version = "0.87", default-features = false } -ra-ap-rustc_index = { version = "0.87", default-features = false } -ra-ap-rustc_abi = { version = "0.87", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.87", default-features = false } +ra-ap-rustc_lexer = { version = "0.91", default-features = false } +ra-ap-rustc_parse_format = { version = "0.91", default-features = false } +ra-ap-rustc_index = { version = "0.91", default-features = false } +ra-ap-rustc_abi = { version = "0.91", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.91", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index d85bc9a4320f..12f5f6ad79ab 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -244,7 +244,7 @@ bitflags::bitflags! { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitData { pub name: Name, - pub items: Vec<(Name, AssocItemId)>, + pub items: Box<[(Name, AssocItemId)]>, pub flags: TraitFlags, pub visibility: RawVisibility, // box it as the vec is usually empty anyways @@ -360,7 +360,7 @@ impl TraitAliasData { pub struct ImplData { pub target_trait: Option, pub self_ty: TypeRefId, - pub items: Box<[AssocItemId]>, + pub items: Box<[(Name, AssocItemId)]>, pub is_negative: bool, pub is_unsafe: bool, // box it as the vec is usually empty anyways @@ -393,7 +393,6 @@ impl ImplData { collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items); let (items, macro_calls, diagnostics) = collector.finish(); - let items = items.into_iter().map(|(_, item)| item).collect(); ( Arc::new(ImplData { @@ -648,12 +647,12 @@ impl<'a> AssocItemCollector<'a> { fn finish( self, ) -> ( - Vec<(Name, AssocItemId)>, + Box<[(Name, AssocItemId)]>, Option, MacroCallId)>>>, Vec, ) { ( - self.items, + self.items.into_boxed_slice(), if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) }, self.diagnostics, ) diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index ac262950f13c..a3ffdf770e81 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -10,7 +10,6 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::{format_to, TupleExt}; -use syntax::ToSmolStr; use triomphe::Arc; use crate::{ @@ -88,9 +87,9 @@ impl ImportMap { .iter() // We've only collected items, whose name cannot be tuple field so unwrapping is fine. .flat_map(|(&item, (info, _))| { - info.iter().enumerate().map(move |(idx, info)| { - (item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32) - }) + info.iter() + .enumerate() + .map(move |(idx, info)| (item, info.name.as_str(), idx as u32)) }) .collect(); importables.sort_by(|(_, l_info, _), (_, r_info, _)| { @@ -441,7 +440,7 @@ pub fn search_dependencies( } fn search_maps( - db: &dyn DefDatabase, + _db: &dyn DefDatabase, import_maps: &[Arc], mut stream: fst::map::Union<'_>, query: &Query, @@ -464,11 +463,7 @@ fn search_maps( .then(|| (item, &import_infos[info_idx as usize])) }) .filter(|&(_, info)| { - query.search_mode.check( - &query.query, - query.case_sensitive, - &info.name.unescaped().display(db.upcast()).to_smolstr(), - ) + query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str()) }); res.extend(iter.map(TupleExt::head)); } diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 2c3eb5c8e5e4..0fec7674109b 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -162,6 +162,20 @@ impl ItemScope { .map(move |name| (name, self.get(name))) } + pub fn values(&self) -> impl Iterator)> + '_ { + self.values.iter().map(|(n, &i)| (n, i)) + } + + pub fn types( + &self, + ) -> impl Iterator)> + '_ { + self.types.iter().map(|(n, &i)| (n, i)) + } + + pub fn macros(&self) -> impl Iterator)> + '_ { + self.macros.iter().map(|(n, &i)| (n, i)) + } + pub fn imports(&self) -> impl Iterator + '_ { self.use_imports_types .keys() @@ -263,11 +277,6 @@ impl ItemScope { self.unnamed_consts.iter().copied() } - /// Iterate over all module scoped macros - pub(crate) fn macros(&self) -> impl Iterator + '_ { - self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) - } - /// Iterate over all legacy textual scoped macros visible at the end of the module pub fn legacy_macros(&self) -> impl Iterator + '_ { self.legacy_macros.iter().map(|(name, def)| (name, &**def)) diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 0629d87e5444..afdc49a2dc59 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -107,7 +107,7 @@ impl LangItems { for (_, module_data) in crate_def_map.modules() { for impl_def in module_data.scope.impls() { lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef); - for assoc in db.impl_data(impl_def).items.iter().copied() { + for &(_, assoc) in db.impl_data(impl_def).items.iter() { match assoc { AssocItemId::FunctionId(f) => { lang_items.collect_lang_item(db, f, LangItemTarget::Function) diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 8af27513ebc0..da9ffae8aab0 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -502,7 +502,7 @@ impl ModuleId { } /// Whether this module represents the crate root module - fn is_crate_root(&self) -> bool { + pub fn is_crate_root(&self) -> bool { self.local_id == DefMap::ROOT && self.block.is_none() } } @@ -910,6 +910,7 @@ pub enum AssocItemId { ConstId(ConstId), TypeAliasId(TypeAliasId), } + // FIXME: not every function, ... is actually an assoc item. maybe we should make // sure that you can only turn actual assoc items into AssocItemIds. This would // require not implementing From, and instead having some checked way of diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 6808c3cd4d79..1e4b42dff5fb 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -717,8 +717,8 @@ impl DefCollector<'_> { } } None => { - for (name, def) in root_scope.macros() { - self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate)); + for (name, it) in root_scope.macros() { + self.def_map.macro_use_prelude.insert(name.clone(), (it.def, extern_crate)); } } } diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs index ab4ffbb2c1e4..d7e4ca41cd5d 100644 --- a/crates/hir-def/src/nameres/mod_resolution.rs +++ b/crates/hir-def/src/nameres/mod_resolution.rs @@ -4,7 +4,6 @@ use base_db::AnchoredPath; use hir_expand::{name::Name, HirFileIdExt}; use limit::Limit; use span::EditionedFileId; -use syntax::ToSmolStr as _; use crate::{db::DefDatabase, HirFileId}; @@ -35,7 +34,7 @@ impl ModDir { let path = match attr_path { None => { let mut path = self.dir_path.clone(); - path.push(&name.unescaped().display_no_db().to_smolstr()); + path.push(name.as_str()); path } Some(attr_path) => { @@ -66,7 +65,7 @@ impl ModDir { name: &Name, attr_path: Option<&str>, ) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> { - let name = name.unescaped(); + let name = name.as_str(); let mut candidate_files = ArrayVec::<_, 2>::new(); match attr_path { @@ -74,16 +73,8 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None => { - candidate_files.push(format!( - "{}{}.rs", - self.dir_path.0, - name.display(db.upcast()) - )); - candidate_files.push(format!( - "{}{}/mod.rs", - self.dir_path.0, - name.display(db.upcast()) - )); + candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); + candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); } }; @@ -97,7 +88,7 @@ impl ModDir { let dir_path = if root_dir_owner { DirPath::empty() } else { - DirPath::new(format!("{}/", name.display(db.upcast()))) + DirPath::new(format!("{}/", name)) }; if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) { return Ok(( diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index df48ce673731..0b9b6da8d513 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -166,6 +166,17 @@ impl Resolver { db: &dyn DefDatabase, path: &Path, ) -> Option<(TypeNs, Option, Option)> { + self.resolve_path_in_type_ns_with_prefix_info(db, path).map( + |(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import), + ) + } + + pub fn resolve_path_in_type_ns_with_prefix_info( + &self, + db: &dyn DefDatabase, + path: &Path, + ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> + { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => it.mod_path(), @@ -181,7 +192,12 @@ impl Resolver { | LangItemTarget::ImplDef(_) | LangItemTarget::Static(_) => return None, }; - return Some((type_ns, seg.as_ref().map(|_| 1), None)); + return Some(( + type_ns, + seg.as_ref().map(|_| 1), + None, + ResolvePathResultPrefixInfo::default(), + )); } }; let first_name = path.segments().first()?; @@ -197,17 +213,32 @@ impl Resolver { Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue, Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { - return Some((TypeNs::GenericParam(id), remaining_idx(), None)); + return Some(( + TypeNs::GenericParam(id), + remaining_idx(), + None, + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::ImplDefScope(impl_) => { if *first_name == sym::Self_.clone() { - return Some((TypeNs::SelfType(impl_), remaining_idx(), None)); + return Some(( + TypeNs::SelfType(impl_), + remaining_idx(), + None, + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::AdtScope(adt) => { if *first_name == sym::Self_.clone() { - return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None)); + return Some(( + TypeNs::AdtSelfType(adt), + remaining_idx(), + None, + ResolvePathResultPrefixInfo::default(), + )); } } Scope::BlockScope(m) => { @@ -220,18 +251,6 @@ impl Resolver { self.module_scope.resolve_path_in_type_ns(db, path) } - pub fn resolve_path_in_type_ns_fully_with_imports( - &self, - db: &dyn DefDatabase, - path: &Path, - ) -> Option<(TypeNs, Option)> { - let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?; - if unresolved.is_some() { - return None; - } - Some((res, imp)) - } - pub fn resolve_path_in_type_ns_fully( &self, db: &dyn DefDatabase, @@ -986,11 +1005,12 @@ impl ModuleItemMap { &self, db: &dyn DefDatabase, path: &ModPath, - ) -> Option<(TypeNs, Option, Option)> { - let (module_def, idx, _) = + ) -> Option<(TypeNs, Option, Option, ResolvePathResultPrefixInfo)> + { + let (module_def, idx, prefix_info) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); let (res, import) = to_type_ns(module_def)?; - Some((res, idx, import)) + Some((res, idx, import, prefix_info)) } } diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index 4edb68359225..c4473e454a1b 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -240,12 +240,12 @@ impl Visibility { if a_ancestors.any(|m| m == mod_b.local_id) { // B is above A - return Some(Visibility::Module(mod_a, expl_b)); + return Some(Visibility::Module(mod_a, expl_a)); } if b_ancestors.any(|m| m == mod_a.local_id) { // A is above B - return Some(Visibility::Module(mod_b, expl_a)); + return Some(Visibility::Module(mod_b, expl_b)); } None diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 4f2f9ec40d0b..f0cf7ebf479f 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -23,15 +23,6 @@ pub struct ModPath { segments: SmallVec<[Name; 1]>, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct UnescapedModPath<'a>(&'a ModPath); - -impl<'a> UnescapedModPath<'a> { - pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - UnescapedDisplay { db, path: self } - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, @@ -135,9 +126,11 @@ impl ModPath { _ => None, } } - - pub fn unescaped(&self) -> UnescapedModPath<'_> { - UnescapedModPath(self) + pub fn display_verbatim<'a>( + &'a self, + db: &'a dyn crate::db::ExpandDatabase, + ) -> impl fmt::Display + 'a { + Display { db, path: self, edition: None } } pub fn display<'a>( @@ -145,7 +138,7 @@ impl ModPath { db: &'a dyn crate::db::ExpandDatabase, edition: Edition, ) -> impl fmt::Display + 'a { - Display { db, path: self, edition } + Display { db, path: self, edition: Some(edition) } } } @@ -158,23 +151,12 @@ impl Extend for ModPath { struct Display<'a> { db: &'a dyn ExpandDatabase, path: &'a ModPath, - edition: Edition, + edition: Option, } impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition)) - } -} - -struct UnescapedDisplay<'a> { - db: &'a dyn ExpandDatabase, - path: &'a UnescapedModPath<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path.0, f, Escape::No) + display_fmt_path(self.db, self.path, f, self.edition) } } @@ -184,16 +166,11 @@ impl From for ModPath { } } -enum Escape { - No, - IfNeeded(Edition), -} - fn display_fmt_path( db: &dyn ExpandDatabase, path: &ModPath, f: &mut fmt::Formatter<'_>, - escaped: Escape, + edition: Option, ) -> fmt::Result { let mut first_segment = true; let mut add_segment = |s| -> fmt::Result { @@ -221,10 +198,10 @@ fn display_fmt_path( f.write_str("::")?; } first_segment = false; - match escaped { - Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?, - Escape::No => segment.unescaped().display(db).fmt(f)?, - } + match edition { + Some(edition) => segment.display(db, edition).fmt(f)?, + None => fmt::Display::fmt(segment.as_str(), f)?, + }; } Ok(()) } @@ -398,6 +375,9 @@ macro_rules! __known_path { (core::fmt::Debug) => {}; (std::fmt::format) => {}; (core::ops::Try) => {}; + (core::convert::From) => {}; + (core::convert::TryFrom) => {}; + (core::str::FromStr) => {}; ($path:path) => { compile_error!("Please register your known path in the path module") }; diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 54a61a096d70..848870c3a384 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -4,14 +4,14 @@ use std::fmt; use intern::{sym, Symbol}; use span::{Edition, SyntaxContextId}; -use syntax::ast; use syntax::utils::is_raw_identifier; +use syntax::{ast, format_smolstr}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are /// not there yet! /// -/// Note that the rawness (`r#`) of names does not depend on whether they are written raw. +/// Note that the rawness (`r#`) of names is not preserved. Names are always stored without a `r#` prefix. /// This is because we want to show (in completions etc.) names as raw depending on the needs /// of the current crate, for example if it is edition 2021 complete `gen` even if the defining /// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well. @@ -51,31 +51,25 @@ impl PartialEq for Name { } } +impl PartialEq<&Symbol> for Name { + fn eq(&self, &sym: &&Symbol) -> bool { + self.symbol == *sym + } +} + impl PartialEq for Symbol { fn eq(&self, name: &Name) -> bool { *self == name.symbol } } -/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnescapedName<'a>(&'a Name); - -impl<'a> UnescapedName<'a> { - pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - _ = db; - UnescapedDisplay { name: self } - } - #[doc(hidden)] - pub fn display_no_db(self) -> impl fmt::Display + 'a { - UnescapedDisplay { name: self } +impl PartialEq for &Symbol { + fn eq(&self, name: &Name) -> bool { + **self == name.symbol } } impl Name { - /// Note: this is private to make creating name from random string hard. - /// Hopefully, this should allow us to integrate hygiene cleaner in the - /// future, and to switch to interned representation of names. fn new_text(text: &str) -> Name { Name { symbol: Symbol::intern(text), ctx: () } } @@ -86,7 +80,10 @@ impl Name { // Can't do that for all `SyntaxContextId`s because it breaks Salsa. ctx.remove_root_edition(); _ = ctx; - Self::new_text(text) + match text.strip_prefix("r#") { + Some(text) => Self::new_text(text), + None => Self::new_text(text), + } } pub fn new_root(text: &str) -> Name { @@ -95,16 +92,45 @@ impl Name { } pub fn new_tuple_field(idx: usize) -> Name { - Name { symbol: Symbol::intern(&idx.to_string()), ctx: () } + let symbol = match idx { + 0 => sym::INTEGER_0.clone(), + 1 => sym::INTEGER_1.clone(), + 2 => sym::INTEGER_2.clone(), + 3 => sym::INTEGER_3.clone(), + 4 => sym::INTEGER_4.clone(), + 5 => sym::INTEGER_5.clone(), + 6 => sym::INTEGER_6.clone(), + 7 => sym::INTEGER_7.clone(), + 8 => sym::INTEGER_8.clone(), + 9 => sym::INTEGER_9.clone(), + 10 => sym::INTEGER_10.clone(), + 11 => sym::INTEGER_11.clone(), + 12 => sym::INTEGER_12.clone(), + 13 => sym::INTEGER_13.clone(), + 14 => sym::INTEGER_14.clone(), + 15 => sym::INTEGER_15.clone(), + _ => Symbol::intern(&idx.to_string()), + }; + Name { symbol, ctx: () } } pub fn new_lifetime(lt: &ast::Lifetime) -> Name { - Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () } + let text = lt.text(); + match text.strip_prefix("'r#") { + Some(text) => Self::new_text(&format_smolstr!("'{text}")), + None => Self::new_text(text.as_str()), + } + } + + pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { + debug_assert!(!symbol.as_str().starts_with("r#")); + _ = ctx; + Self { symbol, ctx: () } } - /// Resolve a name from the text of token. - fn resolve(raw_text: &str) -> Name { - Name::new_text(raw_text.trim_start_matches("r#")) + // FIXME: This needs to go once we have hygiene + pub fn new_symbol_root(sym: Symbol) -> Self { + Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015)) } /// A fake name for things missing in the source code. @@ -141,19 +167,19 @@ impl Name { self.symbol.as_str().parse().ok() } + /// Whether this name needs to be escaped in the given edition via `r#`. + pub fn needs_escape(&self, edition: Edition) -> bool { + is_raw_identifier(self.symbol.as_str(), edition) + } + /// Returns the text this name represents if it isn't a tuple field. + /// + /// Do not use this for user-facing text, use `display` instead to handle editions properly. + // FIXME: This should take a database argument to hide the interning pub fn as_str(&self) -> &str { self.symbol.as_str() } - pub fn unescaped(&self) -> UnescapedName<'_> { - UnescapedName(self) - } - - pub fn is_escaped(&self, edition: Edition) -> bool { - is_raw_identifier(self.symbol.as_str(), edition) - } - pub fn display<'a>( &'a self, db: &dyn crate::db::ExpandDatabase, @@ -163,7 +189,7 @@ impl Name { self.display_no_db(edition) } - // FIXME: Remove this + // FIXME: Remove this in favor of `display`, see fixme on `as_str` #[doc(hidden)] pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ { Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) } @@ -172,21 +198,6 @@ impl Name { pub fn symbol(&self) -> &Symbol { &self.symbol } - - pub const fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { - _ = ctx; - Self { symbol, ctx: () } - } - - // FIXME: This needs to go once we have hygiene - pub const fn new_symbol_root(sym: Symbol) -> Self { - Self { symbol: sym, ctx: () } - } - - #[inline] - pub fn eq_ident(&self, ident: &str) -> bool { - self.as_str() == ident.trim_start_matches("r#") - } } struct Display<'a> { @@ -203,17 +214,6 @@ impl fmt::Display for Display<'_> { } } -struct UnescapedDisplay<'a> { - name: UnescapedName<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let symbol = self.name.0.symbol.as_str(); - fmt::Display::fmt(symbol, f) - } -} - pub trait AsName { fn as_name(&self) -> Name; } @@ -222,14 +222,14 @@ impl AsName for ast::NameRef { fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::resolve(&self.text()), + None => Name::new_root(&self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::resolve(&self.text()) + Name::new_root(&self.text()) } } @@ -244,7 +244,7 @@ impl AsName for ast::NameOrNameRef { impl AsName for tt::Ident { fn as_name(&self) -> Name { - Name::resolve(self.sym.as_str()) + Name::new_root(self.sym.as_str()) } } @@ -262,6 +262,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_text(&self.name) + Name::new_root(&self.name) } } diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index 9f01f1eb2590..c8ff6cba3dd1 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -856,7 +856,7 @@ fn impl_def_datum( let associated_ty_value_ids = impl_data .items .iter() - .filter_map(|item| match item { + .filter_map(|(_, item)| match item { AssocItemId::TypeAliasId(type_alias) => Some(*type_alias), _ => None, }) diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 4991d173b9c4..774991560e9c 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -231,8 +231,7 @@ impl<'a> DeclValidator<'a> { .filter_map(|(pat_id, pat)| match pat { Pat::Bind { id, .. } => { let bind_name = &body.bindings[*id].name; - let mut suggested_text = - to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?; + let mut suggested_text = to_lower_snake_case(bind_name.as_str())?; if is_raw_identifier(&suggested_text, edition) { suggested_text.insert_str(0, "r#"); } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 2523aba53833..9283c46d0f61 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -277,7 +277,7 @@ impl CapturedItem { /// Converts the place to a name that can be inserted into source code. pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { let body = db.body(owner); - let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string(); + let mut result = body[self.place.local].name.as_str().to_owned(); for proj in &self.place.projections { match proj { ProjectionElem::Deref => {} diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 0c1f63880cd2..108171586ea8 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -197,6 +197,7 @@ fn layout_of_simd_ty( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: 0, })) } @@ -313,6 +314,7 @@ pub fn layout_of_ty_query( size, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Slice(element) => { @@ -326,6 +328,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Str => Layout { @@ -337,6 +340,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: 0, }, // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 24f67fd66020..432b8f4d94ea 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -761,8 +761,8 @@ impl<'a> TyLoweringContext<'a> { path: &Path, on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), ) -> Option<(TypeNs, Option)> { - let (resolution, remaining_index, _) = - self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?; + let (resolution, remaining_index, _, prefix_info) = + self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?; let segments = path.segments(); match path { @@ -771,13 +771,12 @@ impl<'a> TyLoweringContext<'a> { _ => return Some((resolution, remaining_index)), }; - let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index { - None => ( - segments.strip_last(), - segments.len() - 1, - segments.last().expect("resolved path has at least one element"), - ), - Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()), + let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index { + None if prefix_info.enum_variant => { + (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2)) + } + None => (segments.strip_last(), segments.len() - 1, None), + Some(i) => (segments.take(i - 1), i - 1, None), }; for (i, mod_segment) in module_segments.iter().enumerate() { @@ -792,9 +791,23 @@ impl<'a> TyLoweringContext<'a> { } } + if let Some(enum_segment) = enum_segment { + if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }, + ); + } + } + self.handle_type_ns_resolution( &resolution, - resolved_segment, + segments.get(resolved_segment_idx).expect("should have resolved segment"), resolved_segment_idx, on_diagnostic, ); diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 62b071b2f326..182032f04812 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -746,16 +746,9 @@ fn lookup_impl_assoc_item_for_trait_ref( let table = InferenceTable::new(db, env); let (impl_data, impl_subst) = find_matching_impl(impls, table, trait_ref)?; - let item = impl_data.items.iter().find_map(|&it| match it { - AssocItemId::FunctionId(f) => { - (db.function_data(f).name == *name).then_some(AssocItemId::FunctionId(f)) - } - AssocItemId::ConstId(c) => db - .const_data(c) - .name - .as_ref() - .map(|n| n == name) - .and_then(|result| if result { Some(AssocItemId::ConstId(c)) } else { None }), + let item = impl_data.items.iter().find_map(|(n, it)| match *it { + AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)), + AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)), AssocItemId::TypeAliasId(_) => None, })?; Some((item, impl_subst)) @@ -850,7 +843,7 @@ fn is_inherent_impl_coherent( }; rustc_has_incoherent_inherent_impls && !impl_data.items.is_empty() - && impl_data.items.iter().copied().all(|assoc| match assoc { + && impl_data.items.iter().all(|&(_, assoc)| match assoc { AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl, AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl, AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl, @@ -1399,7 +1392,7 @@ fn iterate_inherent_methods( callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { for &impl_id in impls.for_self_ty(self_ty) { - for &item in table.db.impl_data(impl_id).items.iter() { + for &(ref item_name, item) in table.db.impl_data(impl_id).items.iter() { let visible = match is_valid_impl_method_candidate( table, self_ty, @@ -1408,6 +1401,7 @@ fn iterate_inherent_methods( name, impl_id, item, + item_name, ) { IsValidCandidate::Yes => true, IsValidCandidate::NotVisible => false, @@ -1467,6 +1461,7 @@ fn is_valid_impl_method_candidate( name: Option<&Name>, impl_id: ImplId, item: AssocItemId, + item_name: &Name, ) -> IsValidCandidate { match item { AssocItemId::FunctionId(f) => is_valid_impl_fn_candidate( @@ -1477,11 +1472,12 @@ fn is_valid_impl_method_candidate( receiver_ty, self_ty, visible_from_module, + item_name, ), AssocItemId::ConstId(c) => { let db = table.db; check_that!(receiver_ty.is_none()); - check_that!(name.is_none_or(|n| db.const_data(c).name.as_ref() == Some(n))); + check_that!(name.is_none_or(|n| n == item_name)); if let Some(from_module) = visible_from_module { if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) { @@ -1565,11 +1561,13 @@ fn is_valid_impl_fn_candidate( receiver_ty: Option<&Ty>, self_ty: &Ty, visible_from_module: Option, + item_name: &Name, ) -> IsValidCandidate { + check_that!(name.is_none_or(|n| n == item_name)); + let db = table.db; let data = db.function_data(fn_id); - check_that!(name.is_none_or(|n| n == &data.name)); if let Some(from_module) = visible_from_module { if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) { cov_mark::hit!(autoderef_candidate_not_visible); diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index acdcbb4757d7..00da9b251768 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -435,7 +435,7 @@ pub(crate) fn visit_module( visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb); for impl_id in crate_def_map[module_id].scope.impls() { let impl_data = db.impl_data(impl_id); - for &item in impl_data.items.iter() { + for &(_, item) in impl_data.items.iter() { match item { AssocItemId::FunctionId(it) => { let body = db.body(it.into()); diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 6aadc5c4f7e7..c68ff706e481 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -20,6 +20,7 @@ itertools.workspace = true smallvec.workspace = true tracing.workspace = true triomphe.workspace = true +indexmap.workspace = true # local deps base-db.workspace = true diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index efa88d7c83ee..4938478bb121 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -775,29 +775,16 @@ impl Module { AssocItemId::ConstId(id) => !db.const_data(id).has_body, AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(), }); - impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map( - |&item| { - Some(( - item, - match item { - AssocItemId::FunctionId(it) => db.function_data(it).name.clone(), - AssocItemId::ConstId(it) => { - db.const_data(it).name.as_ref()?.clone() - } - AssocItemId::TypeAliasId(it) => db.type_alias_data(it).name.clone(), - }, - )) - }, - )); + impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().cloned()); let redundant = impl_assoc_items_scratch .iter() - .filter(|(id, name)| { + .filter(|(name, id)| { !items.iter().any(|(impl_name, impl_item)| { discriminant(impl_item) == discriminant(id) && impl_name == name }) }) - .map(|(item, name)| (name.clone(), AssocItem::from(*item))); + .map(|(name, item)| (name.clone(), AssocItem::from(*item))); for (name, assoc_item) in redundant { acc.push( TraitImplRedundantAssocItems { @@ -812,7 +799,7 @@ impl Module { let missing: Vec<_> = required_items .filter(|(name, id)| { - !impl_assoc_items_scratch.iter().any(|(impl_item, impl_name)| { + !impl_assoc_items_scratch.iter().any(|(impl_name, impl_item)| { discriminant(impl_item) == discriminant(id) && impl_name == name }) }) @@ -844,7 +831,7 @@ impl Module { source_map, ); - for &item in db.impl_data(impl_def.id).items.iter() { + for &(_, item) in db.impl_data(impl_def.id).items.iter() { AssocItem::from(item).diagnostics(db, acc, style_lints); } } @@ -2769,6 +2756,15 @@ impl Trait { traits.iter().map(|tr| Trait::from(*tr)).collect() } + pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq) -> Option { + db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then( + |&(_, it)| match it { + AssocItemId::FunctionId(id) => Some(Function { id }), + _ => None, + }, + ) + } + pub fn items(self, db: &dyn HirDatabase) -> Vec { db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() } @@ -4307,7 +4303,7 @@ impl Impl { } pub fn items(self, db: &dyn HirDatabase) -> Vec { - db.impl_data(self.id).items.iter().map(|&it| it.into()).collect() + db.impl_data(self.id).items.iter().map(|&(_, it)| it.into()).collect() } pub fn is_negative(self, db: &dyn HirDatabase) -> bool { @@ -4686,6 +4682,10 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool)) } + pub fn is_str(&self) -> bool { + matches!(self.ty.kind(Interner), TyKind::Str) + } + pub fn is_never(&self) -> bool { matches!(self.ty.kind(Interner), TyKind::Never) } @@ -5165,7 +5165,7 @@ impl Type { let impls = db.inherent_impls_in_crate(krate); for impl_def in impls.for_self_ty(&self.ty) { - for &item in db.impl_data(*impl_def).items.iter() { + for &(_, item) in db.impl_data(*impl_def).items.iter() { if callback(item) { return; } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 92f6c4b5ab3a..09470bed9cfb 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -39,8 +39,8 @@ use stdx::TupleExt; use syntax::{ algo::skip_trivia_token, ast::{self, HasAttrs as _, HasGenericParams}, - AstNode, AstToken, Direction, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, - TextRange, TextSize, + AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, + TextSize, }; use triomphe::Arc; @@ -1439,6 +1439,22 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } + /// Env is used to derive the trait environment + // FIXME: better api for the trait environment + pub fn resolve_trait_impl_method( + &self, + env: Type, + trait_: Trait, + func: Function, + subst: impl IntoIterator, + ) -> Option { + let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None); + for s in subst { + substs = substs.push(s.ty); + } + Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into()) + } + fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option { self.analyze(range_pat.syntax())?.resolve_range_pat(self.db, range_pat) } @@ -1467,6 +1483,8 @@ impl<'db> SemanticsImpl<'db> { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } + // This does not resolve the method call to the correct trait impl! + // We should probably fix that. pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } @@ -1587,14 +1605,11 @@ impl<'db> SemanticsImpl<'db> { pub fn resolve_mod_path_relative( &self, to: Module, - segments: impl IntoIterator, + segments: impl IntoIterator, ) -> Option> { let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items( self.db.upcast(), - &ModPath::from_segments( - hir_def::path::PathKind::Plain, - segments.into_iter().map(|it| Name::new_root(&it)), - ), + &ModPath::from_segments(hir_def::path::PathKind::Plain, segments), ); Some(items.iter_items().map(|(item, _)| item.into())) } diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs index ec65ea9a9a8b..d5dfb9857186 100644 --- a/crates/hir/src/semantics/child_by_source.rs +++ b/crates/hir/src/semantics/child_by_source.rs @@ -56,7 +56,7 @@ impl ChildBySource for ImplId { res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id); }, ); - data.items.iter().for_each(|&item| { + data.items.iter().for_each(|&(_, item)| { add_assoc_item(db, res, file_id, item); }); } diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index f8416f86bf9f..a6b8ed70c363 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -1,27 +1,34 @@ //! File symbol extraction. +use either::Either; use hir_def::{ db::DefDatabase, - item_scope::ItemInNs, + item_scope::{ImportId, ImportOrExternCrate}, + per_ns::Item, src::{HasChildSource, HasSource}, - AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, - TraitId, + visibility::{Visibility, VisibilityExplicitness}, + AdtId, AssocItemId, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, }; -use hir_expand::HirFileId; +use hir_expand::{name::Name, HirFileId}; use hir_ty::{ db::HirDatabase, display::{hir_display_with_types_map, HirDisplay}, }; +use intern::Symbol; +use rustc_hash::FxHashMap; use span::Edition; use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr}; use crate::{Module, ModuleDef, Semantics}; +pub type FxIndexSet = indexmap::IndexSet>; + /// The actual data that is stored in the index. It should be as compact as /// possible. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FileSymbol { - pub name: SmolStr, + pub name: Symbol, pub def: ModuleDef, pub loc: DeclarationLocation, pub container_name: Option, @@ -37,7 +44,7 @@ pub struct DeclarationLocation { /// This points to the whole syntax node of the declaration. pub ptr: SyntaxNodePtr, /// This points to the [`syntax::ast::Name`] identifier of the declaration. - pub name_ptr: AstPtr, + pub name_ptr: AstPtr>, } impl DeclarationLocation { @@ -55,7 +62,7 @@ struct SymbolCollectorWork { pub struct SymbolCollector<'a> { db: &'a dyn HirDatabase, - symbols: Vec, + symbols: FxIndexSet, work: Vec, current_container_name: Option, edition: Edition, @@ -86,11 +93,11 @@ impl<'a> SymbolCollector<'a> { } } - pub fn finish(self) -> Vec { - self.symbols + pub fn finish(self) -> Box<[FileSymbol]> { + self.symbols.into_iter().collect() } - pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec { + pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { let mut symbol_collector = SymbolCollector::new(db); symbol_collector.collect(module); symbol_collector.finish() @@ -104,96 +111,174 @@ impl<'a> SymbolCollector<'a> { } fn collect_from_module(&mut self, module_id: ModuleId) { - let def_map = module_id.def_map(self.db.upcast()); - let scope = &def_map[module_id.local_id].scope; - - for module_def_id in scope.declarations() { - match module_def_id { - ModuleDefId::ModuleId(id) => self.push_module(id), + let push_decl = |this: &mut Self, def, name| { + match def { + ModuleDefId::ModuleId(id) => this.push_module(id, name), ModuleDefId::FunctionId(id) => { - self.push_decl(id, false); - self.collect_from_body(id); + this.push_decl(id, name, false); + this.collect_from_body(id); } - ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id, false), - ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, false), - ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, false), + ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false), + ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false), + ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false), ModuleDefId::ConstId(id) => { - self.push_decl(id, false); - self.collect_from_body(id); + this.push_decl(id, name, false); + this.collect_from_body(id); } ModuleDefId::StaticId(id) => { - self.push_decl(id, false); - self.collect_from_body(id); + this.push_decl(id, name, false); + this.collect_from_body(id); } ModuleDefId::TraitId(id) => { - self.push_decl(id, false); - self.collect_from_trait(id); + this.push_decl(id, name, false); + this.collect_from_trait(id); } ModuleDefId::TraitAliasId(id) => { - self.push_decl(id, false); + this.push_decl(id, name, false); } ModuleDefId::TypeAliasId(id) => { - self.push_decl(id, false); + this.push_decl(id, name, false); } ModuleDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => self.push_decl(id, false), - MacroId::MacroRulesId(id) => self.push_decl(id, false), - MacroId::ProcMacroId(id) => self.push_decl(id, false), + MacroId::Macro2Id(id) => this.push_decl(id, name, false), + MacroId::MacroRulesId(id) => this.push_decl(id, name, false), + MacroId::ProcMacroId(id) => this.push_decl(id, name, false), }, // Don't index these. ModuleDefId::BuiltinType(_) => {} ModuleDefId::EnumVariantId(_) => {} } - } + }; - for impl_id in scope.impls() { - self.collect_from_impl(impl_id); - } + // Nested trees are very common, so a cache here will hit a lot. + let import_child_source_cache = &mut FxHashMap::default(); + + let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { + let source = import_child_source_cache + .entry(i.import) + .or_insert_with(|| i.import.child_source(this.db.upcast())); + let Some(use_tree_src) = source.value.get(i.idx) else { return }; + let Some(name_ptr) = use_tree_src + .rename() + .and_then(|rename| rename.name()) + .map(Either::Left) + .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right)) + .map(|it| AstPtr::new(&it)) + else { + return; + }; + let dec_loc = DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(use_tree_src.syntax()), + name_ptr, + }; + this.symbols.insert(FileSymbol { + name: name.symbol().clone(), + def: def.into(), + container_name: this.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + is_assoc: false, + }); + }; - // Record renamed imports. - // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily - // for now. - for id in scope.imports() { - let source = id.import.child_source(self.db.upcast()); - let Some(use_tree_src) = source.value.get(id.idx) else { continue }; - let Some(rename) = use_tree_src.rename() else { continue }; - let Some(name) = rename.name() else { continue }; - - let res = scope.fully_resolve_import(self.db.upcast(), id); - res.iter_items().for_each(|(item, _)| { - let def = match item { - ItemInNs::Types(def) | ItemInNs::Values(def) => def, - ItemInNs::Macros(def) => ModuleDefId::from(def), - } - .into(); + let push_extern_crate = + |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| { + let loc = i.lookup(this.db.upcast()); + let source = loc.source(this.db.upcast()); + let Some(name_ptr) = source + .value + .rename() + .and_then(|rename| rename.name()) + .map(Either::Left) + .or_else(|| source.value.name_ref().map(Either::Right)) + .map(|it| AstPtr::new(&it)) + else { + return; + }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr: AstPtr::new(&name), + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr, }; - - self.symbols.push(FileSymbol { - name: name.text().into(), - def, - container_name: self.current_container_name.clone(), + this.symbols.insert(FileSymbol { + name: name.symbol().clone(), + def: def.into(), + container_name: this.current_container_name.clone(), loc: dec_loc, is_alias: false, is_assoc: false, }); - }); + }; + + let is_explicit_import = |vis| { + match vis { + Visibility::Module(_, VisibilityExplicitness::Explicit) => true, + Visibility::Module(_, VisibilityExplicitness::Implicit) => { + // consider imports in the crate root explicit, as these are visibly + // crate-wide anyways + module_id.is_crate_root() + } + Visibility::Public => true, + } + }; + + let def_map = module_id.def_map(self.db.upcast()); + let scope = &def_map[module_id.local_id].scope; + + for impl_id in scope.impls() { + self.collect_from_impl(impl_id); + } + + for (name, Item { def, vis, import }) in scope.types() { + if let Some(i) = import { + if is_explicit_import(vis) { + match i { + ImportOrExternCrate::Import(i) => push_import(self, i, name, def), + ImportOrExternCrate::ExternCrate(i) => { + push_extern_crate(self, i, name, def) + } + } + } + continue; + } + // self is a declaration + push_decl(self, def, name) + } + + for (name, Item { def, vis, import }) in scope.macros() { + if let Some(i) = import { + if is_explicit_import(vis) { + push_import(self, i, name, def.into()); + } + continue; + } + // self is a declaration + push_decl(self, def.into(), name) + } + + for (name, Item { def, vis, import }) in scope.values() { + if let Some(i) = import { + if is_explicit_import(vis) { + push_import(self, i, name, def); + } + continue; + } + // self is a declaration + push_decl(self, def, name) } for const_id in scope.unnamed_consts() { self.collect_from_body(const_id); } - for (_, id) in scope.legacy_macros() { + for (name, id) in scope.legacy_macros() { for &id in id { if id.module(self.db.upcast()) == module_id { match id { - MacroId::Macro2Id(id) => self.push_decl(id, false), - MacroId::MacroRulesId(id) => self.push_decl(id, false), - MacroId::ProcMacroId(id) => self.push_decl(id, false), + MacroId::Macro2Id(id) => self.push_decl(id, name, false), + MacroId::MacroRulesId(id) => self.push_decl(id, name, false), + MacroId::ProcMacroId(id) => self.push_decl(id, name, false), } } } @@ -223,8 +308,8 @@ impl<'a> SymbolCollector<'a> { .to_smolstr(), ); self.with_container_name(impl_name, |s| { - for &assoc_item_id in impl_data.items.iter() { - s.push_assoc_item(assoc_item_id) + for &(ref name, assoc_item_id) in &impl_data.items { + s.push_assoc_item(assoc_item_id, name) } }) } @@ -232,8 +317,8 @@ impl<'a> SymbolCollector<'a> { fn collect_from_trait(&mut self, trait_id: TraitId) { let trait_data = self.db.trait_data(trait_id); self.with_container_name(Some(trait_data.name.as_str().into()), |s| { - for &(_, assoc_item_id) in &trait_data.items { - s.push_assoc_item(assoc_item_id); + for &(ref name, assoc_item_id) in &trait_data.items { + s.push_assoc_item(assoc_item_id, name); } }); } @@ -266,15 +351,15 @@ impl<'a> SymbolCollector<'a> { } } - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) { + fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) { match assoc_item_id { - AssocItemId::FunctionId(id) => self.push_decl(id, true), - AssocItemId::ConstId(id) => self.push_decl(id, true), - AssocItemId::TypeAliasId(id) => self.push_decl(id, true), + AssocItemId::FunctionId(id) => self.push_decl(id, name, true), + AssocItemId::ConstId(id) => self.push_decl(id, name, true), + AssocItemId::TypeAliasId(id) => self.push_decl(id, name, true), } } - fn push_decl<'db, L>(&mut self, id: L, is_assoc: bool) + fn push_decl<'db, L>(&mut self, id: L, name: &Name, is_assoc: bool) where L: Lookup = dyn DefDatabase + 'db> + Into, ::Data: HasSource, @@ -287,13 +372,13 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_node), + name_ptr: AstPtr::new(&name_node).wrap_left(), }; if let Some(attrs) = def.attrs(self.db) { for alias in attrs.doc_aliases() { - self.symbols.push(FileSymbol { - name: alias.as_str().into(), + self.symbols.insert(FileSymbol { + name: alias.clone(), def, loc: dec_loc.clone(), container_name: self.current_container_name.clone(), @@ -303,8 +388,8 @@ impl<'a> SymbolCollector<'a> { } } - self.symbols.push(FileSymbol { - name: name_node.text().into(), + self.symbols.insert(FileSymbol { + name: name.symbol().clone(), def, container_name: self.current_container_name.clone(), loc: dec_loc, @@ -313,7 +398,7 @@ impl<'a> SymbolCollector<'a> { }); } - fn push_module(&mut self, module_id: ModuleId) { + fn push_module(&mut self, module_id: ModuleId, name: &Name) { let def_map = module_id.def_map(self.db.upcast()); let module_data = &def_map[module_id.local_id]; let Some(declaration) = module_data.origin.declaration() else { return }; @@ -322,15 +407,15 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: declaration.file_id, ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: AstPtr::new(&name_node), + name_ptr: AstPtr::new(&name_node).wrap_left(), }; let def = ModuleDef::Module(module_id.into()); if let Some(attrs) = def.attrs(self.db) { for alias in attrs.doc_aliases() { - self.symbols.push(FileSymbol { - name: alias.as_str().into(), + self.symbols.insert(FileSymbol { + name: alias.clone(), def, loc: dec_loc.clone(), container_name: self.current_container_name.clone(), @@ -340,8 +425,8 @@ impl<'a> SymbolCollector<'a> { } } - self.symbols.push(FileSymbol { - name: name_node.text().into(), + self.symbols.insert(FileSymbol { + name: name.symbol().clone(), def: ModuleDef::Module(module_id.into()), container_name: self.current_container_name.clone(), loc: dec_loc, diff --git a/crates/ide-assists/src/assist_context.rs b/crates/ide-assists/src/assist_context.rs index 074d943719f1..64e77b2d6982 100644 --- a/crates/ide-assists/src/assist_context.rs +++ b/crates/ide-assists/src/assist_context.rs @@ -109,6 +109,10 @@ impl<'a> AssistContext<'a> { self.trimmed_range } + pub(crate) fn source_file(&self) -> &SourceFile { + &self.source_file + } + pub(crate) fn token_at_offset(&self) -> TokenAtOffset { self.source_file.syntax().token_at_offset(self.offset()) } diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs index a5c5b08d5b0c..eb784cd1226f 100644 --- a/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> }; // Verify this is `bool::then` that is being called. let func = ctx.sema.resolve_method_call(&mcall)?; - if !func.name(ctx.sema.db).eq_ident("then") { + if func.name(ctx.sema.db) != sym::then { return None; } let assoc = func.as_assoc_item(ctx.sema.db)?; diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index bb04a43cf961..d34cf895cd90 100644 --- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -343,11 +343,9 @@ fn compute_closure_type_params( let mut mentioned_names = mentioned_generic_params .iter() .filter_map(|param| match param { - hir::GenericParam::TypeParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) - } + hir::GenericParam::TypeParam(param) => Some(param.name(ctx.db()).as_str().to_smolstr()), hir::GenericParam::ConstParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) + Some(param.name(ctx.db()).as_str().to_smolstr()) } hir::GenericParam::LifetimeParam(_) => None, }) @@ -390,7 +388,7 @@ fn compute_closure_type_params( let has_name = syntax .descendants() .filter_map(ast::NameOrNameRef::cast) - .any(|name| mentioned_names.contains(&*name.text())); + .any(|name| mentioned_names.contains(name.text().trim_start_matches("r#"))); let mut has_new_params = false; if has_name { syntax diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 615b5d3f98b5..d4f2ea3bd941 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -170,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va ), _ => false, }) - .any(|(name, _)| name.eq_ident(variant_name.text().as_str())) + .any(|(name, _)| name.as_str() == variant_name.text().trim_start_matches("r#")) } fn extract_generic_params( diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index 0cc807aff642..97321f4ec1ef 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -4,6 +4,7 @@ use ide_db::{ syntax_helpers::{suggest_name, LexedStr}, }; use syntax::{ + algo::ancestors_at_offset, ast::{ self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory, AstNode, @@ -68,7 +69,10 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let node = if ctx.has_empty_selection() { if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) { t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone() - } else if let Some(expr) = ctx.find_node_at_offset::() { + } else if let Some(expr) = ancestors_at_offset(ctx.source_file().syntax(), ctx.offset()) + .next() + .and_then(ast::Expr::cast) + { expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone() } else { return None; @@ -469,11 +473,11 @@ mod tests { extract_variable, r#" fn main() -> i32 { - if true { + if$0 true { 1 } else { 2 - }$0 + } } "#, r#" @@ -581,11 +585,11 @@ fn main() { extract_variable, r#" fn main() -> i32 { - if true { + if$0 true { 1 } else { 2 - }$0 + } } "#, r#" @@ -676,11 +680,11 @@ fn main() { extract_variable, r#" fn main() -> i32 { - if true { + if$0 true { 1 } else { 2 - }$0 + } } "#, r#" diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index 7a92d8911bf8..47e4a68293f0 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let (_, def) = module .scope(ctx.db(), None) .into_iter() - .find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?; + .find(|(name, _)| name.as_str() == name_ref.text().trim_start_matches("r#"))?; let ScopeDef::ModuleDef(def) = def else { return None; }; diff --git a/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/crates/ide-assists/src/handlers/move_from_mod_rs.rs index 8a7a06b380f5..10915f8aafb8 100644 --- a/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/crates/ide-assists/src/handlers/move_module_to_file.rs b/crates/ide-assists/src/handlers/move_module_to_file.rs index 9692b7059291..bbf18e21948e 100644 --- a/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> .string_value_unescape() .is_none() => { - format_to!(buf, "{}/", name.unescaped().display(db)) + format_to!(buf, "{}/", name.as_str()) } _ => (), } diff --git a/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 2925e2334b44..7b38c795dc80 100644 --- a/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index 849b8a42c694..2a8465f634cf 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -208,7 +208,7 @@ fn find_trait_method( if let Some(hir::AssocItem::Function(method)) = trait_.items(db).into_iter().find(|item: &hir::AssocItem| { item.name(db) - .map(|name| name.eq_ident(trait_method_name.text().as_str())) + .map(|name| name.as_str() == trait_method_name.text().trim_start_matches("r#")) .unwrap_or(false) }) { diff --git a/crates/ide-assists/src/handlers/reorder_fields.rs b/crates/ide-assists/src/handlers/reorder_fields.rs index 972303c2a041..a79a82be4507 100644 --- a/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/crates/ide-assists/src/handlers/reorder_fields.rs @@ -110,7 +110,7 @@ fn compute_fields_ranks( .fields(ctx.db()) .into_iter() .enumerate() - .map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, field)| (field.name(ctx.db()).as_str().to_owned(), idx)) .collect(); Some(res) diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs index eb1d538f8743..c3404173eafe 100644 --- a/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -122,7 +122,7 @@ fn compute_item_ranks( .iter() .flat_map(|i| i.name(ctx.db())) .enumerate() - .map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, name)| (name.as_str().to_owned(), idx)) .collect(), ) } diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 3b2b2fd706e1..435b88de4ae6 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -5,7 +5,7 @@ use ide_db::imports::{ insert_use::ImportScope, }; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T}; +use syntax::{ast, AstNode, SyntaxNode}; use crate::{ config::AutoImportExclusionType, @@ -403,10 +403,11 @@ fn import_on_the_fly_method( fn import_name(ctx: &CompletionContext<'_>) -> String { let token_kind = ctx.token.kind(); - if matches!(token_kind, T![.] | T![::]) { - String::new() - } else { + + if token_kind.is_any_identifier() { ctx.token.to_string() + } else { + String::new() } } @@ -443,7 +444,7 @@ fn compute_fuzzy_completion_order_key( cov_mark::hit!(certain_fuzzy_order_test); let import_name = match proposed_mod_path.segments().last() { // FIXME: nasty alloc, this is a hot path! - Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(), + Some(name) => name.as_str().to_ascii_lowercase(), None => return usize::MAX, }; match import_name.match_indices(user_input_lowercased).next() { diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 6d1945c45341..5f0288ae9509 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -85,7 +85,7 @@ fn complete_trait_impl_name( name: &Option, kind: ImplCompletionKind, ) -> Option<()> { - let item = match name { + let macro_file_item = match name { Some(name) => name.syntax().parent(), None => { let token = &ctx.token; @@ -96,12 +96,12 @@ fn complete_trait_impl_name( .parent() } }?; - let item = ctx.sema.original_syntax_node_rooted(&item)?; + let real_file_item = ctx.sema.original_syntax_node_rooted(¯o_file_item)?; // item -> ASSOC_ITEM_LIST -> IMPL - let impl_def = ast::Impl::cast(item.parent()?.parent()?)?; + let impl_def = ast::Impl::cast(macro_file_item.parent()?.parent()?)?; let replacement_range = { // ctx.sema.original_ast_node(item)?; - let first_child = item + let first_child = real_file_item .children_with_tokens() .find(|child| { !matches!( @@ -109,7 +109,7 @@ fn complete_trait_impl_name( SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR ) }) - .unwrap_or_else(|| SyntaxElement::Node(item.clone())); + .unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone())); TextRange::new(first_child.text_range().start(), ctx.source_range().end()) }; @@ -133,8 +133,11 @@ pub(crate) fn complete_trait_impl_item_by_name( acc, ctx, ImplCompletionKind::All, - match name_ref { - Some(name) => name.syntax().text_range(), + match name_ref + .as_ref() + .and_then(|name| ctx.sema.original_syntax_node_rooted(name.syntax())) + { + Some(name) => name.text_range(), None => ctx.source_range(), }, impl_, @@ -359,7 +362,7 @@ fn add_type_alias_impl( type_alias: hir::TypeAlias, impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db).unescaped().display(ctx.db).to_smolstr(); + let alias_name = type_alias.name(ctx.db).as_str().to_smolstr(); let label = format_smolstr!("type {alias_name} ="); @@ -516,7 +519,7 @@ fn function_declaration( mod tests { use expect_test::expect; - use crate::tests::{check_edit, check_no_kw}; + use crate::tests::{check, check_edit, check_no_kw}; #[test] fn no_completion_inside_fn() { @@ -1639,4 +1642,51 @@ impl DesugaredAsyncTrait for () { "#, ); } + + #[test] + fn within_attr_macro() { + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + f$0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + md proc_macros + kw crate:: + kw self:: + "#]], + ); + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + fn $0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + "#]], + ); + } } diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index bafe32942098..cca6a22f290d 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -7,7 +7,7 @@ use ide_db::{ base_db::{SourceRootDatabase, VfsPath}, FxHashSet, RootDatabase, SymbolKind, }; -use syntax::{ast, AstNode, SyntaxKind, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxKind}; use crate::{context::CompletionContext, CompletionItem, Completions}; @@ -140,9 +140,7 @@ fn directory_to_look_for_submodules( module_chain_to_containing_module_file(module, db) .into_iter() .filter_map(|module| module.name(db)) - .try_fold(base_directory, |path, name| { - path.join(&name.unescaped().display_no_db().to_smolstr()) - }) + .try_fold(base_directory, |path, name| path.join(name.as_str())) } fn module_chain_to_containing_module_file( diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 3c4d489c0ff8..f5a50ae81907 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -5,7 +5,7 @@ use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use itertools::Either; use syntax::{ - algo::{ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, + algo::{self, ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, ast::{ self, AttrKind, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, NameOrNameRef, @@ -85,6 +85,11 @@ pub(super) fn expand_and_analyze( }) } +fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option { + let token = file.token_at_offset(offset).left_biased()?; + algo::skip_whitespace_token(token, Direction::Prev) +} + /// Expand attributes and macro calls at the current cursor position for both the original file /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original /// and speculative states stay in sync. @@ -125,9 +130,7 @@ fn expand( // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) - && original_file - .token_at_offset(original_offset + relative_offset) - .left_biased() + && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. @@ -143,9 +146,11 @@ fn expand( let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); + let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset) + .and_then(|token| token.parent_ancestors().find_map(ast::Item::cast)); let ancestor_items = iter::successors( Option::zip( - find_node_at_offset::(&original_file, original_offset), + original_node, find_node_at_offset::( &speculative_file, fake_ident_token.text_range().start(), @@ -1590,11 +1595,11 @@ fn pattern_context_for( }).map(|enum_| enum_.variants(sema.db)) }) }).map(|variants| variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).unescaped().display(sema.db).to_string(); + let variant_name = variant.name(sema.db); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { - let pat_already_present = pat.syntax().to_string().contains(&variant_name); + let pat_already_present = pat.syntax().to_string().contains(variant_name.as_str()); pat_already_present.then_some(pat_already_present) }).is_some() }); diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index dc2f9a768029..b0a096b64af2 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -181,6 +181,8 @@ pub struct CompletionRelevance { pub postfix_match: Option, /// This is set for items that are function (associated or method) pub function: Option, + /// true when there is an `await.method()` or `iter().method()` completion. + pub is_skipping_completion: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -269,6 +271,7 @@ impl CompletionRelevance { postfix_match, trait_, function, + is_skipping_completion, } = self; // only applicable for completions within use items @@ -296,6 +299,12 @@ impl CompletionRelevance { score -= 5; } } + + // Lower rank for completions that skip `await` and `iter()`. + if is_skipping_completion { + score -= 7; + } + // lower rank for items that need an import if requires_import { score -= 1; diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 7fee2240908d..1b7adf1adb06 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -130,10 +130,8 @@ pub(crate) fn render_field( let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); let name = field.name(db); - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), @@ -142,7 +140,8 @@ pub(crate) fn render_field( ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), - exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), + exact_name_match: compute_exact_name_match(ctx.completion, &name), + is_skipping_completion: receiver.is_some(), ..CompletionRelevance::default() }); item.detail(ty.display(db, ctx.completion.edition).to_string()) @@ -215,6 +214,10 @@ pub(crate) fn render_tuple_field( ); item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string()) .lookup_by(field.to_string()); + item.set_relevance(CompletionRelevance { + is_skipping_completion: receiver.is_some(), + ..ctx.completion_relevance() + }); item.build(ctx.db()) } @@ -423,7 +426,7 @@ fn render_resolution_path( let name = local_name.display_no_db(ctx.completion.edition).to_smolstr(); let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); - if local_name.is_escaped(completion.edition) { + if local_name.needs_escape(completion.edition) { item.insert_text(local_name.display_no_db(completion.edition).to_smolstr()); } // Add `<>` for generic types @@ -512,7 +515,7 @@ fn render_resolution_simple_( let mut item = CompletionItem::new( kind, ctx.source_range(), - local_name.unescaped().display(db).to_smolstr(), + local_name.as_str().to_smolstr(), ctx.completion.edition, ); item.set_relevance(ctx.completion_relevance()) @@ -1335,6 +1338,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1364,6 +1368,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1453,6 +1458,7 @@ fn foo() { A { the$0 } } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -1511,6 +1517,7 @@ impl S { return_type: Other, }, ), + is_skipping_completion: false, }, }, CompletionItem { @@ -1653,6 +1660,7 @@ fn foo(s: S) { s.$0 } return_type: Other, }, ), + is_skipping_completion: false, }, }, ] @@ -1864,6 +1872,7 @@ fn f() -> i32 { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2624,6 +2633,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@107", }, @@ -2709,6 +2719,7 @@ fn foo() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2766,6 +2777,7 @@ fn main() { return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@92", }, @@ -3140,6 +3152,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, CompletionItem { @@ -3173,6 +3186,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs index 415d87c6239b..e357ab24d22d 100644 --- a/crates/ide-completion/src/render/const_.rs +++ b/crates/ide-completion/src/render/const_.rs @@ -14,10 +14,8 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display(db, ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr()); let detail = const_.display(db, ctx.completion.edition).to_string(); let mut item = diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 3b97d67169ec..c3354902c3b7 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -59,13 +59,10 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())), + format_smolstr!("{}.{}", receiver, name.as_str()), format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)), ), - _ => ( - name.unescaped().display(db).to_smolstr(), - name.display(db, completion.edition).to_smolstr(), - ), + _ => (name.as_str().to_smolstr(), name.display(db, completion.edition).to_smolstr()), }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( @@ -126,6 +123,7 @@ fn render( exact_name_match: compute_exact_name_match(completion, &call), function, trait_: trait_info, + is_skipping_completion: matches!(func_kind, FuncKind::Method(_, Some(_))), ..ctx.completion_relevance() }); @@ -151,7 +149,7 @@ fn render( item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) .detail(detail) - .lookup_by(name.unescaped().display(db).to_smolstr()); + .lookup_by(name.as_str().to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { add_call_parens( diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index c71356e5300f..aab54ca5e014 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -75,7 +75,7 @@ fn render( None => (name.clone().into(), name.into(), false), }; let (qualified_name, escaped_qualified_name) = ( - qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display_verbatim(ctx.db()).to_string(), qualified_name.display(ctx.db(), completion.edition).to_string(), ); let snippet_cap = ctx.snippet_cap(); diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index 6490171fbb48..e265e92f9794 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -46,21 +46,19 @@ fn render( ctx.source_range() }; - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), completion.edition).to_smolstr()); let docs = ctx.docs(macro_); let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); let is_fn_like = macro_.is_fn_like(completion.db); - let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") }; + let (bra, ket) = if is_fn_like { guess_macro_braces(name, docs_str) } else { ("", "") }; let needs_bang = is_fn_like && !is_use_path && !has_macro_bang; let mut item = CompletionItem::new( SymbolKind::from(macro_.kind(completion.db)), source_range, - label(&ctx, needs_bang, bra, ket, &name), + label(&ctx, needs_bang, bra, ket, &name.to_smolstr()), completion.edition, ); item.set_deprecated(ctx.is_deprecated(macro_)) @@ -71,11 +69,11 @@ fn render( match ctx.snippet_cap() { Some(cap) if needs_bang && !has_call_parens => { let snippet = format!("{escaped_name}!{bra}$0{ket}"); - let lookup = banged_name(&name); + let lookup = banged_name(name); item.insert_snippet(cap, snippet).lookup_by(lookup); } _ if needs_bang => { - item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name)); + item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(name)); } _ => { cov_mark::hit!(dont_insert_macro_call_parens_unnecessary); diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 5675dfb5c6ff..124abb17b6a1 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -31,13 +31,11 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())); - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), ctx.completion.edition).to_smolstr()); let kind = strukt.kind(ctx.db()); - let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap()); - let lookup = format_literal_lookup(name.as_str(), kind); + let label = format_literal_label(name, kind, ctx.snippet_cap()); + let lookup = format_literal_lookup(name, kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; let db = ctx.db(); @@ -61,13 +59,13 @@ pub(crate) fn render_variant_pat( let (name, escaped_name) = match path { Some(path) => ( - path.unescaped().display(ctx.db()).to_string().into(), - path.display(ctx.db(), ctx.completion.edition).to_string().into(), + path.display_verbatim(ctx.db()).to_smolstr(), + path.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); let it = ( - name.unescaped().display(ctx.db()).to_smolstr(), + name.as_str().to_smolstr(), name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ); it diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs index 09eb19201c5b..1b952f31360c 100644 --- a/crates/ide-completion/src/render/type_alias.rs +++ b/crates/ide-completion/src/render/type_alias.rs @@ -32,14 +32,11 @@ fn render( let name = type_alias.name(db); let (name, escaped_name) = if with_eq { ( - SmolStr::from_iter([&name.unescaped().display(db).to_smolstr(), " = "]), + SmolStr::from_iter([&name.as_str().to_smolstr(), " = "]), SmolStr::from_iter([&name.display_no_db(ctx.completion.edition).to_smolstr(), " = "]), ) } else { - ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ) + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()) }; let detail = type_alias.display(db, ctx.completion.edition).to_string(); diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index e053e299d903..742036265211 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs @@ -23,12 +23,12 @@ pub(crate) fn render_union_literal( let (qualified_name, escaped_qualified_name) = match path { Some(p) => ( - p.unescaped().display(ctx.db()).to_string(), - p.display(ctx.db(), ctx.completion.edition).to_string(), + p.display_verbatim(ctx.db()).to_smolstr(), + p.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => ( - name.unescaped().display(ctx.db()).to_string(), - name.display(ctx.db(), ctx.completion.edition).to_string(), + name.as_str().to_smolstr(), + name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), }; let label = format_literal_label( diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 0adf1b825f7b..d491e438feff 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -1746,7 +1746,7 @@ fn intrinsics() { fn function() { transmute$0 } - "#, +"#, expect![[r#" fn transmute(…) (use core::mem::transmute) unsafe fn(Src) -> Dst "#]], @@ -1767,7 +1767,9 @@ fn function() { mem::transmute$0 } "#, - expect![""], + expect![[r#" + fn transmute(…) (use core::mem) unsafe fn(Src) -> Dst + "#]], ); } diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 49d26dfe25c1..d12bda0816fd 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -794,7 +794,7 @@ impl NameRefClass { hir::AssocItem::TypeAlias(it) => Some(it), _ => None, }) - .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str())) + .find(|alias| alias.name(sema.db).as_str() == name_ref.text().trim_start_matches("r#")) { // No substitution, this can only occur in type position. return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); diff --git a/crates/ide-db/src/famous_defs.rs b/crates/ide-db/src/famous_defs.rs index 9e3506d6f53b..2f4d07446f2c 100644 --- a/crates/ide-db/src/famous_defs.rs +++ b/crates/ide-db/src/famous_defs.rs @@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> { self.find_trait("core:cmp:Ord") } + pub fn core_convert_FromStr(&self) -> Option { + self.find_trait("core:str:FromStr") + } + pub fn core_convert_From(&self) -> Option { self.find_trait("core:convert:From") } @@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:Into") } + pub fn core_convert_TryFrom(&self) -> Option { + self.find_trait("core:convert:TryFrom") + } + + pub fn core_convert_TryInto(&self) -> Option { + self.find_trait("core:convert:TryInto") + } + pub fn core_convert_Index(&self) -> Option { self.find_trait("core:ops:Index") } @@ -130,6 +142,13 @@ impl FamousDefs<'_, '_> { self.find_macro("core:unimplemented") } + pub fn core_fmt_Display(&self) -> Option { + self.find_trait("core:fmt:Display") + } + + pub fn alloc_string_ToString(&self) -> Option { + self.find_trait("alloc:string:ToString") + } pub fn builtin_crates(&self) -> impl Iterator { IntoIterator::into_iter([ self.std(), @@ -202,14 +221,15 @@ impl FamousDefs<'_, '_> { for segment in path { module = module.children(db).find_map(|child| { let name = child.name(db)?; - if name.eq_ident(segment) { + if name.as_str() == segment { Some(child) } else { None } })?; } - let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1; + let def = + module.scope(db, None).into_iter().find(|(name, _def)| name.as_str() == trait_)?.1; Some(def) } } diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 8f0be1d90354..ad86d855b553 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -1,15 +1,17 @@ //! Look up accessible paths for items. +use std::ops::ControlFlow; + use hir::{ db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig, - ItemInNs, ModPath, Module, ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, + ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Trait, TyFingerprint, Type, }; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ ast::{self, make, HasName}, - AstNode, SmolStr, SyntaxNode, + AstNode, SyntaxNode, }; use crate::{ @@ -51,7 +53,7 @@ pub struct TraitImportCandidate { #[derive(Debug)] pub struct PathImportCandidate { /// Optional qualifier before name. - pub qualifier: Vec, + pub qualifier: Vec, /// The name the item (struct, trait, enum, etc.) should have. pub name: NameToImport, } @@ -70,10 +72,18 @@ pub enum NameToImport { impl NameToImport { pub fn exact_case_sensitive(s: String) -> NameToImport { + let s = match s.strip_prefix("r#") { + Some(s) => s.to_owned(), + None => s, + }; NameToImport::Exact(s, true) } pub fn fuzzy(s: String) -> NameToImport { + let s = match s.strip_prefix("r#") { + Some(s) => s.to_owned(), + None => s, + }; // unless all chars are lowercase, we do a case sensitive search let case_sensitive = s.chars().any(|c| c.is_uppercase()); NameToImport::Fuzzy(s, case_sensitive) @@ -350,21 +360,27 @@ fn path_applicable_imports( .take(DEFAULT_QUERY_SEARCH_LIMIT.inner()) .collect() } + // we have some unresolved qualifier that we search an import for + // The key here is that whatever we import must form a resolved path for the remainder of + // what follows + // FIXME: This doesn't handle visibility [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name( sema, current_crate, - NameToImport::Exact(first_qsegment.to_string(), true), + NameToImport::Exact(first_qsegment.as_str().to_owned(), true), AssocSearchMode::Exclude, ) .filter_map(|item| { - import_for_item( + // we found imports for `first_qsegment`, now we need to filter these imports by whether + // they result in resolving the rest of the path successfully + validate_resolvable( sema, scope, mod_path, + scope_filter, &path_candidate.name, item, qualifier_rest, - scope_filter, ) }) .take(DEFAULT_QUERY_SEARCH_LIMIT.inner()) @@ -372,14 +388,16 @@ fn path_applicable_imports( } } -fn import_for_item( +/// Validates and builds an import for `resolved_qualifier` if the `unresolved_qualifier` appended +/// to it resolves and there is a validate `candidate` after that. +fn validate_resolvable( sema: &Semantics<'_, RootDatabase>, scope: &SemanticsScope<'_>, mod_path: impl Fn(ItemInNs) -> Option, + scope_filter: impl Fn(ItemInNs) -> bool, candidate: &NameToImport, resolved_qualifier: ItemInNs, - unresolved_qualifier: &[SmolStr], - scope_filter: impl Fn(ItemInNs) -> bool, + unresolved_qualifier: &[Name], ) -> Option { let _p = tracing::info_span!("ImportAssets::import_for_item").entered(); @@ -410,8 +428,11 @@ fn import_for_item( module, candidate.clone(), AssocSearchMode::Exclude, + |it| match scope_filter(it) { + true => ControlFlow::Break(it), + false => ControlFlow::Continue(()), + }, ) - .find(|&it| scope_filter(it)) .map(|item| LocatedImport::new(import_path_candidate, resolved_qualifier, item)) } // FIXME @@ -709,7 +730,7 @@ fn path_import_candidate( if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) { let qualifier = qualifier .segments() - .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) + .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) .collect::>>()?; ImportCandidate::Path(PathImportCandidate { qualifier, name }) } else { diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 7f66ea0c103f..a2062f36d3fd 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -2,6 +2,8 @@ //! by its name and a few criteria. //! The main reason for this module to exist is the fact that project's items and dependencies' items //! are located in different caches, with different APIs. +use std::ops::ControlFlow; + use either::Either; use hir::{import_map, Crate, ItemInNs, Module, Semantics}; use limit::Limit; @@ -17,6 +19,7 @@ pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100); pub use import_map::AssocSearchMode; +// FIXME: Do callbacks instead to avoid allocations. /// Searches for importable items with the given name in the crate and its dependencies. pub fn items_with_name<'a>( sema: &'a Semantics<'_, RootDatabase>, @@ -70,12 +73,13 @@ pub fn items_with_name<'a>( } /// Searches for importable items with the given name in the crate and its dependencies. -pub fn items_with_name_in_module<'a>( - sema: &'a Semantics<'_, RootDatabase>, +pub fn items_with_name_in_module( + sema: &Semantics<'_, RootDatabase>, module: Module, name: NameToImport, assoc_item_search: AssocSearchMode, -) -> impl Iterator + 'a { + mut cb: impl FnMut(ItemInNs) -> ControlFlow, +) -> Option { let _p = tracing::info_span!("items_with_name_in", name = name.text(), assoc_item_search = ?assoc_item_search, ?module) .entered(); @@ -107,14 +111,12 @@ pub fn items_with_name_in_module<'a>( local_query } }; - let mut local_results = Vec::new(); local_query.search(&[sema.db.module_symbols(module)], |local_candidate| { - local_results.push(match local_candidate.def { + cb(match local_candidate.def { hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), def => ItemInNs::from(def), }) - }); - local_results.into_iter() + }) } fn find_items<'a>( @@ -142,7 +144,8 @@ fn find_items<'a>( local_results.push(match local_candidate.def { hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), def => ItemInNs::from(def), - }) + }); + ControlFlow::<()>::Continue(()) }); local_results.into_iter().chain(external_importables) } diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 42efbd68e33d..59914bedde43 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -263,13 +263,12 @@ fn rename_mod( // - Module has submodules defined in separate files let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) { // Go up one level since the anchor is inside the dir we're trying to rename - (true, _, Some(mod_name)) => Some(( - format!("../{}", mod_name.unescaped().display(sema.db)), - format!("../{new_name}"), - )), + (true, _, Some(mod_name)) => { + Some((format!("../{}", mod_name.as_str()), format!("../{new_name}"))) + } // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) + Some((mod_name.as_str().to_owned(), new_name.to_owned())) } _ => None, }; diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index a75aba137be6..7fc563a42410 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -625,7 +625,7 @@ impl<'a> FindUsages<'a> { let _p = tracing::info_span!("collect_possible_aliases").entered(); let db = sema.db; - let container_name = container.name(db).unescaped().display(db).to_smolstr(); + let container_name = container.name(db).as_str().to_smolstr(); let search_scope = Definition::from(container).search_scope(db); let mut seen = FxHashSet::default(); let mut completed = FxHashSet::default(); @@ -925,12 +925,8 @@ impl<'a> FindUsages<'a> { .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) }) }; - // We need to unescape the name in case it is written without "r#" in earlier - // editions of Rust where it isn't a keyword. - self.def - .name(sema.db) - .or_else(self_kw_refs) - .map(|it| it.unescaped().display(sema.db).to_smolstr()) + // We need to search without the `r#`, hence `as_str` access. + self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.as_str().to_smolstr()) } }; let name = match &name { diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index 94d354d28e51..c94644eeb89b 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -25,6 +25,7 @@ use std::{ fmt, hash::{Hash, Hasher}, mem, + ops::ControlFlow, }; use base_db::{ @@ -136,16 +137,13 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar // the module or crate indices for those in salsa unless we need to. .for_each(|module| symbol_collector.collect(module)); - let mut symbols = symbol_collector.finish(); - symbols.shrink_to_fit(); - Arc::new(SymbolIndex::new(symbols)) + Arc::new(SymbolIndex::new(symbol_collector.finish())) } fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = tracing::info_span!("module_symbols").entered(); - let symbols = SymbolCollector::collect_module(db.upcast(), module); - Arc::new(SymbolIndex::new(symbols)) + Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { @@ -222,13 +220,16 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { }; let mut res = vec![]; - query.search(&indices, |f| res.push(f.clone())); + query.search::<()>(&indices, |f| { + res.push(f.clone()); + ControlFlow::Continue(()) + }); res } #[derive(Default)] pub struct SymbolIndex { - symbols: Vec, + symbols: Box<[FileSymbol]>, map: fst::Map>, } @@ -253,10 +254,10 @@ impl Hash for SymbolIndex { } impl SymbolIndex { - fn new(mut symbols: Vec) -> SymbolIndex { + fn new(mut symbols: Box<[FileSymbol]>) -> SymbolIndex { fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering { - let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase()); - let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase()); + let lhs_chars = lhs.name.as_str().chars().map(|c| c.to_ascii_lowercase()); + let rhs_chars = rhs.name.as_str().chars().map(|c| c.to_ascii_lowercase()); lhs_chars.cmp(rhs_chars) } @@ -316,11 +317,11 @@ impl SymbolIndex { } impl Query { - pub(crate) fn search<'sym>( + pub(crate) fn search<'sym, T>( self, indices: &'sym [Arc], - cb: impl FnMut(&'sym FileSymbol), - ) { + cb: impl FnMut(&'sym FileSymbol) -> ControlFlow, + ) -> Option { let _p = tracing::info_span!("symbol_index::Query::search").entered(); let mut op = fst::map::OpBuilder::new(); match self.mode { @@ -351,12 +352,12 @@ impl Query { } } - fn search_maps<'sym>( + fn search_maps<'sym, T>( &self, indices: &'sym [Arc], mut stream: fst::map::Union<'_>, - mut cb: impl FnMut(&'sym FileSymbol), - ) { + mut cb: impl FnMut(&'sym FileSymbol) -> ControlFlow, + ) -> Option { let ignore_underscore_prefixed = !self.query.starts_with("__"); while let Some((_, indexed_values)) = stream.next() { for &IndexedValue { index, value } in indexed_values { @@ -377,15 +378,19 @@ impl Query { continue; } // Hide symbols that start with `__` unless the query starts with `__` - if ignore_underscore_prefixed && symbol.name.starts_with("__") { + let symbol_name = symbol.name.as_str(); + if ignore_underscore_prefixed && symbol_name.starts_with("__") { continue; } - if self.mode.check(&self.query, self.case_sensitive, &symbol.name) { - cb(symbol); + if self.mode.check(&self.query, self.case_sensitive, symbol_name) { + if let Some(b) = cb(symbol).break_value() { + return Some(b); + } } } } } + None } fn matches_assoc_mode(&self, is_trait_assoc_item: bool) -> bool { @@ -476,9 +481,9 @@ use Macro as ItemLikeMacro; use Macro as Trait; // overlay namespaces //- /b_mod.rs struct StructInModB; -use super::Macro as SuperItemLikeMacro; -use crate::b_mod::StructInModB as ThisStruct; -use crate::Trait as IsThisJustATrait; +pub(self) use super::Macro as SuperItemLikeMacro; +pub(self) use crate::b_mod::StructInModB as ThisStruct; +pub(self) use crate::Trait as IsThisJustATrait; "#, ); @@ -487,7 +492,7 @@ use crate::Trait as IsThisJustATrait; .into_iter() .map(|module_id| { let mut symbols = SymbolCollector::collect_module(&db, module_id); - symbols.sort_by_key(|it| it.name.clone()); + symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) .collect(); @@ -514,7 +519,7 @@ struct Duplicate; .into_iter() .map(|module_id| { let mut symbols = SymbolCollector::collect_module(&db, module_id); - symbols.sort_by_key(|it| it.name.clone()); + symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) .collect(); diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 9d70942199c8..535777dfcbea 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -631,7 +631,7 @@ def: Function( Function { id: FunctionId( - 3, + 2, ), }, ), @@ -664,7 +664,7 @@ def: Function( Function { id: FunctionId( - 2, + 1, ), }, ), @@ -794,7 +794,7 @@ def: Function( Function { id: FunctionId( - 1, + 3, ), }, ), @@ -879,12 +879,10 @@ [ FileSymbol { name: "IsThisJustATrait", - def: Macro( - Macro { - id: Macro2Id( - Macro2Id( - 0, - ), + def: Trait( + Trait { + id: TraitId( + 0, ), }, ), @@ -897,12 +895,12 @@ ), ptr: SyntaxNodePtr { kind: USE_TREE, - range: 111..143, + range: 141..173, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 127..143, + range: 157..173, }, ), }, @@ -911,40 +909,7 @@ is_assoc: false, }, FileSymbol { - name: "StructInModB", - def: Adt( - Struct( - Struct { - id: StructId( - 4, - ), - }, - ), - ), - loc: DeclarationLocation { - hir_file_id: EditionedFileId( - FileId( - 1, - ), - Edition2021, - ), - ptr: SyntaxNodePtr { - kind: STRUCT, - range: 0..20, - }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 7..19, - }, - ), - }, - container_name: None, - is_alias: false, - is_assoc: false, - }, - FileSymbol { - name: "SuperItemLikeMacro", + name: "IsThisJustATrait", def: Macro( Macro { id: Macro2Id( @@ -963,12 +928,12 @@ ), ptr: SyntaxNodePtr { kind: USE_TREE, - range: 25..59, + range: 141..173, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 41..59, + range: 157..173, }, ), }, @@ -977,7 +942,7 @@ is_assoc: false, }, FileSymbol { - name: "ThisStruct", + name: "StructInModB", def: Adt( Struct( Struct { @@ -995,13 +960,13 @@ Edition2021, ), ptr: SyntaxNodePtr { - kind: USE_TREE, - range: 65..105, + kind: STRUCT, + range: 0..20, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 95..105, + range: 7..19, }, ), }, @@ -1010,15 +975,15 @@ is_assoc: false, }, FileSymbol { - name: "ThisStruct", - def: Adt( - Struct( - Struct { - id: StructId( - 4, + name: "SuperItemLikeMacro", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, ), - }, - ), + ), + }, ), loc: DeclarationLocation { hir_file_id: EditionedFileId( @@ -1029,12 +994,12 @@ ), ptr: SyntaxNodePtr { kind: USE_TREE, - range: 65..105, + range: 35..69, }, name_ptr: AstPtr( SyntaxNodePtr { kind: NAME, - range: 95..105, + range: 51..69, }, ), }, diff --git a/crates/ide-db/src/ty_filter.rs b/crates/ide-db/src/ty_filter.rs index 515bc418cb46..2fdd8358637d 100644 --- a/crates/ide-db/src/ty_filter.rs +++ b/crates/ide-db/src/ty_filter.rs @@ -26,7 +26,7 @@ impl TryEnum { _ => return None, }; TryEnum::ALL.iter().find_map(|&var| { - if enum_.name(sema.db).eq_ident(var.type_name()) { + if enum_.name(sema.db).as_str() == var.type_name() { return Some(var); } None diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index 2b59c1a22f6f..7d62daf716c4 100644 --- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -600,6 +600,25 @@ pub mod __private { //- /bar.rs crate:bar deps:foo edition:2018 fn bar() { _ = foo::__private::Result::<(), ()>::Ok; +} + "#, + ); + } + + #[test] + fn enum_variant_type_ns() { + check_diagnostics( + r#" +enum KvnDeserializerErr { + UnexpectedKeyword { found: I, expected: I }, +} + +fn foo() { + let _x: KvnDeserializerErr<()> = + KvnDeserializerErr::<()>::UnexpectedKeyword { found: (), expected: () }; + let _x: KvnDeserializerErr<()> = + KvnDeserializerErr::<()>::UnexpectedKeyword::<()> { found: (), expected: () }; + // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both } "#, ); diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 13591dfb2eeb..f3109b9bb73a 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -112,7 +112,7 @@ fn fixes( // shouldn't occur _ => continue 'crates, }; - match current.children.iter().find(|(name, _)| name.eq_ident(seg)) { + match current.children.iter().find(|(name, _)| name.as_str() == seg) { Some((_, &child)) => current = &crate_def_map[child], None => continue 'crates, } @@ -161,7 +161,7 @@ fn fixes( // try finding a parent that has an inline tree from here on let mut current = module; for s in stack.iter().rev() { - match module.children.iter().find(|(name, _)| name.eq_ident(s)) { + match module.children.iter().find(|(name, _)| name.as_str() == s) { Some((_, child)) => { current = &crate_def_map[*child]; } diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index bc9843f3f35a..cfd8919730ad 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -413,8 +413,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { def.canonical_module_path(db).map(|it| { let mut path = String::new(); - it.flat_map(|it| it.name(db)) - .for_each(|name| format_to!(path, "{}/", name.unescaped().display(db))); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.as_str())); path }) } @@ -590,10 +589,10 @@ fn filename_and_frag_for_def( let res = match def { Definition::Adt(adt) => match adt { Adt::Struct(s) => { - format!("struct.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("struct.{}.html", s.name(db).as_str()) } - Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())), - Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())), + Adt::Enum(e) => format!("enum.{}.html", e.name(db).as_str()), + Adt::Union(u) => format!("union.{}.html", u.name(db).as_str()), }, Definition::Crate(_) => String::from("index.html"), Definition::Module(m) => match m.name(db) { @@ -603,48 +602,48 @@ fn filename_and_frag_for_def( Some(kw) => { format!("keyword.{}.html", kw) } - None => format!("{}/index.html", name.unescaped().display(db.upcast())), + None => format!("{}/index.html", name.as_str()), } } None => String::from("index.html"), }, Definition::Trait(t) => { - format!("trait.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("trait.{}.html", t.name(db).as_str()) } Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("traitalias.{}.html", t.name(db).as_str()) } Definition::TypeAlias(t) => { - format!("type.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("type.{}.html", t.name(db).as_str()) } Definition::BuiltinType(t) => { - format!("primitive.{}.html", t.name().unescaped().display(db.upcast())) + format!("primitive.{}.html", t.name().as_str()) } Definition::Function(f) => { - format!("fn.{}.html", f.name(db).unescaped().display(db.upcast())) + format!("fn.{}.html", f.name(db).as_str()) } Definition::Variant(ev) => { format!( "enum.{}.html#variant.{}", - ev.parent_enum(db).name(db).unescaped().display(db.upcast()), - ev.name(db).unescaped().display(db.upcast()) + ev.parent_enum(db).name(db).as_str(), + ev.name(db).as_str() ) } Definition::Const(c) => { - format!("const.{}.html", c.name(db)?.unescaped().display(db.upcast())) + format!("const.{}.html", c.name(db)?.as_str()) } Definition::Static(s) => { - format!("static.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("static.{}.html", s.name(db).as_str()) } Definition::Macro(mac) => match mac.kind(db) { hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { - format!("macro.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("macro.{}.html", mac.name(db).as_str()) } hir::MacroKind::Derive => { - format!("derive.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("derive.{}.html", mac.name(db).as_str()) } }, Definition::Field(field) => { @@ -654,11 +653,7 @@ fn filename_and_frag_for_def( hir::VariantDef::Variant(it) => Definition::Variant(it), }; let (_, file, _) = filename_and_frag_for_def(db, def)?; - return Some(( - def, - file, - Some(format!("structfield.{}", field.name(db).unescaped().display(db.upcast()))), - )); + return Some((def, file, Some(format!("structfield.{}", field.name(db).as_str())))); } Definition::SelfType(impl_) => { let adt = impl_.self_ty(db).as_adt()?.into(); @@ -667,7 +662,7 @@ fn filename_and_frag_for_def( return Some((adt, file, Some(String::from("impl")))); } Definition::ExternCrateDecl(it) => { - format!("{}/index.html", it.name(db).unescaped().display(db.upcast())) + format!("{}/index.html", it.name(db).as_str()) } Definition::Local(_) | Definition::GenericParam(_) @@ -699,16 +694,16 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) -> // Rustdoc makes this decision based on whether a method 'has defaultness'. // Currently this is only the case for provided trait methods. if is_trait_method && !function.has_body(db) { - format!("tymethod.{}", function.name(db).unescaped().display(db.upcast())) + format!("tymethod.{}", function.name(db).as_str()) } else { - format!("method.{}", function.name(db).unescaped().display(db.upcast())) + format!("method.{}", function.name(db).as_str()) } } AssocItem::Const(constant) => { - format!("associatedconstant.{}", constant.name(db)?.unescaped().display(db.upcast())) + format!("associatedconstant.{}", constant.name(db)?.as_str()) } AssocItem::TypeAlias(ty) => { - format!("associatedtype.{}", ty.name(db).unescaped().display(db.upcast())) + format!("associatedtype.{}", ty.name(db).as_str()) } }) } diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 6c739de82d96..d18732a6b846 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -5,10 +5,14 @@ use crate::{ navigation_target::{self, ToNav}, FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; -use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; +use hir::{ + sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt, + ModuleDef, Semantics, +}; use ide_db::{ base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, + famous_defs::FamousDefs, helpers::pick_best_token, RootDatabase, SymbolKind, }; @@ -81,6 +85,10 @@ pub(crate) fn goto_definition( return Some(RangeInfo::new(original_token.text_range(), navs)); } + if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) { + return Some(RangeInfo::new(original_token.text_range(), navs)); + } + let navs = sema .descend_into_macros_no_opaque(original_token.clone()) .into_iter() @@ -125,6 +133,77 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } +// If the token is into(), try_into(), search the definition of From, TryFrom. +fn find_definition_for_known_blanket_dual_impls( + sema: &Semantics<'_, RootDatabase>, + original_token: &SyntaxToken, +) -> Option> { + let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; + let callable = sema.resolve_method_call_as_callable(&method_call)?; + let CallableKind::Function(f) = callable.kind() else { return None }; + let assoc = f.as_assoc_item(sema.db)?; + + let return_type = callable.return_type(); + let fd = FamousDefs(sema, return_type.krate(sema.db)); + + let t = match assoc.container(sema.db) { + hir::AssocItemContainer::Trait(t) => t, + hir::AssocItemContainer::Impl(impl_) + if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse => + { + let t = fd.core_convert_FromStr()?; + let t_f = t.function(sema.db, &sym::from_str)?; + return sema + .resolve_trait_impl_method( + return_type.clone(), + t, + t_f, + [return_type.type_arguments().next()?], + ) + .map(|f| def_to_nav(sema.db, f.into())); + } + hir::AssocItemContainer::Impl(_) => return None, + }; + + let fn_name = f.name(sema.db); + let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) { + let dual = fd.core_convert_From()?; + let dual_f = dual.function(sema.db, &sym::from)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [return_type, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) { + let dual = fd.core_convert_TryFrom()?; + let dual_f = dual.function(sema.db, &sym::try_from)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + // Extract the `T` from `Result` + [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) { + let dual = fd.core_fmt_Display()?; + let dual_f = dual.function(sema.db, &sym::fmt)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [callable.receiver_param(sema.db)?.1.strip_reference()], + )? + } else { + return None; + }; + // Assert that we got a trait impl function, if we are back in a trait definition we didn't + // succeed + let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?; + let def = Definition::from(f); + Some(def_to_nav(sema.db, def)) +} + fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: ast::String, @@ -3022,4 +3101,177 @@ fn foo() { "#, ); } + #[test] + fn into_call_to_from_definition() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl From for B { + fn from(value: A) -> Self { + //^^^^ + B + } +} + +fn f() { + let a = A; + let b: B = a.into$0(); +} + "#, + ); + } + + #[test] + fn into_call_to_from_definition_with_trait_bounds() { + check( + r#" +//- minicore: from, iterator +struct A; + +impl From for A +where + T: IntoIterator, +{ + fn from(value: T) -> Self { + //^^^^ + A + } +} + +fn f() { + let a: A = [1, 2, 3].into$0(); +} + "#, + ); + } + + #[test] + fn goto_into_definition_if_exists() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl Into for A { + fn into(self) -> B { + //^^^^ + B + } +} + +fn f() { + let a = A; + let b: B = a.into$0(); +} + "#, + ); + } + + #[test] + fn try_into_call_to_try_from_definition() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl TryFrom for B { + type Error = String; + + fn try_from(value: A) -> Result { + //^^^^^^^^ + Ok(B) + } +} + +fn f() { + let a = A; + let b: Result = a.try_into$0(); +} + "#, + ); + } + + #[test] + fn goto_try_into_definition_if_exists() { + check( + r#" +//- minicore: from +struct A; + +struct B; + +impl TryInto for A { + type Error = String; + + fn try_into(self) -> Result { + //^^^^^^^^ + Ok(B) + } +} + +fn f() { + let a = A; + let b: Result = a.try_into$0(); +} + "#, + ); + } + + #[test] + fn parse_call_to_from_str_definition() { + check( + r#" +//- minicore: from, str +struct A; +impl FromStr for A { + type Error = String; + fn from_str(value: &str) -> Result { + //^^^^^^^^ + Ok(A) + } +} +fn f() { + let a: Result = "aaaaaa".parse$0(); +} + "#, + ); + } + + #[test] + fn to_string_call_to_display_definition() { + check( + r#" +//- minicore:fmt +//- /alloc.rs crate:alloc +pub mod string { + pub struct String; + pub trait ToString { + fn to_string(&self) -> String; + } + + impl ToString for T { + fn to_string(&self) -> String { String } + } +} +//- /lib.rs crate:lib deps:alloc +use alloc::string::ToString; +struct A; +impl core::fmt::Display for A { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {} + // ^^^ +} +fn f() { + A.to_string$0(); +} + "#, + ); + } } diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index a7b066700c54..a6f7e0c184ac 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -124,9 +124,7 @@ fn should_hide_param_name_hint( } let fn_name = match callable.kind() { - hir::CallableKind::Function(it) => { - Some(it.name(sema.db).unescaped().display_no_db().to_smolstr()) - } + hir::CallableKind::Function(it) => Some(it.name(sema.db).as_str().to_smolstr()), _ => None, }; let fn_name = fn_name.as_deref(); diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 702f274ae3e6..d9f80cb53dd6 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -44,13 +44,16 @@ pub struct NavigationTarget { /// /// This range must be contained within [`Self::full_range`]. pub focus_range: Option, + // FIXME: Symbol pub name: SmolStr, pub kind: Option, + // FIXME: Symbol pub container_name: Option, pub description: Option, pub docs: Option, /// In addition to a `name` field, a `NavigationTarget` may also be aliased /// In such cases we want a `NavigationTarget` to be accessible by its alias + // FIXME: Symbol pub alias: Option, } @@ -191,10 +194,10 @@ impl TryToNav for FileSymbol { NavigationTarget { file_id, name: self.is_alias.then(|| self.def.name(db)).flatten().map_or_else( - || self.name.clone(), + || self.name.as_str().into(), |it| it.display_no_db(edition).to_smolstr(), ), - alias: self.is_alias.then(|| self.name.clone()), + alias: self.is_alias.then(|| self.name.as_str().into()), kind: Some(self.def.into()), full_range, focus_range, diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index ba739df3092b..07dfd83c4eb7 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -263,7 +263,7 @@ fn find_definitions( .and_then(|def| { // if the name differs from the definitions name it has to be an alias if def - .name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str())) + .name(sema.db).is_some_and(|it| it.as_str() != name_ref.text().trim_start_matches("r#")) { Err(format_err!("Renaming aliases is currently unsupported")) } else { diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 131b21a46b8d..7a090a6b6bb7 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -174,6 +174,7 @@ define_symbols! { const_param_ty, Context, Continue, + convert, copy, Copy, core_panic, @@ -239,6 +240,10 @@ define_symbols! { format_unsafe_arg, format, freeze, + from, + From, + FromStr, + from_str, from_output, from_residual, from_usize, @@ -270,6 +275,8 @@ define_symbols! { index_mut, index, Index, + into, + Into, into_future, into_iter, IntoFuture, @@ -358,6 +365,7 @@ define_symbols! { panic_nounwind, panic, Param, + parse, partial_ord, PartialEq, PartialOrd, @@ -451,12 +459,17 @@ define_symbols! { termination, test_case, test, + then, thiscall, + to_string, trace_macros, transmute_opts, transmute_trait, transparent, + try_into, Try, + TryFrom, + try_from, tuple_trait, u128, u16, diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs index e38571dd3ec6..b197b086f377 100644 --- a/crates/parser/src/event.rs +++ b/crates/parser/src/event.rs @@ -12,7 +12,7 @@ use crate::{ /// `Parser` produces a flat list of `Event`s. /// They are converted to a tree-structure in /// a separate pass, via `TreeBuilder`. -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub(crate) enum Event { /// This event signifies the start of the node. /// It should be either abandoned (in which case the diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 03439b784d35..389c01933c99 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -134,10 +134,12 @@ pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) { // test_err let_else_right_curly_brace // fn func() { let Some(_) = {Some(1)} else { panic!("h") };} if let Some(expr) = expr_after_eq { - if BlockLike::is_blocklike(expr.kind()) { - p.error( - "right curly brace `}` before `else` in a `let...else` statement not allowed", - ) + if let Some(token) = expr.last_token(p) { + if token == T!['}'] { + p.error( + "right curly brace `}` before `else` in a `let...else` statement not allowed" + ) + } } } diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 75a75f601cf1..2f6ba525747c 100644 --- a/crates/parser/src/parser.rs +++ b/crates/parser/src/parser.rs @@ -318,7 +318,8 @@ impl Marker { _ => unreachable!(), } p.push_event(Event::Finish); - CompletedMarker::new(self.pos, kind) + let end_pos = p.events.len() as u32; + CompletedMarker::new(self.pos, end_pos, kind) } /// Abandons the syntax tree node. All its children @@ -336,13 +337,14 @@ impl Marker { } pub(crate) struct CompletedMarker { - pos: u32, + start_pos: u32, + end_pos: u32, kind: SyntaxKind, } impl CompletedMarker { - fn new(pos: u32, kind: SyntaxKind) -> Self { - CompletedMarker { pos, kind } + fn new(start_pos: u32, end_pos: u32, kind: SyntaxKind) -> Self { + CompletedMarker { start_pos, end_pos, kind } } /// This method allows to create a new node which starts @@ -360,10 +362,10 @@ impl CompletedMarker { /// distance to `NEWSTART` into forward_parent(=2 in this case); pub(crate) fn precede(self, p: &mut Parser<'_>) -> Marker { let new_pos = p.start(); - let idx = self.pos as usize; + let idx = self.start_pos as usize; match &mut p.events[idx] { Event::Start { forward_parent, .. } => { - *forward_parent = Some(new_pos.pos - self.pos); + *forward_parent = Some(new_pos.pos - self.start_pos); } _ => unreachable!(), } @@ -376,7 +378,7 @@ impl CompletedMarker { let idx = m.pos as usize; match &mut p.events[idx] { Event::Start { forward_parent, .. } => { - *forward_parent = Some(self.pos - m.pos); + *forward_parent = Some(self.start_pos - m.pos); } _ => unreachable!(), } @@ -386,4 +388,13 @@ impl CompletedMarker { pub(crate) fn kind(&self) -> SyntaxKind { self.kind } + + pub(crate) fn last_token(&self, p: &Parser<'_>) -> Option { + let end_pos = self.end_pos as usize; + debug_assert_eq!(p.events[end_pos - 1], Event::Finish); + p.events[..end_pos].iter().rev().find_map(|event| match event { + Event::Token { kind, .. } => Some(*kind), + _ => None, + }) + } } diff --git a/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast new file mode 100644 index 000000000000..578dc2b0f966 --- /dev/null +++ b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast @@ -0,0 +1,79 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "X" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + RECORD_FIELD + NAME + IDENT "a" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "X" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE "\n " + RECORD_EXPR_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "1" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" +error 63: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs new file mode 100644 index 000000000000..c0c0edc98301 --- /dev/null +++ b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs @@ -0,0 +1,8 @@ +struct X {a: i32} +fn f() { + let foo = X { + a: 1 + } else { + return; + }; +} \ No newline at end of file diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast new file mode 100644 index 000000000000..8e994f22d41b --- /dev/null +++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast @@ -0,0 +1,42 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + LITERAL + INT_NUMBER "1" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LITERAL + INT_NUMBER "1" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item +error 23: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs new file mode 100644 index 000000000000..c29ddcce1ff5 --- /dev/null +++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs @@ -0,0 +1,5 @@ +let foo = 1 + { + 1 +} else { + return; +}; diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast new file mode 100644 index 000000000000..055b583acec8 --- /dev/null +++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast @@ -0,0 +1,90 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "r" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "ok" + WHITESPACE " " + EQ "=" + WHITESPACE " " + MACRO_EXPR + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "format_args" + BANG "!" + TOKEN_TREE + L_PAREN "(" + STRING "\"\"" + R_PAREN ")" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "bad" + WHITESPACE " " + EQ "=" + WHITESPACE " " + MACRO_EXPR + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "format_args" + BANG "!" + WHITESPACE " " + TOKEN_TREE + L_CURLY "{" + STRING "\"\"" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 89: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs new file mode 100644 index 000000000000..5916fa07dc2f --- /dev/null +++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs @@ -0,0 +1,5 @@ +fn r() { + let ok = format_args!("") else { return; }; + + let bad = format_args! {""} else { return; }; +} diff --git a/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast new file mode 100644 index 000000000000..8c7fd8c295d9 --- /dev/null +++ b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast @@ -0,0 +1,40 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "1" + DOT2 ".." + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LITERAL + INT_NUMBER "1" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item +error 22: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs new file mode 100644 index 000000000000..5417131d28ea --- /dev/null +++ b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs @@ -0,0 +1,5 @@ +let foo = 1..{ + 1 +} else { + return; +}; diff --git a/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast new file mode 100644 index 000000000000..57925a0d192f --- /dev/null +++ b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast @@ -0,0 +1,55 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PARAM + IDENT_PAT + NAME + IDENT "x" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + PIPE "|" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item +error 28: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs new file mode 100644 index 000000000000..89c7579b071f --- /dev/null +++ b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs @@ -0,0 +1,5 @@ +let foo = |x: i32| { + x +} else { + return; +}; diff --git a/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast new file mode 100644 index 000000000000..4fb70bd50e38 --- /dev/null +++ b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast @@ -0,0 +1,38 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PREFIX_EXPR + MINUS "-" + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LITERAL + INT_NUMBER "1" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item +error 20: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs new file mode 100644 index 000000000000..1ba7f7d761ba --- /dev/null +++ b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs @@ -0,0 +1,5 @@ +let foo = -{ + 1 +} else { + return; +}; diff --git a/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast new file mode 100644 index 000000000000..e8eeeee695e9 --- /dev/null +++ b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast @@ -0,0 +1,90 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "o" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + THIN_ARROW "->" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Result" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + TUPLE_TYPE + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE " " + TYPE_ARG + TUPLE_TYPE + L_PAREN "(" + R_PAREN ")" + R_ANGLE ">" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + YEET_EXPR + DO_KW "do" + WHITESPACE " " + YEET_KW "yeet" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + WHITESPACE " " + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Ok" + ARG_LIST + L_PAREN "(" + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" +error 67: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs new file mode 100644 index 000000000000..188fb07d91b4 --- /dev/null +++ b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs @@ -0,0 +1,7 @@ +fn o() -> Result<(), ()> { + let foo = do yeet { + () + } else { + return Ok(()); + }; +} \ No newline at end of file diff --git a/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast new file mode 100644 index 000000000000..cc5e1278c3de --- /dev/null +++ b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast @@ -0,0 +1,40 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BECOME_EXPR + BECOME_KW "become" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item +error 27: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs new file mode 100644 index 000000000000..622548b8f33c --- /dev/null +++ b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs @@ -0,0 +1,5 @@ +let foo = become { + () +} else { + return; +}; diff --git a/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast new file mode 100644 index 000000000000..ea2f4f28e2d8 --- /dev/null +++ b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast @@ -0,0 +1,38 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + REF_EXPR + AMP "&" + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LITERAL + INT_NUMBER "1" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item +error 20: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs new file mode 100644 index 000000000000..9a00dca3689f --- /dev/null +++ b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs @@ -0,0 +1,5 @@ +let foo = &{ + 1 +} else { + return; +}; diff --git a/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast new file mode 100644 index 000000000000..47396140c5c3 --- /dev/null +++ b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast @@ -0,0 +1,45 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "bar" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LITERAL + INT_NUMBER "1" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item +error 25: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs new file mode 100644 index 000000000000..08e677416f10 --- /dev/null +++ b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs @@ -0,0 +1,5 @@ +let foo = bar = { + 1 +} else { + return; +}; diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index 00695c547372..191535ac55e9 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -14,6 +14,7 @@ doctest = false [dependencies] object.workspace = true +libc.workspace = true libloading.workspace = true memmap2.workspace = true diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index fe15d42b4e48..cbf7a277bfae 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -28,11 +28,16 @@ fn load_library(file: &Utf8Path) -> Result { #[cfg(unix)] fn load_library(file: &Utf8Path) -> Result { + // not defined by POSIX, different values on mips vs other targets + #[cfg(target_env = "gnu")] + use libc::RTLD_DEEPBIND; use libloading::os::unix::Library as UnixLibrary; - use std::os::raw::c_int; + // defined by POSIX + use libloading::os::unix::RTLD_NOW; - const RTLD_NOW: c_int = 0x00002; - const RTLD_DEEPBIND: c_int = 0x00008; + // MUSL and bionic don't have it.. + #[cfg(not(target_env = "gnu"))] + const RTLD_DEEPBIND: std::os::raw::c_int = 0x0; unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) } } diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 2e3413f339b6..3179c810f699 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -21,7 +21,10 @@ jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = tr perf-event = "=0.4.7" [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.52", features = ["Win32_System_Threading", "Win32_System_ProcessStatus"] } +windows-sys = { version = "0.59", features = [ + "Win32_System_Threading", + "Win32_System_ProcessStatus", +] } [features] cpu_profiler = [] diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index 6a88cf022dfb..a39639676104 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -63,7 +63,7 @@ use crate::{ManifestPath, TargetKind}; pub struct ProjectJson { /// e.g. `path/to/sysroot` pub(crate) sysroot: Option, - /// e.g. `path/to/sysroot/lib/rustlib/src/rust` + /// e.g. `path/to/sysroot/lib/rustlib/src/rust/library` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, /// The path to the rust-project.json file. May be None if this diff --git a/crates/project-model/src/toolchain_info/rustc_cfg.rs b/crates/project-model/src/toolchain_info/rustc_cfg.rs index afcc81207949..4bf9b59e7d03 100644 --- a/crates/project-model/src/toolchain_info/rustc_cfg.rs +++ b/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -66,7 +66,7 @@ fn rustc_print_cfg( QueryConfig::Cargo(sysroot, cargo_toml) => { let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); cmd.envs(extra_env); - cmd.args(["rustc"]).args(RUSTC_ARGS); + cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); if let Some(target) = target { cmd.args(["--target", target]); } diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index d06130ce8c59..c24cbb4a3117 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -76,7 +76,10 @@ vfs.workspace = true paths.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.52", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Threading"] } +windows-sys = { version = "0.59", features = [ + "Win32_System_Diagnostics_Debug", + "Win32_System_Threading", +] } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = true } diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index bf0d6df9ad81..1ebb48c577a6 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -23,7 +23,7 @@ itertools.workspace = true [target.'cfg(windows)'.dependencies] miow = "0.6.0" -windows-sys = { version = "0.52", features = ["Win32_Foundation"] } +windows-sys = { version = "0.59", features = ["Win32_Foundation"] } [features] # Uncomment to enable for the whole crate graph diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 4462c2ce1e13..fd06736a2524 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -32,7 +32,7 @@ //! error: fmt //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fn: tuple -//! from: sized +//! from: sized, result //! future: pin //! coroutine: pin //! dispatch_from_dyn: unsize, pin @@ -332,6 +332,25 @@ pub mod convert { t } } + + pub trait TryFrom: Sized { + type Error; + fn try_from(value: T) -> Result; + } + pub trait TryInto: Sized { + type Error; + fn try_into(self) -> Result; + } + + impl TryInto for T + where + U: TryFrom, + { + type Error = U::Error; + fn try_into(self) -> Result { + U::try_from(self) + } + } // endregion:from // region:as_ref @@ -1555,6 +1574,15 @@ pub mod str { pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { "" } + pub trait FromStr: Sized { + type Err; + fn from_str(s: &str) -> Result; + } + impl str { + pub fn parse(&self) -> Result { + FromStr::from_str(self) + } + } } // endregion:str @@ -1814,7 +1842,7 @@ pub mod prelude { cmp::{Eq, PartialEq}, // :eq cmp::{Ord, PartialOrd}, // :ord convert::AsRef, // :as_ref - convert::{From, Into}, // :from + convert::{From, Into, TryFrom, TryInto}, // :from default::Default, // :default iter::{IntoIterator, Iterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive @@ -1829,6 +1857,7 @@ pub mod prelude { option::Option::{self, None, Some}, // :option panic, // :panic result::Result::{self, Err, Ok}, // :result + str::FromStr, // :str }; } diff --git a/docs/dev/README.md b/docs/dev/README.md index cd0f49174cdc..3ba492e09597 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -154,19 +154,21 @@ There are also several VS Code commands which might be of interest: * `rust-analyzer: Status` shows some memory-usage statistics. -* `rust-analyzer: Syntax Tree` shows syntax tree of the current file/selection. - * `rust-analyzer: View Hir` shows the HIR expressions within the function containing the cursor. - You can hover over syntax nodes in the opened text file to see the appropriate - rust code that it refers to and the rust editor will also highlight the proper - text range. +* If `rust-analyzer.showSyntaxTree` is enabled in settings, `Rust Syntax Tree: Focus on Rust Syntax Tree View` shows the syntax tree of the current file. + + You can click on nodes in the rust editor to go to the corresponding syntax node. + + You can click on `Reveal Syntax Element` next to a syntax node to go to the corresponding rust code and highlight the proper text range. If you trigger Go to Definition in the inspected Rust source file, - the syntax tree read-only editor should scroll to and select the + the syntax tree view should scroll to and select the appropriate syntax node token. - ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png) + You can click on `Copy` next to a syntax node to copy a text representation of the node. + + ![demo](https://github.com/user-attachments/assets/2d20ae87-0abf-495f-bee8-54aa2494a00d) ## Profiling diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index ffc820e9b7f7..4a2a6f2e3686 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -716,6 +716,32 @@ interface JsonProject { /// dependencies as well as sysroot crate (libstd, /// libcore and such). crates: Crate[]; + /// Configuration for CLI commands. + /// + /// These are used for running and debugging binaries + /// and tests without encoding build system-specific + /// knowledge into rust-analyzer. + /// + /// # Example + /// + /// Below is an example of a test runnable. `{label}` and `{test_id}` + /// are explained in `Runnable::args`'s documentation below. + /// + /// ```json + /// { + /// "program": "buck", + /// "args": [ + /// "test", + /// "{label}", + /// "--", + /// "{test_id}", + /// "--print-passing-details" + /// ], + /// "cwd": "/home/user/repo-root/", + /// "kind": "testOne" + /// } + /// ``` + runnables?: Runnable[]; } interface Crate { @@ -726,7 +752,10 @@ interface Crate { /// Path to the root module of the crate. root_module: string; /// Edition of the crate. - edition: "2015" | "2018" | "2021"; + edition: '2015' | '2018' | '2021' | '2024'; + /// The version of the crate. Used for calculating + /// the correct docs.rs URL. + version?: string; /// Dependencies deps: Dep[]; /// Should this crate be treated as a member of @@ -757,9 +786,9 @@ interface Crate { /// rust-analyzer assumes that files from one /// source can't refer to files in another source. source?: { - include_dirs: string[], - exclude_dirs: string[], - }, + include_dirs: string[]; + exclude_dirs: string[]; + }; /// List of cfg groups this crate inherits. /// /// All cfg in these groups will be concatenated to @@ -776,21 +805,68 @@ interface Crate { target?: string; /// Environment variables, used for /// the `env!` macro - env: { [key: string]: string; }, + env: { [key: string]: string; }; /// Whether the crate is a proc-macro crate. is_proc_macro: boolean; /// For proc-macro crates, path to compiled /// proc-macro (.so file). proc_macro_dylib_path?: string; + + /// Repository, matching the URL that would be used + /// in Cargo.toml. + repository?: string; + + /// Build-specific data about this crate. + build?: BuildInfo; } interface Dep { /// Index of a crate in the `crates` array. - crate: number, + crate: number; /// Name as should appear in the (implicit) /// `extern crate name` declaration. - name: string, + name: string; +} + +interface BuildInfo { + /// The name associated with this crate. + /// + /// This is determined by the build system that produced + /// the `rust-project.json` in question. For instance, if buck were used, + /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. + /// + /// Do not attempt to parse the contents of this string; it is a build system-specific + /// identifier similar to `Crate::display_name`. + label: string; + /// Path corresponding to the build system-specific file defining the crate. + build_file: string; + /// The kind of target. + /// + /// This information is used to determine what sort + /// of runnable codelens to provide, if any. + target_kind: 'bin' | 'lib' | 'test'; +} + +interface Runnable { + /// The program invoked by the runnable. + /// + /// For example, this might be `cargo`, `buck`, or `bazel`. + program: string; + /// The arguments passed to `program`. + args: string[]; + /// The current working directory of the runnable. + cwd: string; + /// Used to decide what code lens to offer. + /// + /// `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// + /// The args for testOne can contain two template strings: + /// `{label}` and `{test_id}`. `{label}` will be replaced + /// with the `Build::label` and `{test_id}` will be replaced + /// with the test name. + kind: 'testOne' | string; } ---- diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 5550bfa6558a..96dc4f19b82c 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -361,7 +361,14 @@ export class Ctx implements RustAnalyzerExtensionApi { } }); - vscode.workspace.onDidChangeTextDocument(async () => { + vscode.workspace.onDidChangeTextDocument(async (e) => { + if ( + vscode.window.activeTextEditor?.document !== e.document || + e.contentChanges.length === 0 + ) { + return; + } + if (this.syntaxTreeView?.visible) { await this.syntaxTreeProvider?.refresh(); } diff --git a/lib/lsp-server/src/msg.rs b/lib/lsp-server/src/msg.rs index 11f98f507904..074bc43388a9 100644 --- a/lib/lsp-server/src/msg.rs +++ b/lib/lsp-server/src/msg.rs @@ -181,15 +181,15 @@ impl Message { Ok(Some(msg)) } - pub fn write(self, w: &mut impl Write) -> io::Result<()> { + pub fn write(&self, w: &mut impl Write) -> io::Result<()> { self._write(w) } - fn _write(self, w: &mut dyn Write) -> io::Result<()> { + fn _write(&self, w: &mut dyn Write) -> io::Result<()> { #[derive(Serialize)] - struct JsonRpc { + struct JsonRpc<'a> { jsonrpc: &'static str, #[serde(flatten)] - msg: Message, + msg: &'a Message, } let text = serde_json::to_string(&JsonRpc { jsonrpc: "2.0", msg: self })?; write_msg_text(w, &text) diff --git a/lib/lsp-server/src/socket.rs b/lib/lsp-server/src/socket.rs index 36d728456f77..48400abf2295 100644 --- a/lib/lsp-server/src/socket.rs +++ b/lib/lsp-server/src/socket.rs @@ -15,8 +15,11 @@ pub(crate) fn socket_transport( stream: TcpStream, ) -> (Sender, Receiver, IoThreads) { let (reader_receiver, reader) = make_reader(stream.try_clone().unwrap()); - let (writer_sender, writer) = make_write(stream); - let io_threads = make_io_threads(reader, writer); + let (writer_sender, writer, messages_to_drop) = make_write(stream); + let dropper = std::thread::spawn(move || { + messages_to_drop.into_iter().for_each(drop); + }); + let io_threads = make_io_threads(reader, writer, dropper); (writer_sender, reader_receiver, io_threads) } @@ -36,11 +39,21 @@ fn make_reader(stream: TcpStream) -> (Receiver, thread::JoinHandle (Sender, thread::JoinHandle>) { +fn make_write( + mut stream: TcpStream, +) -> (Sender, thread::JoinHandle>, Receiver) { let (writer_sender, writer_receiver) = bounded::(0); + let (drop_sender, drop_receiver) = bounded::(0); let writer = thread::spawn(move || { - writer_receiver.into_iter().try_for_each(|it| it.write(&mut stream)).unwrap(); + writer_receiver + .into_iter() + .try_for_each(|it| { + let result = it.write(&mut stream); + let _ = drop_sender.send(it); + result + }) + .unwrap(); Ok(()) }); - (writer_sender, writer) + (writer_sender, writer, drop_receiver) } diff --git a/lib/lsp-server/src/stdio.rs b/lib/lsp-server/src/stdio.rs index 279a6bce0807..8344c9f56b53 100644 --- a/lib/lsp-server/src/stdio.rs +++ b/lib/lsp-server/src/stdio.rs @@ -11,15 +11,24 @@ use crate::Message; /// Creates an LSP connection via stdio. pub(crate) fn stdio_transport() -> (Sender, Receiver, IoThreads) { + let (drop_sender, drop_receiver) = bounded::(0); let (writer_sender, writer_receiver) = bounded::(0); let writer = thread::Builder::new() .name("LspServerWriter".to_owned()) .spawn(move || { let stdout = stdout(); let mut stdout = stdout.lock(); - writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout)) + writer_receiver.into_iter().try_for_each(|it| { + let result = it.write(&mut stdout); + let _ = drop_sender.send(it); + result + }) }) .unwrap(); + let dropper = thread::Builder::new() + .name("LspMessageDropper".to_owned()) + .spawn(move || drop_receiver.into_iter().for_each(drop)) + .unwrap(); let (reader_sender, reader_receiver) = bounded::(0); let reader = thread::Builder::new() .name("LspServerReader".to_owned()) @@ -41,7 +50,7 @@ pub(crate) fn stdio_transport() -> (Sender, Receiver, IoThread Ok(()) }) .unwrap(); - let threads = IoThreads { reader, writer }; + let threads = IoThreads { reader, writer, dropper }; (writer_sender, reader_receiver, threads) } @@ -49,13 +58,15 @@ pub(crate) fn stdio_transport() -> (Sender, Receiver, IoThread pub(crate) fn make_io_threads( reader: thread::JoinHandle>, writer: thread::JoinHandle>, + dropper: thread::JoinHandle<()>, ) -> IoThreads { - IoThreads { reader, writer } + IoThreads { reader, writer, dropper } } pub struct IoThreads { reader: thread::JoinHandle>, writer: thread::JoinHandle>, + dropper: thread::JoinHandle<()>, } impl IoThreads { @@ -64,6 +75,12 @@ impl IoThreads { Ok(r) => r?, Err(err) => std::panic::panic_any(err), } + match self.dropper.join() { + Ok(_) => (), + Err(err) => { + std::panic::panic_any(err); + } + } match self.writer.join() { Ok(r) => r, Err(err) => { diff --git a/rust-version b/rust-version index 517fdfe18edc..2d9a927c638b 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -fb546ee09b226bc4dd4b712d35a372d923c4fa54 +9a1d156f38c51441ee51e5a068f1d0caf4bb0f27