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