diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
commit | dc0db358abe19481e475e10c32149b53370f1a1c (patch) | |
tree | ab8ce99c4b255ce46f99ef402c27916055b899ee /src/tools/rust-analyzer/crates/ide-db | |
parent | Releasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-db')
24 files changed, 1183 insertions, 604 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 57daaf623..4e75dc4db 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -23,6 +23,8 @@ itertools = "0.10.5" arrayvec = "0.7.2" indexmap = "1.9.1" memchr = "2.5.0" +triomphe.workspace = true +nohash-hasher.workspace = true # local deps base-db.workspace = true @@ -36,6 +38,8 @@ text-edit.workspace = true # something from some `hir-xxx` subpackage, reexport the API via `hir`. hir.workspace = true +line-index.workspace = true + [dev-dependencies] expect-test = "1.4.0" oorandom = "11.1.3" diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index ea1d9cc49..0dd544d0a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -1,13 +1,15 @@ //! Applies changes to the IDE state transactionally. -use std::sync::Arc; - use base_db::{ - salsa::{Database, Durability}, + salsa::{ + debug::{DebugQueryTable, TableEntry}, + Database, Durability, Query, QueryTable, + }, Change, SourceRootId, }; use profile::{memory_usage, Bytes}; use rustc_hash::FxHashSet; +use triomphe::Arc; use crate::{symbol_index::SymbolsDatabase, RootDatabase}; @@ -48,22 +50,44 @@ impl RootDatabase { // | VS Code | **rust-analyzer: Memory Usage (Clears Database)** // |=== // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[] - pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> { - let mut acc: Vec<(String, Bytes)> = vec![]; + pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> { + let mut acc: Vec<(String, Bytes, usize)> = vec![]; + + fn collect_query_count<'q, Q>(table: &QueryTable<'q, Q>) -> usize + where + QueryTable<'q, Q>: DebugQueryTable, + Q: Query, + <Q as Query>::Storage: 'q, + { + struct EntryCounter(usize); + impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter { + fn from_iter<T>(iter: T) -> EntryCounter + where + T: IntoIterator<Item = TableEntry<K, V>>, + { + EntryCounter(iter.into_iter().count()) + } + } + table.entries::<EntryCounter>().0 + } + macro_rules! purge_each_query { ($($q:path)*) => {$( let before = memory_usage().allocated; - $q.in_db(self).purge(); + let table = $q.in_db(self); + let count = collect_query_count(&table); + table.purge(); let after = memory_usage().allocated; let q: $q = Default::default(); let name = format!("{:?}", q); - acc.push((name, before - after)); + acc.push((name, before - after, count)); )*} } purge_each_query![ // SourceDatabase base_db::ParseQuery base_db::CrateGraphQuery + base_db::ProcMacrosQuery // SourceDatabaseExt base_db::FileTextQuery @@ -79,7 +103,6 @@ impl RootDatabase { hir::db::MacroDefQuery hir::db::MacroExpandQuery hir::db::ExpandProcMacroQuery - hir::db::MacroExpandErrorQuery hir::db::HygieneFrameQuery // DefDatabase diff --git a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs index 8c6c1c44a..7a7328f31 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs @@ -98,7 +98,7 @@ impl FromStr for AssistKind { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AssistId(pub &'static str, pub AssistKind); -/// A way to control how many asssist to resolve during the assist resolution. +/// A way to control how many assist to resolve during the assist resolution. /// When an assist is resolved, its edits are calculated that might be costly to always do by default. #[derive(Debug)] pub enum AssistResolveStrategy { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 4071c490b..760834bfa 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -8,8 +8,8 @@ use arrayvec::ArrayVec; use hir::{ Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field, - Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, - Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, + Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name, + PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, Visibility, }; use stdx::impl_from; @@ -622,22 +622,3 @@ impl From<ModuleDef> for Definition { } } } - -impl From<Definition> for Option<ItemInNs> { - fn from(def: Definition) -> Self { - let item = match def { - Definition::Module(it) => ModuleDef::Module(it), - Definition::Function(it) => ModuleDef::Function(it), - Definition::Adt(it) => ModuleDef::Adt(it), - Definition::Variant(it) => ModuleDef::Variant(it), - Definition::Const(it) => ModuleDef::Const(it), - Definition::Static(it) => ModuleDef::Static(it), - Definition::Trait(it) => ModuleDef::Trait(it), - Definition::TraitAlias(it) => ModuleDef::TraitAlias(it), - Definition::TypeAlias(it) => ModuleDef::TypeAlias(it), - Definition::BuiltinType(it) => ModuleDef::BuiltinType(it), - _ => return None, - }; - Some(ItemInNs::from(item)) - } -} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index f0c369096..e488300b4 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -4230,7 +4230,7 @@ pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable. pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () }; ``` -Like transarent `struct`s, a transparent `union` of type `U` has the same +Like transparent `struct`s, a transparent `union` of type `U` has the same layout, size, and ABI as its single non-ZST field. If it is generic over a type `T`, and all its fields are ZSTs except for exactly one field of type `T`, then it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized). @@ -6548,7 +6548,7 @@ subtracting elements in an Add impl."##, }, Lint { label: "clippy::suspicious_assignment_formatting", - description: r##"Checks for use of the non-existent `=*`, `=!` and `=-` + description: r##"Checks for use of the nonexistent `=*`, `=!` and `=-` operators."##, }, Lint { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 8e3b1eef1..eba9d8afc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -77,7 +77,7 @@ pub fn visit_file_defs( } module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); - let is_root = module.is_crate_root(db); + let is_root = module.is_crate_root(); module .legacy_macros(db) .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index b26b0a908..901d592c6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -362,12 +362,12 @@ fn import_for_item( let original_item_candidate = item_for_path_search(db, original_item)?; let import_path_candidate = mod_path(original_item_candidate)?; - let import_path_string = import_path_candidate.to_string(); + let import_path_string = import_path_candidate.display(db).to_string(); let expected_import_end = if item_as_assoc(db, original_item).is_some() { unresolved_qualifier.to_string() } else { - format!("{unresolved_qualifier}::{}", item_name(db, original_item)?) + format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db)) }; if !import_path_string.contains(unresolved_first_segment) || !import_path_string.ends_with(&expected_import_end) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs index 07a57c883..46f1353e2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs @@ -5,17 +5,11 @@ use either::Either; use hir::{ import_map::{self, ImportKind}, - symbols::FileSymbol, AsAssocItem, Crate, ItemInNs, Semantics, }; use limit::Limit; -use syntax::{ast, AstNode, SyntaxKind::NAME}; -use crate::{ - defs::{Definition, NameClass}, - imports::import_assets::NameToImport, - symbol_index, RootDatabase, -}; +use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; /// A value to use, when uncertain which limit to pick. pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40); @@ -115,12 +109,12 @@ fn find_items<'a>( }); // Query the local crate using the symbol index. - let local_results = symbol_index::crate_symbols(db, krate, local_query) + let local_results = local_query + .search(&symbol_index::crate_symbols(db, krate)) .into_iter() - .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate)) - .filter_map(|name_definition_to_import| match name_definition_to_import { - Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), - def => <Option<_>>::from(def), + .filter_map(|local_candidate| match local_candidate.def { + hir::ModuleDef::Macro(macro_def) => Some(ItemInNs::Macros(macro_def)), + def => Some(ItemInNs::from(def)), }); external_importables.chain(local_results).filter(move |&item| match assoc_item_search { @@ -130,22 +124,6 @@ fn find_items<'a>( }) } -fn get_name_definition( - sema: &Semantics<'_, RootDatabase>, - import_candidate: &FileSymbol, -) -> Option<Definition> { - let _p = profile::span("get_name_definition"); - - let candidate_node = import_candidate.loc.syntax(sema)?; - let candidate_name_node = if candidate_node.kind() != NAME { - candidate_node.children().find(|it| it.kind() == NAME)? - } else { - candidate_node - }; - let name = ast::Name::cast(candidate_name_node)?; - NameClass::classify(sema, &name)?.defined() -} - fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some() } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index b1df11bf9..ff1a20f03 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -13,7 +13,6 @@ pub mod famous_defs; pub mod helpers; pub mod items_locator; pub mod label; -pub mod line_index; pub mod path_transform; pub mod rename; pub mod rust_doc; @@ -43,21 +42,20 @@ pub mod syntax_helpers { pub use parser::LexedStr; } -use std::{fmt, mem::ManuallyDrop, sync::Arc}; +use std::{fmt, mem::ManuallyDrop}; use base_db::{ salsa::{self, Durability}, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; -use hir::{ - db::{DefDatabase, ExpandDatabase, HirDatabase}, - symbols::FileSymbolKind, -}; -use stdx::hash::NoHashHashSet; +use hir::db::{DefDatabase, ExpandDatabase, HirDatabase}; +use triomphe::Arc; use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +pub use ::line_index; + /// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience. pub use base_db; @@ -114,13 +112,13 @@ impl Upcast<dyn HirDatabase> for RootDatabase { } impl FileLoader for RootDatabase { - fn file_text(&self, file_id: FileId) -> Arc<String> { + fn file_text(&self, file_id: FileId) -> Arc<str> { FileLoaderDelegate(self).file_text(file_id) } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> { + fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { FileLoaderDelegate(self).relevant_crates(file_id) } } @@ -137,18 +135,186 @@ impl RootDatabase { pub fn new(lru_capacity: Option<usize>) -> RootDatabase { let mut db = RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()) }; db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); + db.set_proc_macros_with_durability(Default::default(), Durability::HIGH); db.set_local_roots_with_durability(Default::default(), Durability::HIGH); db.set_library_roots_with_durability(Default::default(), Durability::HIGH); - db.set_enable_proc_attr_macros(false); - db.update_lru_capacity(lru_capacity); + db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH); + db.update_parse_query_lru_capacity(lru_capacity); db } - pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { - let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); + pub fn enable_proc_attr_macros(&mut self) { + self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); + } + + pub fn update_parse_query_lru_capacity(&mut self, lru_capacity: Option<usize>) { + let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); - hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity); - hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); + // macro expansions are usually rather small, so we can afford to keep more of them alive + hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); + hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); + } + + pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) { + use hir::db as hir_db; + + base_db::ParseQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(ParseQuery)) + .copied() + .unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP), + ); + hir_db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(ParseMacroExpansionQuery)) + .copied() + .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP), + ); + hir_db::MacroExpandQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(MacroExpandQuery)) + .copied() + .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP), + ); + + macro_rules! update_lru_capacity_per_query { + ($( $module:ident :: $query:ident )*) => {$( + if let Some(&cap) = lru_capacities.get(stringify!($query)) { + $module::$query.in_db_mut(self).set_lru_capacity(cap); + } + )*} + } + update_lru_capacity_per_query![ + // SourceDatabase + // base_db::ParseQuery + // base_db::CrateGraphQuery + // base_db::ProcMacrosQuery + + // SourceDatabaseExt + // base_db::FileTextQuery + // base_db::FileSourceRootQuery + // base_db::SourceRootQuery + base_db::SourceRootCratesQuery + + // ExpandDatabase + hir_db::AstIdMapQuery + // hir_db::ParseMacroExpansionQuery + // hir_db::InternMacroCallQuery + hir_db::MacroArgTextQuery + hir_db::MacroDefQuery + // hir_db::MacroExpandQuery + hir_db::ExpandProcMacroQuery + hir_db::HygieneFrameQuery + hir_db::ParseMacroExpansionErrorQuery + + // DefDatabase + hir_db::FileItemTreeQuery + hir_db::CrateDefMapQueryQuery + hir_db::BlockDefMapQuery + hir_db::StructDataQuery + hir_db::StructDataWithDiagnosticsQuery + hir_db::UnionDataQuery + hir_db::UnionDataWithDiagnosticsQuery + hir_db::EnumDataQuery + hir_db::EnumDataWithDiagnosticsQuery + hir_db::ImplDataQuery + hir_db::ImplDataWithDiagnosticsQuery + hir_db::TraitDataQuery + hir_db::TraitDataWithDiagnosticsQuery + hir_db::TraitAliasDataQuery + hir_db::TypeAliasDataQuery + hir_db::FunctionDataQuery + hir_db::ConstDataQuery + hir_db::StaticDataQuery + hir_db::Macro2DataQuery + hir_db::MacroRulesDataQuery + hir_db::ProcMacroDataQuery + hir_db::BodyWithSourceMapQuery + hir_db::BodyQuery + hir_db::ExprScopesQuery + hir_db::GenericParamsQuery + hir_db::VariantsAttrsQuery + hir_db::FieldsAttrsQuery + hir_db::VariantsAttrsSourceMapQuery + hir_db::FieldsAttrsSourceMapQuery + hir_db::AttrsQuery + hir_db::CrateLangItemsQuery + hir_db::LangItemQuery + hir_db::ImportMapQuery + hir_db::FieldVisibilitiesQuery + hir_db::FunctionVisibilityQuery + hir_db::ConstVisibilityQuery + hir_db::CrateSupportsNoStdQuery + + // HirDatabase + hir_db::InferQueryQuery + hir_db::MirBodyQuery + hir_db::BorrowckQuery + hir_db::TyQuery + hir_db::ValueTyQuery + hir_db::ImplSelfTyQuery + hir_db::ConstParamTyQuery + hir_db::ConstEvalQuery + hir_db::ConstEvalDiscriminantQuery + hir_db::ImplTraitQuery + hir_db::FieldTypesQuery + hir_db::LayoutOfAdtQuery + hir_db::TargetDataLayoutQuery + hir_db::CallableItemSignatureQuery + hir_db::ReturnTypeImplTraitsQuery + hir_db::GenericPredicatesForParamQuery + hir_db::GenericPredicatesQuery + hir_db::TraitEnvironmentQuery + hir_db::GenericDefaultsQuery + hir_db::InherentImplsInCrateQuery + hir_db::InherentImplsInBlockQuery + hir_db::IncoherentInherentImplCratesQuery + hir_db::TraitImplsInCrateQuery + hir_db::TraitImplsInBlockQuery + hir_db::TraitImplsInDepsQuery + // hir_db::InternCallableDefQuery + // hir_db::InternLifetimeParamIdQuery + // hir_db::InternImplTraitIdQuery + // hir_db::InternTypeOrConstParamIdQuery + // hir_db::InternClosureQuery + // hir_db::InternGeneratorQuery + hir_db::AssociatedTyDataQuery + hir_db::TraitDatumQuery + hir_db::StructDatumQuery + hir_db::ImplDatumQuery + hir_db::FnDefDatumQuery + hir_db::FnDefVarianceQuery + hir_db::AdtVarianceQuery + hir_db::AssociatedTyValueQuery + hir_db::TraitSolveQueryQuery + hir_db::ProgramClausesForChalkEnvQuery + + // SymbolsDatabase + symbol_index::ModuleSymbolsQuery + symbol_index::LibrarySymbolsQuery + // symbol_index::LocalRootsQuery + // symbol_index::LibraryRootsQuery + + // LineIndexDatabase + crate::LineIndexQuery + + // InternDatabase + // hir_db::InternFunctionQuery + // hir_db::InternStructQuery + // hir_db::InternUnionQuery + // hir_db::InternEnumQuery + // hir_db::InternConstQuery + // hir_db::InternStaticQuery + // hir_db::InternTraitQuery + // hir_db::InternTraitAliasQuery + // hir_db::InternTypeAliasQuery + // hir_db::InternImplQuery + // hir_db::InternExternBlockQuery + // hir_db::InternBlockQuery + // hir_db::InternMacro2Query + // hir_db::InternProcMacroQuery + // hir_db::InternMacroRulesQuery + ]; } } @@ -211,20 +377,22 @@ impl From<hir::MacroKind> for SymbolKind { } } -impl From<FileSymbolKind> for SymbolKind { - fn from(it: FileSymbolKind) -> Self { +impl From<hir::ModuleDefId> for SymbolKind { + fn from(it: hir::ModuleDefId) -> Self { match it { - FileSymbolKind::Const => SymbolKind::Const, - FileSymbolKind::Enum => SymbolKind::Enum, - FileSymbolKind::Function => SymbolKind::Function, - FileSymbolKind::Macro => SymbolKind::Macro, - FileSymbolKind::Module => SymbolKind::Module, - FileSymbolKind::Static => SymbolKind::Static, - FileSymbolKind::Struct => SymbolKind::Struct, - FileSymbolKind::Trait => SymbolKind::Trait, - FileSymbolKind::TraitAlias => SymbolKind::TraitAlias, - FileSymbolKind::TypeAlias => SymbolKind::TypeAlias, - FileSymbolKind::Union => SymbolKind::Union, + hir::ModuleDefId::ConstId(..) => SymbolKind::Const, + hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant, + hir::ModuleDefId::FunctionId(..) => SymbolKind::Function, + hir::ModuleDefId::MacroId(..) => SymbolKind::Macro, + hir::ModuleDefId::ModuleId(..) => SymbolKind::Module, + hir::ModuleDefId::StaticId(..) => SymbolKind::Static, + hir::ModuleDefId::AdtId(hir::AdtId::StructId(..)) => SymbolKind::Struct, + hir::ModuleDefId::AdtId(hir::AdtId::EnumId(..)) => SymbolKind::Enum, + hir::ModuleDefId::AdtId(hir::AdtId::UnionId(..)) => SymbolKind::Union, + hir::ModuleDefId::TraitId(..) => SymbolKind::Trait, + hir::ModuleDefId::TraitAliasId(..) => SymbolKind::TraitAlias, + hir::ModuleDefId::TypeAliasId(..) => SymbolKind::TypeAlias, + hir::ModuleDefId::BuiltinType(..) => SymbolKind::TypeAlias, } } } @@ -247,4 +415,5 @@ impl SnippetCap { #[cfg(test)] mod tests { mod sourcegen_lints; + mod line_index; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs deleted file mode 100644 index 16814a1e6..000000000 --- a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs +++ /dev/null @@ -1,314 +0,0 @@ -//! `LineIndex` maps flat `TextSize` offsets into `(Line, Column)` -//! representation. -use std::{iter, mem}; - -use stdx::hash::NoHashHashMap; -use syntax::{TextRange, TextSize}; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LineIndex { - /// Offset the beginning of each line, zero-based. - pub(crate) newlines: Vec<TextSize>, - /// List of non-ASCII characters on each line. - pub(crate) line_wide_chars: NoHashHashMap<u32, Vec<WideChar>>, -} - -/// Line/Column information in native, utf8 format. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct LineCol { - /// Zero-based - pub line: u32, - /// Zero-based utf8 offset - pub col: u32, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum WideEncoding { - Utf16, - Utf32, -} - -/// Line/Column information in legacy encodings. -/// -/// Deliberately not a generic type and different from `LineCol`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct WideLineCol { - /// Zero-based - pub line: u32, - /// Zero-based - pub col: u32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub(crate) struct WideChar { - /// Start offset of a character inside a line, zero-based - pub(crate) start: TextSize, - /// End offset of a character inside a line, zero-based - pub(crate) end: TextSize, -} - -impl WideChar { - /// Returns the length in 8-bit UTF-8 code units. - fn len(&self) -> TextSize { - self.end - self.start - } - - /// Returns the length in UTF-16 or UTF-32 code units. - fn wide_len(&self, enc: WideEncoding) -> usize { - match enc { - WideEncoding::Utf16 => { - if self.len() == TextSize::from(4) { - 2 - } else { - 1 - } - } - - WideEncoding::Utf32 => 1, - } - } -} - -impl LineIndex { - pub fn new(text: &str) -> LineIndex { - let mut line_wide_chars = NoHashHashMap::default(); - let mut wide_chars = Vec::new(); - - let mut newlines = Vec::with_capacity(16); - newlines.push(TextSize::from(0)); - - let mut curr_row = 0.into(); - let mut curr_col = 0.into(); - let mut line = 0; - for c in text.chars() { - let c_len = TextSize::of(c); - curr_row += c_len; - if c == '\n' { - newlines.push(curr_row); - - // Save any utf-16 characters seen in the previous line - if !wide_chars.is_empty() { - line_wide_chars.insert(line, mem::take(&mut wide_chars)); - } - - // Prepare for processing the next line - curr_col = 0.into(); - line += 1; - continue; - } - - if !c.is_ascii() { - wide_chars.push(WideChar { start: curr_col, end: curr_col + c_len }); - } - - curr_col += c_len; - } - - // Save any utf-16 characters seen in the last line - if !wide_chars.is_empty() { - line_wide_chars.insert(line, wide_chars); - } - - LineIndex { newlines, line_wide_chars } - } - - pub fn line_col(&self, offset: TextSize) -> LineCol { - let line = self.newlines.partition_point(|&it| it <= offset) - 1; - let line_start_offset = self.newlines[line]; - let col = offset - line_start_offset; - LineCol { line: line as u32, col: col.into() } - } - - pub fn offset(&self, line_col: LineCol) -> Option<TextSize> { - self.newlines - .get(line_col.line as usize) - .map(|offset| offset + TextSize::from(line_col.col)) - } - - pub fn to_wide(&self, enc: WideEncoding, line_col: LineCol) -> WideLineCol { - let col = self.utf8_to_wide_col(enc, line_col.line, line_col.col.into()); - WideLineCol { line: line_col.line, col: col as u32 } - } - - pub fn to_utf8(&self, enc: WideEncoding, line_col: WideLineCol) -> LineCol { - let col = self.wide_to_utf8_col(enc, line_col.line, line_col.col); - LineCol { line: line_col.line, col: col.into() } - } - - pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { - let lo = self.newlines.partition_point(|&it| it < range.start()); - let hi = self.newlines.partition_point(|&it| it <= range.end()); - let all = iter::once(range.start()) - .chain(self.newlines[lo..hi].iter().copied()) - .chain(iter::once(range.end())); - - all.clone() - .zip(all.skip(1)) - .map(|(lo, hi)| TextRange::new(lo, hi)) - .filter(|it| !it.is_empty()) - } - - fn utf8_to_wide_col(&self, enc: WideEncoding, line: u32, col: TextSize) -> usize { - let mut res: usize = col.into(); - if let Some(wide_chars) = self.line_wide_chars.get(&line) { - for c in wide_chars { - if c.end <= col { - res -= usize::from(c.len()) - c.wide_len(enc); - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - } - res - } - - fn wide_to_utf8_col(&self, enc: WideEncoding, line: u32, mut col: u32) -> TextSize { - if let Some(wide_chars) = self.line_wide_chars.get(&line) { - for c in wide_chars { - if col > u32::from(c.start) { - col += u32::from(c.len()) - c.wide_len(enc) as u32; - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - } - - col.into() - } -} - -#[cfg(test)] -mod tests { - use test_utils::skip_slow_tests; - - use super::WideEncoding::{Utf16, Utf32}; - use super::*; - - #[test] - fn test_line_index() { - let text = "hello\nworld"; - let table = [ - (00, 0, 0), - (01, 0, 1), - (05, 0, 5), - (06, 1, 0), - (07, 1, 1), - (08, 1, 2), - (10, 1, 4), - (11, 1, 5), - (12, 1, 6), - ]; - - let index = LineIndex::new(text); - for (offset, line, col) in table { - assert_eq!(index.line_col(offset.into()), LineCol { line, col }); - } - - let text = "\nhello\nworld"; - let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)]; - let index = LineIndex::new(text); - for (offset, line, col) in table { - assert_eq!(index.line_col(offset.into()), LineCol { line, col }); - } - } - - #[test] - fn test_char_len() { - assert_eq!('メ'.len_utf8(), 3); - assert_eq!('メ'.len_utf16(), 1); - } - - #[test] - fn test_empty_index() { - let col_index = LineIndex::new( - " -const C: char = 'x'; -", - ); - assert_eq!(col_index.line_wide_chars.len(), 0); - } - - #[test] - fn test_every_chars() { - if skip_slow_tests() { - return; - } - - let text: String = { - let mut chars: Vec<char> = ((0 as char)..char::MAX).collect(); // Neat! - chars.extend("\n".repeat(chars.len() / 16).chars()); - let mut rng = oorandom::Rand32::new(stdx::rand::seed()); - stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize); - chars.into_iter().collect() - }; - assert!(text.contains('💩')); // Sanity check. - - let line_index = LineIndex::new(&text); - - let mut lin_col = LineCol { line: 0, col: 0 }; - let mut col_utf16 = 0; - let mut col_utf32 = 0; - for (offset, c) in text.char_indices() { - let got_offset = line_index.offset(lin_col).unwrap(); - assert_eq!(usize::from(got_offset), offset); - - let got_lin_col = line_index.line_col(got_offset); - assert_eq!(got_lin_col, lin_col); - - for enc in [Utf16, Utf32] { - let wide_lin_col = line_index.to_wide(enc, lin_col); - let got_lin_col = line_index.to_utf8(enc, wide_lin_col); - assert_eq!(got_lin_col, lin_col); - - let want_col = match enc { - Utf16 => col_utf16, - Utf32 => col_utf32, - }; - assert_eq!(wide_lin_col.col, want_col) - } - - if c == '\n' { - lin_col.line += 1; - lin_col.col = 0; - col_utf16 = 0; - col_utf32 = 0; - } else { - lin_col.col += c.len_utf8() as u32; - col_utf16 += c.len_utf16() as u32; - col_utf32 += 1; - } - } - } - - #[test] - fn test_splitlines() { - fn r(lo: u32, hi: u32) -> TextRange { - TextRange::new(lo.into(), hi.into()) - } - - let text = "a\nbb\nccc\n"; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>(); - let expected = vec![r(0, 2), r(2, 5), r(5, 9)]; - assert_eq!(actual, expected); - - let text = ""; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>(); - let expected = vec![]; - assert_eq!(actual, expected); - - let text = "\n"; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>(); - let expected = vec![r(0, 1)]; - assert_eq!(actual, expected) - } -} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 6402a84a6..73e6a920e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -9,6 +9,19 @@ use syntax::{ ted, SyntaxNode, }; +#[derive(Default)] +struct AstSubsts { + types_and_consts: Vec<TypeOrConst>, + lifetimes: Vec<ast::LifetimeArg>, +} + +enum TypeOrConst { + Either(ast::TypeArg), // indistinguishable type or const param + Const(ast::ConstArg), +} + +type LifetimeName = String; + /// `PathTransform` substitutes path in SyntaxNodes in bulk. /// /// This is mostly useful for IDE code generation. If you paste some existing @@ -34,7 +47,7 @@ use syntax::{ /// ``` pub struct PathTransform<'a> { generic_def: Option<hir::GenericDef>, - substs: Vec<ast::Type>, + substs: AstSubsts, target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>, } @@ -72,7 +85,12 @@ impl<'a> PathTransform<'a> { target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>, ) -> PathTransform<'a> { - PathTransform { source_scope, target_scope, generic_def: None, substs: Vec::new() } + PathTransform { + source_scope, + target_scope, + generic_def: None, + substs: AstSubsts::default(), + } } pub fn apply(&self, syntax: &SyntaxNode) { @@ -91,12 +109,14 @@ impl<'a> PathTransform<'a> { let target_module = self.target_scope.module(); let source_module = self.source_scope.module(); let skip = match self.generic_def { - // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky + // this is a trait impl, so we need to skip the first type parameter (i.e. Self) -- this is a bit hacky Some(hir::GenericDef::Trait(_)) => 1, _ => 0, }; - let substs_by_param: FxHashMap<_, _> = self - .generic_def + let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default(); + let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default(); + let mut default_types: Vec<hir::TypeParam> = Default::default(); + self.generic_def .into_iter() .flat_map(|it| it.type_params(db)) .skip(skip) @@ -106,51 +126,105 @@ impl<'a> PathTransform<'a> { // can still hit those trailing values and check if they actually have // a default type. If they do, go for that type from `hir` to `ast` so // the resulting change can be applied correctly. - .zip(self.substs.iter().map(Some).chain(std::iter::repeat(None))) - .filter_map(|(k, v)| match k.split(db) { - Either::Left(_) => None, - Either::Right(t) => match v { - Some(v) => Some((k, v.clone())), - None => { - let default = t.default(db)?; - Some(( - k, - ast::make::ty( - &default.display_source_code(db, source_module.into()).ok()?, - ), - )) + .zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None))) + .for_each(|(k, v)| match (k.split(db), v) { + (Either::Right(k), Some(TypeOrConst::Either(v))) => { + if let Some(ty) = v.ty() { + type_substs.insert(k, ty.clone()); + } + } + (Either::Right(k), None) => { + if let Some(default) = k.default(db) { + if let Some(default) = + &default.display_source_code(db, source_module.into(), false).ok() + { + type_substs.insert(k, ast::make::ty(default).clone_for_update()); + default_types.push(k); + } + } + } + (Either::Left(k), Some(TypeOrConst::Either(v))) => { + if let Some(ty) = v.ty() { + const_substs.insert(k, ty.syntax().clone()); } - }, - }) + } + (Either::Left(k), Some(TypeOrConst::Const(v))) => { + if let Some(expr) = v.expr() { + // FIXME: expressions in curly brackets can cause ambiguity after insertion + // (e.g. `N * 2` -> `{1 + 1} * 2`; it's unclear whether `{1 + 1}` + // is a standalone statement or a part of another expresson) + // and sometimes require slight modifications; see + // https://doc.rust-lang.org/reference/statements.html#expression-statements + const_substs.insert(k, expr.syntax().clone()); + } + } + (Either::Left(_), None) => (), // FIXME: get default const value + _ => (), // ignore mismatching params + }); + let lifetime_substs: FxHashMap<_, _> = self + .generic_def + .into_iter() + .flat_map(|it| it.lifetime_params(db)) + .zip(self.substs.lifetimes.clone()) + .filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?))) .collect(); - Ctx { substs: substs_by_param, target_module, source_scope: self.source_scope } + let ctx = Ctx { + type_substs, + const_substs, + lifetime_substs, + target_module, + source_scope: self.source_scope, + }; + ctx.transform_default_type_substs(default_types); + ctx } } struct Ctx<'a> { - substs: FxHashMap<hir::TypeOrConstParam, ast::Type>, + type_substs: FxHashMap<hir::TypeParam, ast::Type>, + const_substs: FxHashMap<hir::ConstParam, SyntaxNode>, + lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>, target_module: hir::Module, source_scope: &'a SemanticsScope<'a>, } +fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> { + item.preorder().filter_map(|event| match event { + syntax::WalkEvent::Enter(_) => None, + syntax::WalkEvent::Leave(node) => Some(node), + }) +} + impl<'a> Ctx<'a> { fn apply(&self, item: &SyntaxNode) { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = item - .preorder() - .filter_map(|event| match event { - syntax::WalkEvent::Enter(_) => None, - syntax::WalkEvent::Leave(node) => Some(node), - }) - .filter_map(ast::Path::cast) - .collect::<Vec<_>>(); - + let paths = postorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>(); for path in paths { self.transform_path(path); } + + postorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { + if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { + ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); + } + }); } + + fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) { + for k in default_types { + let v = self.type_substs.get(&k).unwrap(); + // `transform_path` may update a node's parent and that would break the + // tree traversal. Thus all paths in the tree are collected into a vec + // so that such operation is safe. + let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>(); + for path in paths { + self.transform_path(path); + } + } + } + fn transform_path(&self, path: ast::Path) -> Option<()> { if path.qualifier().is_some() { return None; @@ -167,7 +241,7 @@ impl<'a> Ctx<'a> { match resolution { hir::PathResolution::TypeParam(tp) => { - if let Some(subst) = self.substs.get(&tp.merge()) { + if let Some(subst) = self.type_substs.get(&tp) { let parent = path.syntax().parent()?; if let Some(parent) = ast::Path::cast(parent.clone()) { // Path inside path means that there is an associated @@ -234,8 +308,12 @@ impl<'a> Ctx<'a> { } ted::replace(path.syntax(), res.syntax()) } + hir::PathResolution::ConstParam(cp) => { + if let Some(subst) = self.const_substs.get(&cp) { + ted::replace(path.syntax(), subst.clone_subtree().clone_for_update()); + } + } hir::PathResolution::Local(_) - | hir::PathResolution::ConstParam(_) | hir::PathResolution::SelfType(_) | hir::PathResolution::Def(_) | hir::PathResolution::BuiltinAttr(_) @@ -248,7 +326,7 @@ impl<'a> Ctx<'a> { // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the // trait ref, and then go from the types in the substs back to the syntax). -fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> { +fn get_syntactic_substs(impl_def: ast::Impl) -> Option<AstSubsts> { let target_trait = impl_def.trait_()?; let path_type = match target_trait { ast::Type::PathType(path) => path, @@ -259,13 +337,22 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> { get_type_args_from_arg_list(generic_arg_list) } -fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<Vec<ast::Type>> { - let mut result = Vec::new(); - for generic_arg in generic_arg_list.generic_args() { - if let ast::GenericArg::TypeArg(type_arg) = generic_arg { - result.push(type_arg.ty()?) +fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<AstSubsts> { + let mut result = AstSubsts::default(); + generic_arg_list.generic_args().for_each(|generic_arg| match generic_arg { + // Const params are marked as consts on definition only, + // being passed to the trait they are indistguishable from type params; + // anyway, we don't really need to distinguish them here. + ast::GenericArg::TypeArg(type_arg) => { + result.types_and_consts.push(TypeOrConst::Either(type_arg)) } - } + // Some const values are recognized correctly. + ast::GenericArg::ConstArg(const_arg) => { + result.types_and_consts.push(TypeOrConst::Const(const_arg)); + } + ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg), + _ => (), + }); Some(result) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index f710211c8..52a23b4b8 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -178,7 +178,7 @@ fn rename_mod( let mut source_change = SourceChange::default(); - if module.is_crate_root(sema.db) { + if module.is_crate_root() { return Ok(source_change); } @@ -202,12 +202,13 @@ 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()), format!("../{new_name}"))) - } + (true, _, Some(mod_name)) => Some(( + format!("../{}", mod_name.unescaped().display(sema.db)), + format!("../{new_name}"), + )), // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().to_string(), new_name.to_string())) + Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) } _ => None, }; @@ -232,7 +233,7 @@ fn rename_mod( { source_change.insert_source_edit( file_id, - TextEdit::replace(file_range.range, new_name.to_string()), + TextEdit::replace(file_range.range, new_name.to_owned()), ) }; } @@ -442,7 +443,7 @@ fn source_edit_from_name_ref( let s = field_name.syntax().text_range().start(); let e = pat.syntax().text_range().start(); edit.delete(TextRange::new(s, e)); - edit.replace(name.syntax().text_range(), new_name.to_string()); + edit.replace(name.syntax().text_range(), new_name.to_owned()); return true; } } @@ -462,7 +463,19 @@ fn source_edit_from_def( if let Definition::Local(local) = def { let mut file_id = None; for source in local.sources(sema.db) { - let source = source.source; + let source = match source.source.clone().original_ast_node(sema.db) { + Some(source) => source, + None => match source.source.syntax().original_file_range_opt(sema.db) { + Some(FileRange { file_id: file_id2, range }) => { + file_id = Some(file_id2); + edit.replace(range, new_name.to_owned()); + continue; + } + None => { + bail!("Can't rename local that is defined in a macro declaration") + } + }, + }; file_id = source.file_id.file_id(); if let Either::Left(pat) = source.value { let name_range = pat.name().unwrap().syntax().text_range(); @@ -485,7 +498,7 @@ fn source_edit_from_def( // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 } // Foo { field: ref mut local } -> Foo { field: ref mut new_name } // ^^^^^ replace this with `new_name` - edit.replace(name_range, new_name.to_string()); + edit.replace(name_range, new_name.to_owned()); } } else { // Foo { ref mut field } -> Foo { field: ref mut new_name } @@ -495,10 +508,10 @@ fn source_edit_from_def( pat.syntax().text_range().start(), format!("{}: ", pat_field.field_name().unwrap()), ); - edit.replace(name_range, new_name.to_string()); + edit.replace(name_range, new_name.to_owned()); } } else { - edit.replace(name_range, new_name.to_string()); + edit.replace(name_range, new_name.to_owned()); } } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 12f5e4e2a..e8ff107bd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -4,17 +4,18 @@ //! get a super-set of matches. Then, we we confirm each match using precise //! name resolution. -use std::{mem, sync::Arc}; +use std::mem; use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use hir::{ AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility, }; use memchr::memmem::Finder; +use nohash_hasher::IntMap; use once_cell::unsync::Lazy; use parser::SyntaxKind; -use stdx::hash::NoHashHashMap; use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; +use triomphe::Arc; use crate::{ defs::{Definition, NameClass, NameRefClass}, @@ -24,7 +25,7 @@ use crate::{ #[derive(Debug, Default, Clone)] pub struct UsageSearchResult { - pub references: NoHashHashMap<FileId, Vec<FileReference>>, + pub references: IntMap<FileId, Vec<FileReference>>, } impl UsageSearchResult { @@ -49,7 +50,7 @@ impl UsageSearchResult { impl IntoIterator for UsageSearchResult { type Item = (FileId, Vec<FileReference>); - type IntoIter = <NoHashHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter; + type IntoIter = <IntMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.references.into_iter() @@ -83,17 +84,17 @@ pub enum ReferenceCategory { /// e.g. for things like local variables. #[derive(Clone, Debug)] pub struct SearchScope { - entries: NoHashHashMap<FileId, Option<TextRange>>, + entries: IntMap<FileId, Option<TextRange>>, } impl SearchScope { - fn new(entries: NoHashHashMap<FileId, Option<TextRange>>) -> SearchScope { + fn new(entries: IntMap<FileId, Option<TextRange>>) -> SearchScope { SearchScope { entries } } /// Build a search scope spanning the entire crate graph of files. fn crate_graph(db: &RootDatabase) -> SearchScope { - let mut entries = NoHashHashMap::default(); + let mut entries = IntMap::default(); let graph = db.crate_graph(); for krate in graph.iter() { @@ -107,7 +108,7 @@ impl SearchScope { /// Build a search scope spanning all the reverse dependencies of the given crate. fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope { - let mut entries = NoHashHashMap::default(); + let mut entries = IntMap::default(); for rev_dep in of.transitive_reverse_dependencies(db) { let root_file = rev_dep.root_file(db); let source_root_id = db.file_source_root(root_file); @@ -127,7 +128,7 @@ impl SearchScope { /// Build a search scope spanning the given module and all its submodules. fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope { - let mut entries = NoHashHashMap::default(); + let mut entries = IntMap::default(); let (file_id, range) = { let InFile { file_id, value } = module.definition_source(db); @@ -160,7 +161,7 @@ impl SearchScope { /// Build an empty search scope. pub fn empty() -> SearchScope { - SearchScope::new(NoHashHashMap::default()) + SearchScope::new(IntMap::default()) } /// Build a empty search scope spanning the given file. @@ -224,7 +225,7 @@ impl Definition { // def is crate root // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name if let &Definition::Module(module) = self { - if module.is_crate_root(db) { + if module.is_crate_root() { return SearchScope::reverse_dependencies(db, module.krate()); } } @@ -242,6 +243,8 @@ impl Definition { DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()), DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()), DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), + // FIXME: implement + DefWithBody::InTypeConst(_) => return SearchScope::empty(), }; return match def { Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)), @@ -391,7 +394,7 @@ impl<'a> FindUsages<'a> { let name = match self.def { // special case crate modules as these do not have a proper name - Definition::Module(module) if module.is_crate_root(self.sema.db) => { + Definition::Module(module) if module.is_crate_root() => { // FIXME: This assumes the crate name is always equal to its display name when it really isn't module .krate() @@ -438,11 +441,11 @@ impl<'a> FindUsages<'a> { fn scope_files<'a>( sema: &'a Semantics<'_, RootDatabase>, scope: &'a SearchScope, - ) -> impl Iterator<Item = (Arc<String>, FileId, TextRange)> + 'a { + ) -> impl Iterator<Item = (Arc<str>, FileId, TextRange)> + 'a { scope.entries.iter().map(|(&file_id, &search_range)| { let text = sema.db.file_text(file_id); let search_range = - search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str()))); + search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text))); (text, file_id, search_range) }) @@ -499,7 +502,7 @@ impl<'a> FindUsages<'a> { let scope = search_scope.intersection(&SearchScope::module_and_children(self.sema.db, module)); - let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate")); + let is_crate_root = module.is_crate_root().then(|| Finder::new("crate")); let finder = &Finder::new("super"); for (text, file_id, search_range) in scope_files(sema, &scope) { @@ -553,7 +556,7 @@ impl<'a> FindUsages<'a> { let text = sema.db.file_text(file_id); let search_range = - search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str()))); + search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text))); let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); let finder = &Finder::new("self"); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 936354f29..061fb0f05 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -5,16 +5,16 @@ use std::{collections::hash_map::Entry, iter, mem}; +use crate::SnippetCap; use base_db::{AnchoredPathBuf, FileId}; -use stdx::{hash::NoHashHashMap, never}; -use syntax::{algo, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; +use nohash_hasher::IntMap; +use stdx::never; +use syntax::{algo, ast, ted, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; use text_edit::{TextEdit, TextEditBuilder}; -use crate::SnippetCap; - #[derive(Default, Debug, Clone)] pub struct SourceChange { - pub source_file_edits: NoHashHashMap<FileId, TextEdit>, + pub source_file_edits: IntMap<FileId, TextEdit>, pub file_system_edits: Vec<FileSystemEdit>, pub is_snippet: bool, } @@ -23,7 +23,7 @@ impl SourceChange { /// Creates a new SourceChange with the given label /// from the edits. pub fn from_edits( - source_file_edits: NoHashHashMap<FileId, TextEdit>, + source_file_edits: IntMap<FileId, TextEdit>, file_system_edits: Vec<FileSystemEdit>, ) -> Self { SourceChange { source_file_edits, file_system_edits, is_snippet: false } @@ -77,8 +77,8 @@ impl Extend<FileSystemEdit> for SourceChange { } } -impl From<NoHashHashMap<FileId, TextEdit>> for SourceChange { - fn from(source_file_edits: NoHashHashMap<FileId, TextEdit>) -> SourceChange { +impl From<IntMap<FileId, TextEdit>> for SourceChange { + fn from(source_file_edits: IntMap<FileId, TextEdit>) -> SourceChange { SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } } } @@ -99,6 +99,8 @@ pub struct SourceChangeBuilder { /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. pub mutated_tree: Option<TreeMutator>, + /// Keeps track of where to place snippets + pub snippet_builder: Option<SnippetBuilder>, } pub struct TreeMutator { @@ -106,6 +108,12 @@ pub struct TreeMutator { mutable_clone: SyntaxNode, } +#[derive(Default)] +pub struct SnippetBuilder { + /// Where to place snippets at + places: Vec<PlaceSnippet>, +} + impl TreeMutator { pub fn new(immutable: &SyntaxNode) -> TreeMutator { let immutable = immutable.ancestors().last().unwrap(); @@ -131,6 +139,7 @@ impl SourceChangeBuilder { source_change: SourceChange::default(), trigger_signature_help: false, mutated_tree: None, + snippet_builder: None, } } @@ -140,6 +149,17 @@ impl SourceChangeBuilder { } fn commit(&mut self) { + // Render snippets first so that they get bundled into the tree diff + if let Some(mut snippets) = self.snippet_builder.take() { + // Last snippet always has stop index 0 + let last_stop = snippets.places.pop().unwrap(); + last_stop.place(0); + + for (index, stop) in snippets.places.into_iter().enumerate() { + stop.place(index + 1) + } + } + if let Some(tm) = self.mutated_tree.take() { algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) } @@ -161,7 +181,7 @@ impl SourceChangeBuilder { /// mutability, and different nodes in the same tree see the same mutations. /// /// The typical pattern for an assist is to find specific nodes in the read - /// phase, and then get their mutable couterparts using `make_mut` in the + /// phase, and then get their mutable counterparts using `make_mut` in the /// mutable state. pub fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) @@ -214,6 +234,30 @@ impl SourceChangeBuilder { self.trigger_signature_help = true; } + /// Adds a tabstop snippet to place the cursor before `node` + pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) { + assert!(node.syntax().parent().is_some()); + self.add_snippet(PlaceSnippet::Before(node.syntax().clone())); + } + + /// Adds a tabstop snippet to place the cursor after `node` + pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) { + assert!(node.syntax().parent().is_some()); + self.add_snippet(PlaceSnippet::After(node.syntax().clone())); + } + + /// Adds a snippet to move the cursor selected over `node` + pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { + assert!(node.syntax().parent().is_some()); + self.add_snippet(PlaceSnippet::Over(node.syntax().clone())) + } + + fn add_snippet(&mut self, snippet: PlaceSnippet) { + let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] }); + snippet_builder.places.push(snippet); + self.source_change.is_snippet = true; + } + pub fn finish(mut self) -> SourceChange { self.commit(); mem::take(&mut self.source_change) @@ -236,3 +280,66 @@ impl From<FileSystemEdit> for SourceChange { } } } + +enum PlaceSnippet { + /// Place a tabstop before a node + Before(SyntaxNode), + /// Place a tabstop before a node + After(SyntaxNode), + /// Place a placeholder snippet in place of the node + Over(SyntaxNode), +} + +impl PlaceSnippet { + /// Places the snippet before or over a node with the given tab stop index + fn place(self, order: usize) { + // ensure the target node is still attached + match &self { + PlaceSnippet::Before(node) | PlaceSnippet::After(node) | PlaceSnippet::Over(node) => { + // node should still be in the tree, but if it isn't + // then it's okay to just ignore this place + if stdx::never!(node.parent().is_none()) { + return; + } + } + } + + match self { + PlaceSnippet::Before(node) => { + ted::insert_raw(ted::Position::before(&node), Self::make_tab_stop(order)); + } + PlaceSnippet::After(node) => { + ted::insert_raw(ted::Position::after(&node), Self::make_tab_stop(order)); + } + PlaceSnippet::Over(node) => { + let position = ted::Position::before(&node); + node.detach(); + + let snippet = ast::SourceFile::parse(&format!("${{{order}:_}}")) + .syntax_node() + .clone_for_update(); + + let placeholder = + snippet.descendants().find_map(ast::UnderscoreExpr::cast).unwrap(); + ted::replace(placeholder.syntax(), node); + + ted::insert_raw(position, snippet); + } + } + } + + fn make_tab_stop(order: usize) -> SyntaxNode { + let stop = ast::SourceFile::parse(&format!("stop!(${order})")) + .syntax_node() + .descendants() + .find_map(ast::TokenTree::cast) + .unwrap() + .syntax() + .clone_for_update(); + + stop.first_token().unwrap().detach(); + stop.last_token().unwrap().detach(); + + stop + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index a91ffd1ec..b54c43b29 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -25,7 +25,6 @@ use std::{ fmt, hash::{Hash, Hasher}, mem, - sync::Arc, }; use base_db::{ @@ -40,6 +39,7 @@ use hir::{ }; use rayon::prelude::*; use rustc_hash::FxHashSet; +use triomphe::Arc; use crate::RootDatabase; @@ -98,6 +98,10 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast<dyn HirDatab /// The symbol index for a given source root within library_roots. fn library_symbols(&self, source_root_id: SourceRootId) -> Arc<SymbolIndex>; + #[salsa::transparent] + /// The symbol indices of modules that make up a given crate. + fn crate_symbols(&self, krate: Crate) -> Box<[Arc<SymbolIndex>]>; + /// The set of "local" (that is, from the current workspace) roots. /// Files in local roots are assumed to change frequently. #[salsa::input] @@ -112,26 +116,33 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast<dyn HirDatab fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Arc<SymbolIndex> { let _p = profile::span("library_symbols"); - // todo: this could be parallelized, once I figure out how to do that... - let symbols = db - .source_root_crates(source_root_id) + let mut symbol_collector = SymbolCollector::new(db.upcast()); + + db.source_root_crates(source_root_id) .iter() .flat_map(|&krate| Crate::from(krate).modules(db.upcast())) - // we specifically avoid calling SymbolsDatabase::module_symbols here, even they do the same thing, + // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing, // as the index for a library is not going to really ever change, and we do not want to store each - // module's index in salsa. - .flat_map(|module| SymbolCollector::collect(db.upcast(), module)) - .collect(); + // 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)) } fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc<SymbolIndex> { let _p = profile::span("module_symbols"); - let symbols = SymbolCollector::collect(db.upcast(), module); + + let symbols = SymbolCollector::collect_module(db.upcast(), module); Arc::new(SymbolIndex::new(symbols)) } +pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolIndex>]> { + let _p = profile::span("crate_symbols"); + krate.modules(db.upcast()).into_iter().map(|module| db.module_symbols(module)).collect() +} + /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap<DB>(DB); impl<DB: ParallelDatabase> Snap<salsa::Snapshot<DB>> { @@ -187,36 +198,21 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { .map_with(Snap::new(db), |snap, &root| snap.library_symbols(root)) .collect() } else { - let mut modules = Vec::new(); + let mut crates = Vec::new(); for &root in db.local_roots().iter() { - let crates = db.source_root_crates(root); - for &krate in crates.iter() { - modules.extend(Crate::from(krate).modules(db)); - } + crates.extend(db.source_root_crates(root).iter().copied()) } - - modules - .par_iter() - .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module)) - .collect() + let indices: Vec<_> = crates + .into_par_iter() + .map_with(Snap::new(db), |snap, krate| snap.crate_symbols(krate.into())) + .collect(); + indices.iter().flat_map(|indices| indices.iter().cloned()).collect() }; query.search(&indices) } -pub fn crate_symbols(db: &RootDatabase, krate: Crate, query: Query) -> Vec<FileSymbol> { - let _p = profile::span("crate_symbols").detail(|| format!("{query:?}")); - - let modules = krate.modules(db); - let indices: Vec<_> = modules - .par_iter() - .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module)) - .collect(); - - query.search(&indices) -} - #[derive(Default)] pub struct SymbolIndex { symbols: Vec<FileSymbol>, @@ -274,7 +270,12 @@ impl SymbolIndex { builder.insert(key, value).unwrap(); } - let map = fst::Map::new(builder.into_inner().unwrap()).unwrap(); + let map = fst::Map::new({ + let mut buf = builder.into_inner().unwrap(); + buf.shrink_to_fit(); + buf + }) + .unwrap(); SymbolIndex { symbols, map } } @@ -316,7 +317,14 @@ impl Query { let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); for symbol in &symbol_index.symbols[start..end] { - if self.only_types && !symbol.kind.is_type() { + if self.only_types + && !matches!( + symbol.def, + hir::ModuleDef::Adt(..) + | hir::ModuleDef::TypeAlias(..) + | hir::ModuleDef::BuiltinType(..) + ) + { continue; } if self.exact { @@ -418,7 +426,7 @@ struct StructInModB; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect(&db, module_id); + let mut symbols = SymbolCollector::collect_module(&db, module_id); symbols.sort_by_key(|it| it.name.clone()); (module_id, symbols) }) @@ -426,4 +434,31 @@ struct StructInModB; expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols); } + + #[test] + fn test_doc_alias() { + let (db, _) = RootDatabase::with_single_file( + r#" +#[doc(alias="s1")] +#[doc(alias="s2")] +#[doc(alias("mul1","mul2"))] +struct Struct; + +#[doc(alias="s1")] +struct Duplicate; + "#, + ); + + let symbols: Vec<_> = Crate::from(db.test_crate()) + .modules(&db) + .into_iter() + .map(|module_id| { + let mut symbols = SymbolCollector::collect_module(&db, module_id); + symbols.sort_by_key(|it| it.name.clone()); + (module_id, symbols) + }) + .collect(); + + expect_file!["./test_data/test_doc_alias.txt"].assert_debug_eq(&symbols); + } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index 2d6927cee..acf0a67de 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs @@ -92,7 +92,7 @@ pub fn lex_format_specifiers( let (_, second) = cloned.next().unwrap_or_default(); match second { '<' | '^' | '>' => { - // alignment specifier, first char specifies fillment + // alignment specifier, first char specifies fill skip_char_and_emit(&mut chars, FormatSpecifier::Fill, &mut callback); skip_char_and_emit(&mut chars, FormatSpecifier::Align, &mut callback); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs index fcef71fb7..fc2308181 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs @@ -1,7 +1,7 @@ //! Tools to work with expressions present in format string literals for the `format_args!` family of macros. //! Primarily meant for assists and completions. -/// Enum for represenging extraced format string args. +/// Enum for representing extracted format string args. /// Can either be extracted expressions (which includes identifiers), /// or placeholders `{}`. #[derive(Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs index 8bc093a85..0b0fc6693 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs @@ -60,7 +60,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; match tok.kind() { - k if is_text(k) && is_next(|it| !it.is_punct() || it == UNDERSCORE, false) => { + k if is_text(k) + && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) => + { mods.push(do_ws(after, tok)); } L_CURLY if is_next(|it| it != R_CURLY, true) => { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index a34dc1b69..22ced69d8 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -52,7 +52,9 @@ pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) } }; if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) { - if Some(node.clone()) != let_stmt.initializer().map(|it| it.syntax().clone()) { + if let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true) + && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true) + { // skipping potential const pat expressions in let statements preorder.skip_subtree(); continue; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt new file mode 100644 index 000000000..7834c6603 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt @@ -0,0 +1,216 @@ +[ + ( + Module { + id: ModuleId { + krate: Idx::<CrateData>(0), + block: None, + local_id: Idx::<ModuleData>(0), + }, + }, + [ + FileSymbol { + name: "Duplicate", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "Struct", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "mul1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "mul2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + ], + ), +] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 8c11408de..1a00e2938 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -2,9 +2,7 @@ ( Module { id: ModuleId { - krate: CrateId( - 0, - ), + krate: Idx::<CrateData>(0), block: None, local_id: Idx::<ModuleData>(0), }, @@ -12,9 +10,18 @@ [ FileSymbol { name: "Alias", + def: TypeAlias( + TypeAlias { + id: TypeAliasId( + 0, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: TYPE_ALIAS, @@ -25,14 +32,23 @@ range: 402..407, }, }, - kind: TypeAlias, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST", + def: Const( + Const { + id: ConstId( + 0, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: CONST, @@ -43,14 +59,23 @@ range: 346..351, }, }, - kind: Const, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST_WITH_INNER", + def: Const( + Const { + id: ConstId( + 2, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: CONST, @@ -61,14 +86,25 @@ range: 526..542, }, }, - kind: Const, container_name: None, + is_alias: false, }, FileSymbol { name: "Enum", + def: Adt( + Enum( + Enum { + id: EnumId( + 0, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: ENUM, @@ -79,14 +115,25 @@ range: 190..194, }, }, - kind: Enum, container_name: None, + is_alias: false, }, FileSymbol { name: "Macro", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: MACRO_DEF, @@ -97,14 +144,23 @@ range: 159..164, }, }, - kind: Macro, container_name: None, + is_alias: false, }, FileSymbol { name: "STATIC", + def: Static( + Static { + id: StaticId( + 0, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: STATIC, @@ -115,14 +171,25 @@ range: 369..375, }, }, - kind: Static, container_name: None, + is_alias: false, }, FileSymbol { name: "Struct", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: STRUCT, @@ -133,14 +200,27 @@ range: 177..183, }, }, - kind: Struct, container_name: None, + is_alias: false, }, FileSymbol { name: "StructFromMacro", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 2147483648, + hir_file_id: MacroFile( + MacroFile { + macro_call_id: MacroCallId( + 0, + ), + }, ), ptr: SyntaxNodePtr { kind: STRUCT, @@ -151,14 +231,25 @@ range: 6..21, }, }, - kind: Struct, container_name: None, + is_alias: false, }, FileSymbol { name: "StructInFn", + def: Adt( + Struct( + Struct { + id: StructId( + 4, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: STRUCT, @@ -169,16 +260,27 @@ range: 325..335, }, }, - kind: Struct, container_name: Some( "main", ), + is_alias: false, }, FileSymbol { name: "StructInNamedConst", + def: Adt( + Struct( + Struct { + id: StructId( + 5, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: STRUCT, @@ -189,16 +291,27 @@ range: 562..580, }, }, - kind: Struct, container_name: Some( "CONST_WITH_INNER", ), + is_alias: false, }, FileSymbol { name: "StructInUnnamedConst", + def: Adt( + Struct( + Struct { + id: StructId( + 6, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: STRUCT, @@ -209,14 +322,23 @@ range: 486..506, }, }, - kind: Struct, container_name: None, + is_alias: false, }, FileSymbol { name: "Trait", + def: Trait( + Trait { + id: TraitId( + 0, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: TRAIT, @@ -227,14 +349,25 @@ range: 267..272, }, }, - kind: Trait, container_name: None, + is_alias: false, }, FileSymbol { name: "Union", + def: Adt( + Union( + Union { + id: UnionId( + 0, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: UNION, @@ -245,14 +378,25 @@ range: 214..219, }, }, - kind: Union, container_name: None, + is_alias: false, }, FileSymbol { name: "a_mod", + def: Module( + Module { + id: ModuleId { + krate: Idx::<CrateData>(0), + block: None, + local_id: Idx::<ModuleData>(1), + }, + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: MODULE, @@ -263,14 +407,25 @@ range: 423..428, }, }, - kind: Module, container_name: None, + is_alias: false, }, FileSymbol { name: "b_mod", + def: Module( + Module { + id: ModuleId { + krate: Idx::<CrateData>(0), + block: None, + local_id: Idx::<ModuleData>(2), + }, + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: MODULE, @@ -281,14 +436,25 @@ range: 598..603, }, }, - kind: Module, container_name: None, + is_alias: false, }, FileSymbol { name: "define_struct", + def: Macro( + Macro { + id: MacroRulesId( + MacroRulesId( + 1, + ), + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: MACRO_RULES, @@ -299,14 +465,23 @@ range: 64..77, }, }, - kind: Macro, container_name: None, + is_alias: false, }, FileSymbol { name: "impl_fn", + def: Function( + Function { + id: FunctionId( + 2, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: FN, @@ -317,14 +492,25 @@ range: 245..252, }, }, - kind: Function, container_name: None, + is_alias: false, }, FileSymbol { name: "macro_rules_macro", + def: Macro( + Macro { + id: MacroRulesId( + MacroRulesId( + 0, + ), + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: MACRO_RULES, @@ -335,14 +521,23 @@ range: 14..31, }, }, - kind: Macro, container_name: None, + is_alias: false, }, FileSymbol { name: "main", + def: Function( + Function { + id: FunctionId( + 0, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: FN, @@ -353,14 +548,23 @@ range: 305..309, }, }, - kind: Function, container_name: None, + is_alias: false, }, FileSymbol { name: "trait_fn", + def: Function( + Function { + id: FunctionId( + 1, + ), + }, + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: FN, @@ -371,19 +575,17 @@ range: 282..290, }, }, - kind: Function, container_name: Some( "Trait", ), + is_alias: false, }, ], ), ( Module { id: ModuleId { - krate: CrateId( - 0, - ), + krate: Idx::<CrateData>(0), block: None, local_id: Idx::<ModuleData>(1), }, @@ -391,9 +593,20 @@ [ FileSymbol { name: "StructInModA", + def: Adt( + Struct( + Struct { + id: StructId( + 2, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 0, + hir_file_id: FileId( + FileId( + 0, + ), ), ptr: SyntaxNodePtr { kind: STRUCT, @@ -404,17 +617,15 @@ range: 442..454, }, }, - kind: Struct, container_name: None, + is_alias: false, }, ], ), ( Module { id: ModuleId { - krate: CrateId( - 0, - ), + krate: Idx::<CrateData>(0), block: None, local_id: Idx::<ModuleData>(2), }, @@ -422,9 +633,20 @@ [ FileSymbol { name: "StructInModB", + def: Adt( + Struct( + Struct { + id: StructId( + 3, + ), + }, + ), + ), loc: DeclarationLocation { - hir_file_id: HirFileId( - 1, + hir_file_id: FileId( + FileId( + 1, + ), ), ptr: SyntaxNodePtr { kind: STRUCT, @@ -435,8 +657,8 @@ range: 7..19, }, }, - kind: Struct, container_name: None, + is_alias: false, }, ], ), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs new file mode 100644 index 000000000..6b49bb263 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs @@ -0,0 +1,49 @@ +use line_index::{LineCol, LineIndex, WideEncoding}; +use test_utils::skip_slow_tests; + +#[test] +fn test_every_chars() { + if skip_slow_tests() { + return; + } + + let text: String = { + let mut chars: Vec<char> = ((0 as char)..char::MAX).collect(); // Neat! + chars.extend("\n".repeat(chars.len() / 16).chars()); + let mut rng = oorandom::Rand32::new(stdx::rand::seed()); + stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize); + chars.into_iter().collect() + }; + assert!(text.contains('💩')); // Sanity check. + + let line_index = LineIndex::new(&text); + + let mut lin_col = LineCol { line: 0, col: 0 }; + let mut col_utf16 = 0; + let mut col_utf32 = 0; + for (offset, c) in text.char_indices() { + let got_offset = line_index.offset(lin_col).unwrap(); + assert_eq!(usize::from(got_offset), offset); + + let got_lin_col = line_index.line_col(got_offset); + assert_eq!(got_lin_col, lin_col); + + for (enc, col) in [(WideEncoding::Utf16, col_utf16), (WideEncoding::Utf32, col_utf32)] { + let wide_lin_col = line_index.to_wide(enc, lin_col).unwrap(); + let got_lin_col = line_index.to_utf8(enc, wide_lin_col).unwrap(); + assert_eq!(got_lin_col, lin_col); + assert_eq!(wide_lin_col.col, col) + } + + if c == '\n' { + lin_col.line += 1; + lin_col.col = 0; + col_utf16 = 0; + col_utf32 = 0; + } else { + lin_col.col += c.len_utf8() as u32; + col_utf16 += c.len_utf16() as u32; + col_utf32 += 1; + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs index 6a7ea7c19..9abbc3441 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs @@ -38,15 +38,15 @@ pub fn get_missing_assoc_items( for item in imp.items(sema.db) { match item { hir::AssocItem::Function(it) => { - impl_fns_consts.insert(it.name(sema.db).to_string()); + impl_fns_consts.insert(it.name(sema.db).display(sema.db).to_string()); } hir::AssocItem::Const(it) => { if let Some(name) = it.name(sema.db) { - impl_fns_consts.insert(name.to_string()); + impl_fns_consts.insert(name.display(sema.db).to_string()); } } hir::AssocItem::TypeAlias(it) => { - impl_type.insert(it.name(sema.db).to_string()); + impl_type.insert(it.name(sema.db).display(sema.db).to_string()); } } } @@ -57,12 +57,14 @@ pub fn get_missing_assoc_items( .into_iter() .filter(|i| match i { hir::AssocItem::Function(f) => { - !impl_fns_consts.contains(&f.name(sema.db).to_string()) + !impl_fns_consts.contains(&f.name(sema.db).display(sema.db).to_string()) + } + hir::AssocItem::TypeAlias(t) => { + !impl_type.contains(&t.name(sema.db).display(sema.db).to_string()) } - hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), hir::AssocItem::Const(c) => c .name(sema.db) - .map(|n| !impl_fns_consts.contains(&n.to_string())) + .map(|n| !impl_fns_consts.contains(&n.display(sema.db).to_string())) .unwrap_or_default(), }) .collect() @@ -137,7 +139,7 @@ mod tests { sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block); let actual = match trait_ { - Some(trait_) => trait_.name(&db).to_string(), + Some(trait_) => trait_.name(&db).display(&db).to_string(), None => String::new(), }; expect.assert_eq(&actual); @@ -152,7 +154,7 @@ mod tests { let items = crate::traits::get_missing_assoc_items(&sema, &impl_block); let actual = items .into_iter() - .map(|item| item.name(&db).unwrap().to_string()) + .map(|item| item.name(&db).unwrap().display(&db).to_string()) .collect::<Vec<_>>() .join("\n"); expect.assert_eq(&actual); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs index 39431bed3..f96ea29ae 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs @@ -1,9 +1,9 @@ -//! Functionality for generating trivial contructors +//! Functionality for generating trivial constructors use hir::StructKind; use syntax::ast; -/// given a type return the trivial contructor (if one exists) +/// given a type return the trivial constructor (if one exists) pub fn use_trivial_constructor( db: &crate::RootDatabase, path: ast::Path, |