ra_ap_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, 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;
70pub use span::{self, FileId};
71
72pub type FxIndexSet<T> = indexmap::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
73pub type FxIndexMap<K, V> =
74    indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
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}
91
92impl std::panic::RefUnwindSafe for RootDatabase {}
93
94#[salsa_macros::db]
95impl salsa::Database for RootDatabase {}
96
97impl Drop for RootDatabase {
98    fn drop(&mut self) {
99        unsafe { ManuallyDrop::drop(&mut self.storage) };
100    }
101}
102
103impl Clone for RootDatabase {
104    fn clone(&self) -> Self {
105        Self {
106            storage: self.storage.clone(),
107            files: self.files.clone(),
108            crates_map: self.crates_map.clone(),
109        }
110    }
111}
112
113impl fmt::Debug for RootDatabase {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        f.debug_struct("RootDatabase").finish()
116    }
117}
118
119#[salsa_macros::db]
120impl SourceDatabase for RootDatabase {
121    fn file_text(&self, file_id: vfs::FileId) -> FileText {
122        self.files.file_text(file_id)
123    }
124
125    fn set_file_text(&mut self, file_id: vfs::FileId, text: &str) {
126        let files = Arc::clone(&self.files);
127        files.set_file_text(self, file_id, text);
128    }
129
130    fn set_file_text_with_durability(
131        &mut self,
132        file_id: vfs::FileId,
133        text: &str,
134        durability: Durability,
135    ) {
136        let files = Arc::clone(&self.files);
137        files.set_file_text_with_durability(self, file_id, text, durability);
138    }
139
140    /// Source root of the file.
141    fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput {
142        self.files.source_root(source_root_id)
143    }
144
145    fn set_source_root_with_durability(
146        &mut self,
147        source_root_id: SourceRootId,
148        source_root: Arc<SourceRoot>,
149        durability: Durability,
150    ) {
151        let files = Arc::clone(&self.files);
152        files.set_source_root_with_durability(self, source_root_id, source_root, durability);
153    }
154
155    fn file_source_root(&self, id: vfs::FileId) -> FileSourceRootInput {
156        self.files.file_source_root(id)
157    }
158
159    fn set_file_source_root_with_durability(
160        &mut self,
161        id: vfs::FileId,
162        source_root_id: SourceRootId,
163        durability: Durability,
164    ) {
165        let files = Arc::clone(&self.files);
166        files.set_file_source_root_with_durability(self, id, source_root_id, durability);
167    }
168
169    fn crates_map(&self) -> Arc<CratesMap> {
170        self.crates_map.clone()
171    }
172}
173
174impl Default for RootDatabase {
175    fn default() -> RootDatabase {
176        RootDatabase::new(None)
177    }
178}
179
180impl RootDatabase {
181    pub fn new(lru_capacity: Option<u16>) -> RootDatabase {
182        let mut db = RootDatabase {
183            storage: ManuallyDrop::new(salsa::Storage::default()),
184            files: Default::default(),
185            crates_map: Default::default(),
186        };
187        // This needs to be here otherwise `CrateGraphBuilder` will panic.
188        db.set_all_crates(Arc::new(Box::new([])));
189        CrateGraphBuilder::default().set_in_db(&mut db);
190        db.set_proc_macros_with_durability(Default::default(), Durability::MEDIUM);
191        db.set_local_roots_with_durability(Default::default(), Durability::MEDIUM);
192        db.set_library_roots_with_durability(Default::default(), Durability::MEDIUM);
193        db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH);
194        db.update_base_query_lru_capacities(lru_capacity);
195        db
196    }
197
198    pub fn enable_proc_attr_macros(&mut self) {
199        self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
200    }
201
202    pub fn update_base_query_lru_capacities(&mut self, _lru_capacity: Option<u16>) {
203        // let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP);
204        // base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP);
205        // base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
206        // // macro expansions are usually rather small, so we can afford to keep more of them alive
207        // hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity);
208        // hir::db::BorrowckQuery.in_db_mut(self).set_lru_capacity(base_db::DEFAULT_BORROWCK_LRU_CAP);
209        // hir::db::BodyWithSourceMapQuery.in_db_mut(self).set_lru_capacity(2048);
210    }
211
212    pub fn update_lru_capacities(&mut self, _lru_capacities: &FxHashMap<Box<str>, u16>) {
213        // FIXME(salsa-transition): bring this back; allow changing LRU settings at runtime.
214        // use hir::db as hir_db;
215
216        // base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP);
217        // base_db::ParseQuery.in_db_mut(self).set_lru_capacity(
218        //     lru_capacities
219        //         .get(stringify!(ParseQuery))
220        //         .copied()
221        //         .unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP),
222        // );
223        // hir_db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(
224        //     lru_capacities
225        //         .get(stringify!(ParseMacroExpansionQuery))
226        //         .copied()
227        //         .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP),
228        // );
229        // hir_db::BorrowckQuery.in_db_mut(self).set_lru_capacity(
230        //     lru_capacities
231        //         .get(stringify!(BorrowckQuery))
232        //         .copied()
233        //         .unwrap_or(base_db::DEFAULT_BORROWCK_LRU_CAP),
234        // );
235        // hir::db::BodyWithSourceMapQuery.in_db_mut(self).set_lru_capacity(2048);
236    }
237}
238
239#[query_group::query_group]
240pub trait LineIndexDatabase: base_db::RootQueryDb {
241    #[salsa::invoke_interned(line_index)]
242    fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
243}
244
245fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
246    let text = db.file_text(file_id).text(db);
247    Arc::new(LineIndex::new(&text))
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
251pub enum SymbolKind {
252    Attribute,
253    BuiltinAttr,
254    Const,
255    ConstParam,
256    Derive,
257    DeriveHelper,
258    Enum,
259    Field,
260    Function,
261    Method,
262    Impl,
263    InlineAsmRegOrRegClass,
264    Label,
265    LifetimeParam,
266    Local,
267    Macro,
268    ProcMacro,
269    Module,
270    SelfParam,
271    SelfType,
272    Static,
273    Struct,
274    ToolModule,
275    Trait,
276    TraitAlias,
277    TypeAlias,
278    TypeParam,
279    Union,
280    ValueParam,
281    Variant,
282}
283
284impl From<hir::MacroKind> for SymbolKind {
285    fn from(it: hir::MacroKind) -> Self {
286        match it {
287            hir::MacroKind::Declarative | hir::MacroKind::DeclarativeBuiltIn => SymbolKind::Macro,
288            hir::MacroKind::ProcMacro => SymbolKind::ProcMacro,
289            hir::MacroKind::Derive | hir::MacroKind::DeriveBuiltIn => SymbolKind::Derive,
290            hir::MacroKind::Attr | hir::MacroKind::AttrBuiltIn => SymbolKind::Attribute,
291        }
292    }
293}
294
295impl From<hir::ModuleDef> for SymbolKind {
296    fn from(it: hir::ModuleDef) -> Self {
297        match it {
298            hir::ModuleDef::Const(..) => SymbolKind::Const,
299            hir::ModuleDef::Variant(..) => SymbolKind::Variant,
300            hir::ModuleDef::Function(..) => SymbolKind::Function,
301            hir::ModuleDef::Macro(mac) if mac.is_proc_macro() => SymbolKind::ProcMacro,
302            hir::ModuleDef::Macro(..) => SymbolKind::Macro,
303            hir::ModuleDef::Module(..) => SymbolKind::Module,
304            hir::ModuleDef::Static(..) => SymbolKind::Static,
305            hir::ModuleDef::Adt(hir::Adt::Struct(..)) => SymbolKind::Struct,
306            hir::ModuleDef::Adt(hir::Adt::Enum(..)) => SymbolKind::Enum,
307            hir::ModuleDef::Adt(hir::Adt::Union(..)) => SymbolKind::Union,
308            hir::ModuleDef::Trait(..) => SymbolKind::Trait,
309            hir::ModuleDef::TraitAlias(..) => SymbolKind::TraitAlias,
310            hir::ModuleDef::TypeAlias(..) => SymbolKind::TypeAlias,
311            hir::ModuleDef::BuiltinType(..) => SymbolKind::TypeAlias,
312        }
313    }
314}
315
316#[derive(Clone, Copy, Debug, PartialEq, Eq)]
317pub struct SnippetCap {
318    _private: (),
319}
320
321impl SnippetCap {
322    pub const fn new(allow_snippets: bool) -> Option<SnippetCap> {
323        if allow_snippets { Some(SnippetCap { _private: () }) } else { None }
324    }
325}
326
327pub struct Ranker<'a> {
328    pub kind: parser::SyntaxKind,
329    pub text: &'a str,
330    pub ident_kind: bool,
331}
332
333impl<'a> Ranker<'a> {
334    pub const MAX_RANK: usize = 0b1110;
335
336    pub fn from_token(token: &'a syntax::SyntaxToken) -> Self {
337        let kind = token.kind();
338        Ranker { kind, text: token.text(), ident_kind: kind.is_any_identifier() }
339    }
340
341    /// A utility function that ranks a token again a given kind and text, returning a number that
342    /// represents how close the token is to the given kind and text.
343    pub fn rank_token(&self, tok: &syntax::SyntaxToken) -> usize {
344        let tok_kind = tok.kind();
345
346        let exact_same_kind = tok_kind == self.kind;
347        let both_idents = exact_same_kind || (tok_kind.is_any_identifier() && self.ident_kind);
348        let same_text = tok.text() == self.text;
349        // anything that mapped into a token tree has likely no semantic information
350        let no_tt_parent =
351            tok.parent().is_some_and(|it| it.kind() != parser::SyntaxKind::TOKEN_TREE);
352        (both_idents as usize)
353            | ((exact_same_kind as usize) << 1)
354            | ((same_text as usize) << 2)
355            | ((no_tt_parent as usize) << 3)
356    }
357}
358
359#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
360pub enum Severity {
361    Error,
362    Warning,
363    WeakWarning,
364    Allow,
365}