Skip to main content

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