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