ide_db/
lib.rs

1//! This crate defines the core data structure representing IDE state -- `RootDatabase`.
2//!
3//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
4
5extern crate self as ide_db;
6
7mod apply_change;
8
9pub mod active_parameter;
10pub mod assists;
11pub mod defs;
12pub mod documentation;
13pub mod famous_defs;
14pub mod helpers;
15pub mod items_locator;
16pub mod label;
17pub mod path_transform;
18pub mod prime_caches;
19pub mod ra_fixture;
20pub mod range_mapper;
21pub mod rename;
22pub mod rust_doc;
23pub mod search;
24pub mod source_change;
25pub mod symbol_index;
26pub mod text_edit;
27pub mod traits;
28pub mod ty_filter;
29pub mod use_trivial_constructor;
30
31pub mod imports {
32    pub mod import_assets;
33    pub mod insert_use;
34    pub mod merge_imports;
35}
36
37pub mod generated {
38    pub mod lints;
39}
40
41pub mod syntax_helpers {
42    pub mod format_string;
43    pub mod format_string_exprs;
44    pub mod tree_diff;
45    pub use hir::prettify_macro_expansion;
46    pub mod node_ext;
47    pub mod suggest_name;
48
49    pub use parser::LexedStr;
50}
51
52pub use hir::{ChangeWithProcMacros, EditionedFileId};
53use salsa::Durability;
54
55use std::{fmt, mem::ManuallyDrop};
56
57use base_db::{
58    CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, RootQueryDb,
59    SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, query_group,
60};
61use hir::{
62    FilePositionWrapper, FileRangeWrapper,
63    db::{DefDatabase, ExpandDatabase},
64};
65use triomphe::Arc;
66
67use crate::line_index::LineIndex;
68pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
69
70pub use ::line_index;
71
72/// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience.
73pub use base_db::{self, FxIndexMap, FxIndexSet};
74pub use span::{self, FileId};
75
76pub type FilePosition = FilePositionWrapper<FileId>;
77pub type FileRange = FileRangeWrapper<FileId>;
78
79#[salsa_macros::db]
80pub struct RootDatabase {
81    // FIXME: Revisit this commit now that we migrated to the new salsa, given we store arcs in this
82    // db directly now
83    // We use `ManuallyDrop` here because every codegen unit that contains a
84    // `&RootDatabase -> &dyn OtherDatabase` cast will instantiate its drop glue in the vtable,
85    // which duplicates `Weak::drop` and `Arc::drop` tens of thousands of times, which makes
86    // compile times of all `ide_*` and downstream crates suffer greatly.
87    storage: ManuallyDrop<salsa::Storage<Self>>,
88    files: Arc<Files>,
89    crates_map: Arc<CratesMap>,
90    nonce: Nonce,
91}
92
93impl std::panic::RefUnwindSafe for RootDatabase {}
94
95#[salsa_macros::db]
96impl salsa::Database for RootDatabase {}
97
98impl Drop for RootDatabase {
99    fn drop(&mut self) {
100        unsafe { ManuallyDrop::drop(&mut self.storage) };
101    }
102}
103
104impl Clone for RootDatabase {
105    fn clone(&self) -> Self {
106        Self {
107            storage: self.storage.clone(),
108            files: self.files.clone(),
109            crates_map: self.crates_map.clone(),
110            nonce: Nonce::new(),
111        }
112    }
113}
114
115impl fmt::Debug for RootDatabase {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        f.debug_struct("RootDatabase").finish()
118    }
119}
120
121#[salsa_macros::db]
122impl SourceDatabase for RootDatabase {
123    fn file_text(&self, file_id: vfs::FileId) -> FileText {
124        self.files.file_text(file_id)
125    }
126
127    fn set_file_text(&mut self, file_id: vfs::FileId, text: &str) {
128        let files = Arc::clone(&self.files);
129        files.set_file_text(self, file_id, text);
130    }
131
132    fn set_file_text_with_durability(
133        &mut self,
134        file_id: vfs::FileId,
135        text: &str,
136        durability: Durability,
137    ) {
138        let files = Arc::clone(&self.files);
139        files.set_file_text_with_durability(self, file_id, text, durability);
140    }
141
142    /// Source root of the file.
143    fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput {
144        self.files.source_root(source_root_id)
145    }
146
147    fn set_source_root_with_durability(
148        &mut self,
149        source_root_id: SourceRootId,
150        source_root: Arc<SourceRoot>,
151        durability: Durability,
152    ) {
153        let files = Arc::clone(&self.files);
154        files.set_source_root_with_durability(self, source_root_id, source_root, durability);
155    }
156
157    fn file_source_root(&self, id: vfs::FileId) -> FileSourceRootInput {
158        self.files.file_source_root(id)
159    }
160
161    fn set_file_source_root_with_durability(
162        &mut self,
163        id: vfs::FileId,
164        source_root_id: SourceRootId,
165        durability: Durability,
166    ) {
167        let files = Arc::clone(&self.files);
168        files.set_file_source_root_with_durability(self, id, source_root_id, durability);
169    }
170
171    fn crates_map(&self) -> Arc<CratesMap> {
172        self.crates_map.clone()
173    }
174
175    fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) {
176        (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision())
177    }
178}
179
180impl Default for RootDatabase {
181    fn default() -> RootDatabase {
182        RootDatabase::new(None)
183    }
184}
185
186impl RootDatabase {
187    pub fn new(lru_capacity: Option<u16>) -> RootDatabase {
188        let mut db = RootDatabase {
189            storage: ManuallyDrop::new(salsa::Storage::default()),
190            files: Default::default(),
191            crates_map: Default::default(),
192            nonce: Nonce::new(),
193        };
194        // This needs to be here otherwise `CrateGraphBuilder` will panic.
195        db.set_all_crates(Arc::new(Box::new([])));
196        CrateGraphBuilder::default().set_in_db(&mut db);
197        db.set_proc_macros_with_durability(Default::default(), Durability::MEDIUM);
198        _ = crate::symbol_index::LibraryRoots::builder(Default::default())
199            .durability(Durability::MEDIUM)
200            .new(&db);
201        _ = crate::symbol_index::LocalRoots::builder(Default::default())
202            .durability(Durability::MEDIUM)
203            .new(&db);
204        db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH);
205        db.update_base_query_lru_capacities(lru_capacity);
206        db
207    }
208
209    pub fn enable_proc_attr_macros(&mut self) {
210        self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
211    }
212
213    pub fn update_base_query_lru_capacities(&mut self, _lru_capacity: Option<u16>) {
214        // let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP);
215        // base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP);
216        // base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
217        // // macro expansions are usually rather small, so we can afford to keep more of them alive
218        // hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity);
219        // hir::db::BorrowckQuery.in_db_mut(self).set_lru_capacity(base_db::DEFAULT_BORROWCK_LRU_CAP);
220        // hir::db::BodyWithSourceMapQuery.in_db_mut(self).set_lru_capacity(2048);
221    }
222
223    pub fn update_lru_capacities(&mut self, _lru_capacities: &FxHashMap<Box<str>, u16>) {
224        // FIXME(salsa-transition): bring this back; allow changing LRU settings at runtime.
225        // use hir::db as hir_db;
226
227        // base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP);
228        // base_db::ParseQuery.in_db_mut(self).set_lru_capacity(
229        //     lru_capacities
230        //         .get(stringify!(ParseQuery))
231        //         .copied()
232        //         .unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP),
233        // );
234        // hir_db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(
235        //     lru_capacities
236        //         .get(stringify!(ParseMacroExpansionQuery))
237        //         .copied()
238        //         .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP),
239        // );
240        // hir_db::BorrowckQuery.in_db_mut(self).set_lru_capacity(
241        //     lru_capacities
242        //         .get(stringify!(BorrowckQuery))
243        //         .copied()
244        //         .unwrap_or(base_db::DEFAULT_BORROWCK_LRU_CAP),
245        // );
246        // hir::db::BodyWithSourceMapQuery.in_db_mut(self).set_lru_capacity(2048);
247    }
248}
249
250#[query_group::query_group]
251pub trait LineIndexDatabase: base_db::RootQueryDb {
252    #[salsa::invoke_interned(line_index)]
253    fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
254}
255
256fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
257    let text = db.file_text(file_id).text(db);
258    Arc::new(LineIndex::new(text))
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
262pub enum SymbolKind {
263    Attribute,
264    BuiltinAttr,
265    Const,
266    ConstParam,
267    Derive,
268    DeriveHelper,
269    Enum,
270    Field,
271    Function,
272    Method,
273    Impl,
274    InlineAsmRegOrRegClass,
275    Label,
276    LifetimeParam,
277    Local,
278    Macro,
279    ProcMacro,
280    Module,
281    SelfParam,
282    SelfType,
283    Static,
284    Struct,
285    ToolModule,
286    Trait,
287    TypeAlias,
288    TypeParam,
289    Union,
290    ValueParam,
291    Variant,
292}
293
294impl From<hir::MacroKind> for SymbolKind {
295    fn from(it: hir::MacroKind) -> Self {
296        match it {
297            hir::MacroKind::Declarative | hir::MacroKind::DeclarativeBuiltIn => SymbolKind::Macro,
298            hir::MacroKind::ProcMacro => SymbolKind::ProcMacro,
299            hir::MacroKind::Derive | hir::MacroKind::DeriveBuiltIn => SymbolKind::Derive,
300            hir::MacroKind::Attr | hir::MacroKind::AttrBuiltIn => SymbolKind::Attribute,
301        }
302    }
303}
304
305impl From<hir::ModuleDef> for SymbolKind {
306    fn from(it: hir::ModuleDef) -> Self {
307        match it {
308            hir::ModuleDef::Const(..) => SymbolKind::Const,
309            hir::ModuleDef::Variant(..) => SymbolKind::Variant,
310            hir::ModuleDef::Function(..) => SymbolKind::Function,
311            hir::ModuleDef::Macro(mac) if mac.is_proc_macro() => SymbolKind::ProcMacro,
312            hir::ModuleDef::Macro(..) => SymbolKind::Macro,
313            hir::ModuleDef::Module(..) => SymbolKind::Module,
314            hir::ModuleDef::Static(..) => SymbolKind::Static,
315            hir::ModuleDef::Adt(hir::Adt::Struct(..)) => SymbolKind::Struct,
316            hir::ModuleDef::Adt(hir::Adt::Enum(..)) => SymbolKind::Enum,
317            hir::ModuleDef::Adt(hir::Adt::Union(..)) => SymbolKind::Union,
318            hir::ModuleDef::Trait(..) => SymbolKind::Trait,
319            hir::ModuleDef::TypeAlias(..) => SymbolKind::TypeAlias,
320            hir::ModuleDef::BuiltinType(..) => SymbolKind::TypeAlias,
321        }
322    }
323}
324
325#[derive(Clone, Copy, Debug, PartialEq, Eq)]
326pub struct SnippetCap {
327    _private: (),
328}
329
330impl SnippetCap {
331    pub const fn new(allow_snippets: bool) -> Option<SnippetCap> {
332        if allow_snippets { Some(SnippetCap { _private: () }) } else { None }
333    }
334}
335
336pub struct Ranker<'a> {
337    pub kind: parser::SyntaxKind,
338    pub text: &'a str,
339    pub ident_kind: bool,
340}
341
342impl<'a> Ranker<'a> {
343    pub const MAX_RANK: usize = 0b1110;
344
345    pub fn from_token(token: &'a syntax::SyntaxToken) -> Self {
346        let kind = token.kind();
347        Ranker { kind, text: token.text(), ident_kind: kind.is_any_identifier() }
348    }
349
350    /// A utility function that ranks a token again a given kind and text, returning a number that
351    /// represents how close the token is to the given kind and text.
352    pub fn rank_token(&self, tok: &syntax::SyntaxToken) -> usize {
353        let tok_kind = tok.kind();
354
355        let exact_same_kind = tok_kind == self.kind;
356        let both_idents = exact_same_kind || (tok_kind.is_any_identifier() && self.ident_kind);
357        let same_text = tok.text() == self.text;
358        // anything that mapped into a token tree has likely no semantic information
359        let no_tt_parent =
360            tok.parent().is_some_and(|it| it.kind() != parser::SyntaxKind::TOKEN_TREE);
361        (both_idents as usize)
362            | ((exact_same_kind as usize) << 1)
363            | ((same_text as usize) << 2)
364            | ((no_tt_parent as usize) << 3)
365    }
366}
367
368#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
369pub enum Severity {
370    Error,
371    Warning,
372    WeakWarning,
373    Allow,
374}
375
376#[derive(Debug, Clone, Copy)]
377pub struct MiniCore<'a>(&'a str);
378
379impl<'a> MiniCore<'a> {
380    #[inline]
381    pub fn new(minicore: &'a str) -> Self {
382        Self(minicore)
383    }
384
385    #[inline]
386    pub const fn default() -> Self {
387        Self(test_utils::MiniCore::RAW_SOURCE)
388    }
389}
390
391impl<'a> Default for MiniCore<'a> {
392    #[inline]
393    fn default() -> Self {
394        Self::default()
395    }
396}