Skip to main content

ide/
lib.rs

1//! ide crate provides "ide-centric" APIs for the rust-analyzer. That is,
2//! it generally operates with files and text ranges, and returns results as
3//! Strings, suitable for displaying to the human.
4//!
5//! What powers this API are the `RootDatabase` struct, which defines a `salsa`
6//! database, and the `hir` crate, where majority of the analysis happens.
7//! However, IDE specific bits of the analysis (most notably completion) happen
8//! in this crate.
9
10// For proving that RootDatabase is RefUnwindSafe.
11
12#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
13#![recursion_limit = "128"]
14
15#[cfg(test)]
16mod fixture;
17
18mod markup;
19mod navigation_target;
20
21mod annotations;
22mod call_hierarchy;
23mod child_modules;
24mod doc_links;
25mod expand_macro;
26mod extend_selection;
27mod fetch_crates;
28mod file_structure;
29mod folding_ranges;
30mod goto_declaration;
31mod goto_definition;
32mod goto_implementation;
33mod goto_type_definition;
34mod highlight_related;
35mod hover;
36mod inlay_hints;
37mod interpret;
38mod join_lines;
39mod markdown_remove;
40mod matching_brace;
41mod moniker;
42mod move_item;
43mod parent_module;
44mod predicate_eval;
45mod references;
46mod rename;
47mod runnables;
48mod signature_help;
49mod ssr;
50mod static_index;
51mod status;
52mod syntax_highlighting;
53mod test_explorer;
54mod typing;
55mod view_crate_graph;
56mod view_hir;
57mod view_item_tree;
58mod view_memory_layout;
59mod view_mir;
60mod view_syntax_tree;
61
62use std::panic::{AssertUnwindSafe, UnwindSafe};
63use std::time::Duration;
64
65use cfg::CfgOptions;
66use fetch_crates::CrateInfo;
67use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym};
68use ide_db::base_db::relevant_crates;
69use ide_db::base_db::salsa::Durability;
70use ide_db::line_index;
71use ide_db::ra_fixture::RaFixtureAnalysis;
72use ide_db::{
73    FxHashMap, FxIndexSet,
74    base_db::{
75        AbsPathBuf, CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath,
76        salsa::{Cancelled, Database},
77    },
78    prime_caches, symbol_index,
79};
80use macros::UpmapFromRaFixture;
81use syntax::{AstNode, SourceFile, ast};
82use triomphe::Arc;
83use view_memory_layout::{RecursiveMemoryLayout, view_memory_layout};
84
85use crate::navigation_target::ToNav;
86
87pub use crate::{
88    annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation},
89    call_hierarchy::{CallHierarchyConfig, CallItem},
90    expand_macro::ExpandedMacro,
91    file_structure::{FileStructureConfig, StructureNode, StructureNodeKind},
92    folding_ranges::{Fold, FoldKind},
93    goto_definition::GotoDefinitionConfig,
94    goto_implementation::GotoImplementationConfig,
95    highlight_related::{HighlightRelatedConfig, HighlightedRange},
96    hover::{
97        HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
98        MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, SubstTyLen,
99    },
100    inlay_hints::{
101        AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
102        GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
103        InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty,
104        LifetimeElisionHints, TypeHintsPlacement,
105    },
106    join_lines::JoinLinesConfig,
107    markup::Markup,
108    moniker::{
109        Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerKind, MonikerResult,
110        PackageInformation, SymbolInformationKind,
111    },
112    move_item::Direction,
113    navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
114    references::{FindAllRefsConfig, ReferenceSearchResult},
115    rename::{RenameConfig, RenameError},
116    runnables::{Runnable, RunnableKind, TestId, UpdateTest},
117    signature_help::SignatureHelp,
118    static_index::{
119        StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig,
120    },
121    syntax_highlighting::{
122        HighlightConfig, HlRange,
123        tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
124    },
125    test_explorer::{TestItem, TestItemKind},
126};
127pub use hir::{PredicateEvaluationResult, PredicateEvaluationStatus, Semantics};
128pub use ide_assists::{
129    Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
130};
131pub use ide_completion::{
132    CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
133    CompletionItemImport, CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet,
134    SnippetScope,
135};
136pub use ide_db::{
137    FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind,
138    assists::ExprFillDefaultMode,
139    base_db::{Crate, CrateGraphBuilder, FileChange, SourceRoot, SourceRootId},
140    documentation::Documentation,
141    label::Label,
142    line_index::{LineCol, LineIndex},
143    prime_caches::ParallelPrimeCachesProgress,
144    ra_fixture::RaFixtureConfig,
145    search::{ReferenceCategory, SearchScope},
146    source_change::{FileSystemEdit, SnippetEdit, SourceChange},
147    symbol_index::Query,
148    text_edit::{Indel, TextEdit},
149};
150pub use ide_diagnostics::{Diagnostic, DiagnosticCode, DiagnosticsConfig};
151pub use ide_ssr::SsrError;
152pub use span::Edition;
153pub use syntax::{TextRange, TextSize};
154
155pub type Cancellable<T> = Result<T, Cancelled>;
156
157/// Info associated with a text range.
158#[derive(Debug, UpmapFromRaFixture)]
159pub struct RangeInfo<T> {
160    pub range: TextRange,
161    pub info: T,
162}
163
164impl<T> RangeInfo<T> {
165    pub fn new(range: TextRange, info: T) -> RangeInfo<T> {
166        RangeInfo { range, info }
167    }
168}
169
170/// `AnalysisHost` stores the current state of the world.
171#[derive(Debug)]
172pub struct AnalysisHost {
173    db: RootDatabase,
174}
175
176impl AnalysisHost {
177    pub fn new(lru_capacity: Option<u16>) -> AnalysisHost {
178        AnalysisHost { db: RootDatabase::new(lru_capacity) }
179    }
180
181    pub fn with_database(db: RootDatabase) -> AnalysisHost {
182        AnalysisHost { db }
183    }
184
185    pub fn update_lru_capacity(&mut self, lru_capacity: Option<u16>) {
186        self.db.update_base_query_lru_capacities(lru_capacity);
187    }
188
189    pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, u16>) {
190        self.db.update_lru_capacities(lru_capacities);
191    }
192
193    /// Returns a snapshot of the current state, which you can query for
194    /// semantic information.
195    pub fn analysis(&self) -> Analysis {
196        Analysis { db: self.db.clone() }
197    }
198
199    /// Applies changes to the current state of the world. If there are
200    /// outstanding snapshots, they will be canceled.
201    pub fn apply_change(&mut self, change: ChangeWithProcMacros) -> Duration {
202        self.db.apply_change(change)
203    }
204
205    /// NB: this clears the database
206    pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes, usize)> {
207        self.db.per_query_memory_usage()
208    }
209    pub fn trigger_cancellation(&mut self) {
210        // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation
211        // the revision bump there is a reset marker for clearing fixpoint poisoning.
212        // That is `trigger_cancellation` is currently bugged wrt to cancellation.
213        // self.db.trigger_cancellation();
214        self.db.synthetic_write(Durability::LOW);
215    }
216    pub fn trigger_garbage_collection(&mut self) {
217        // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation
218        // the revision bump there is a reset marker for clearing fixpoint poisoning.
219        // That is `trigger_lru_eviction` is currently bugged wrt to cancellation.
220        // self.db.trigger_lru_eviction();
221        self.db.synthetic_write(Durability::LOW);
222        // SAFETY: `trigger_lru_eviction` triggers cancellation, so all running queries were canceled.
223        unsafe { hir::collect_ty_garbage() };
224    }
225    pub fn raw_database(&self) -> &RootDatabase {
226        &self.db
227    }
228    pub fn raw_database_mut(&mut self) -> &mut RootDatabase {
229        &mut self.db
230    }
231}
232
233impl Default for AnalysisHost {
234    fn default() -> AnalysisHost {
235        AnalysisHost::new(None)
236    }
237}
238
239/// Analysis is a snapshot of a world state at a moment in time. It is the main
240/// entry point for asking semantic information about the world. When the world
241/// state is advanced using `AnalysisHost::apply_change` method, all existing
242/// `Analysis` are canceled (most method return `Err(Canceled)`).
243#[derive(Debug)]
244pub struct Analysis {
245    db: RootDatabase,
246}
247
248// As a general design guideline, `Analysis` API are intended to be independent
249// from the language server protocol. That is, when exposing some functionality
250// we should think in terms of "what API makes most sense" and not in terms of
251// "what types LSP uses". Although currently LSP is the only consumer of the
252// API, the API should in theory be usable as a library, or via a different
253// protocol.
254impl Analysis {
255    // Creates an analysis instance for a single file, without any external
256    // dependencies, stdlib support or ability to apply changes. See
257    // `AnalysisHost` for creating a fully-featured analysis.
258    pub fn from_single_file(text: String, proc_macro_cwd: Arc<AbsPathBuf>) -> (Analysis, FileId) {
259        let mut host = AnalysisHost::default();
260        let file_id = FileId::from_raw(0);
261        let mut file_set = FileSet::default();
262        file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_owned()));
263        let source_root = SourceRoot::new_local(file_set);
264
265        let mut change = ChangeWithProcMacros::default();
266        change.set_roots(vec![source_root]);
267        let mut crate_graph = CrateGraphBuilder::default();
268        // FIXME: cfg options
269        // Default to enable test for single file.
270        let mut cfg_options = CfgOptions::default();
271
272        let crate_attrs = Vec::new();
273        cfg_options.insert_atom(sym::test);
274        crate_graph.add_crate_root(
275            file_id,
276            Edition::CURRENT,
277            None,
278            None,
279            cfg_options,
280            None,
281            Env::default(),
282            CrateOrigin::Local { repo: None, name: None },
283            crate_attrs,
284            false,
285            proc_macro_cwd,
286            Arc::new(CrateWorkspaceData {
287                target: Err("fixture has no layout".into()),
288                toolchain: None,
289            }),
290        );
291        change.change_file(file_id, Some(text));
292        change.set_crate_graph(crate_graph);
293
294        host.apply_change(change);
295        (host.analysis(), file_id)
296    }
297
298    pub(crate) fn from_ra_fixture(
299        sema: &Semantics<'_, RootDatabase>,
300        literal: ast::String,
301        expanded: &ast::String,
302        config: &RaFixtureConfig<'_>,
303    ) -> Option<(Analysis, RaFixtureAnalysis)> {
304        Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, config, &mut |_| {})
305    }
306
307    /// Like [`Analysis::from_ra_fixture()`], but also calls `on_cursor` with the cursor position.
308    pub(crate) fn from_ra_fixture_with_on_cursor(
309        sema: &Semantics<'_, RootDatabase>,
310        literal: ast::String,
311        expanded: &ast::String,
312        config: &RaFixtureConfig<'_>,
313        on_cursor: &mut dyn FnMut(TextRange),
314    ) -> Option<(Analysis, RaFixtureAnalysis)> {
315        let analysis =
316            RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, config, on_cursor)?;
317        Some((Analysis { db: analysis.db.clone() }, analysis))
318    }
319
320    /// Debug info about the current state of the analysis.
321    pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
322        self.with_db(|db| status::status(db, file_id))
323    }
324
325    pub fn source_root_id(&self, file_id: FileId) -> Cancellable<SourceRootId> {
326        self.with_db(|db| db.file_source_root(file_id).source_root_id(db))
327    }
328
329    pub fn is_local_source_root(&self, source_root_id: SourceRootId) -> Cancellable<bool> {
330        self.with_db(|db| {
331            let sr = db.source_root(source_root_id).source_root(db);
332            !sr.is_library
333        })
334    }
335
336    pub fn parallel_prime_caches<F>(&self, num_worker_threads: usize, cb: F) -> Cancellable<()>
337    where
338        F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe,
339    {
340        self.with_db(move |db| prime_caches::parallel_prime_caches(db, num_worker_threads, &cb))
341    }
342
343    /// Gets the text of the source file.
344    pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
345        self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db).clone())
346    }
347
348    /// Gets the syntax tree of the file.
349    pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> {
350        // FIXME edition
351        self.with_db(|db| {
352            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
353
354            editioned_file_id_wrapper.parse(db).tree()
355        })
356    }
357
358    /// Returns true if this file belongs to an immutable library.
359    pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
360        self.with_db(|db| {
361            let source_root = db.file_source_root(file_id).source_root_id(db);
362            db.source_root(source_root).source_root(db).is_library
363        })
364    }
365
366    /// Gets the file's `LineIndex`: data structure to convert between absolute
367    /// offsets and line/column representation.
368    pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> {
369        self.with_db(|db| line_index(db, file_id).clone())
370    }
371
372    /// Selects the next syntactic nodes encompassing the range.
373    pub fn extend_selection(&self, frange: FileRange) -> Cancellable<TextRange> {
374        self.with_db(|db| extend_selection::extend_selection(db, frange))
375    }
376
377    /// Returns position of the matching brace (all types of braces are
378    /// supported).
379    pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> {
380        self.with_db(|db| {
381            let file_id = EditionedFileId::current_edition(&self.db, position.file_id);
382            let parse = file_id.parse(db);
383            let file = parse.tree();
384            matching_brace::matching_brace(&file, position.offset)
385        })
386    }
387
388    pub fn view_syntax_tree(&self, file_id: FileId) -> Cancellable<String> {
389        self.with_db(|db| view_syntax_tree::view_syntax_tree(db, file_id))
390    }
391
392    pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
393        self.with_db(|db| view_hir::view_hir(db, position))
394    }
395
396    pub fn evaluate_predicate(
397        &self,
398        text: String,
399        position: FilePosition,
400    ) -> Cancellable<PredicateEvaluationResult> {
401        self.with_db(|db| predicate_eval::evaluate_predicate(db, text, position))
402    }
403
404    pub fn view_mir(&self, position: FilePosition) -> Cancellable<String> {
405        self.with_db(|db| view_mir::view_mir(db, position))
406    }
407
408    pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
409        self.with_db(|db| interpret::interpret(db, position))
410    }
411
412    pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
413        self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
414    }
415
416    pub fn discover_test_roots(&self) -> Cancellable<Vec<TestItem>> {
417        self.with_db(test_explorer::discover_test_roots)
418    }
419
420    pub fn discover_tests_in_crate_by_test_id(&self, crate_id: &str) -> Cancellable<Vec<TestItem>> {
421        self.with_db(|db| test_explorer::discover_tests_in_crate_by_test_id(db, crate_id))
422    }
423
424    pub fn discover_tests_in_crate(&self, crate_id: Crate) -> Cancellable<Vec<TestItem>> {
425        self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id))
426    }
427
428    pub fn discover_tests_in_file(&self, file_id: FileId) -> Cancellable<Vec<TestItem>> {
429        self.with_db(|db| test_explorer::discover_tests_in_file(db, file_id))
430    }
431
432    /// Renders the crate graph to GraphViz "dot" syntax.
433    pub fn view_crate_graph(&self, full: bool) -> Cancellable<String> {
434        self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
435    }
436
437    pub fn fetch_crates(&self) -> Cancellable<FxIndexSet<CrateInfo>> {
438        self.with_db(fetch_crates::fetch_crates)
439    }
440
441    pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
442        self.with_db(|db| expand_macro::expand_macro(db, position))
443    }
444
445    /// Returns an edit to remove all newlines in the range, cleaning up minor
446    /// stuff like trailing commas.
447    pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable<TextEdit> {
448        self.with_db(|db| {
449            let editioned_file_id_wrapper =
450                EditionedFileId::current_edition(&self.db, frange.file_id);
451            let parse = editioned_file_id_wrapper.parse(db);
452            join_lines::join_lines(config, &parse.tree(), frange.range)
453        })
454    }
455
456    /// Returns an edit which should be applied when opening a new line, fixing
457    /// up minor stuff like continuing the comment.
458    /// The edit will be a snippet (with `$0`).
459    pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
460        self.with_db(|db| typing::on_enter(db, position))
461    }
462
463    pub const SUPPORTED_TRIGGER_CHARS: &[char] = typing::TRIGGER_CHARS;
464
465    /// Returns an edit which should be applied after a character was typed.
466    ///
467    /// This is useful for some on-the-fly fixups, like adding `;` to `let =`
468    /// automatically.
469    pub fn on_char_typed(
470        &self,
471        position: FilePosition,
472        char_typed: char,
473    ) -> Cancellable<Option<SourceChange>> {
474        // Fast path to not even parse the file.
475        if !typing::TRIGGER_CHARS.contains(&char_typed) {
476            return Ok(None);
477        }
478
479        self.with_db(|db| typing::on_char_typed(db, position, char_typed))
480    }
481
482    /// Returns a tree representation of symbols in the file. Useful to draw a
483    /// file outline.
484    pub fn file_structure(
485        &self,
486        config: &FileStructureConfig,
487        file_id: FileId,
488    ) -> Cancellable<Vec<StructureNode>> {
489        // FIXME: Edition
490        self.with_db(|db| {
491            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
492            let source_file = editioned_file_id_wrapper.parse(db).tree();
493            file_structure::file_structure(&source_file, config)
494        })
495    }
496
497    /// Returns a list of the places in the file where type hints can be displayed.
498    pub fn inlay_hints(
499        &self,
500        config: &InlayHintsConfig<'_>,
501        file_id: FileId,
502        range: Option<TextRange>,
503    ) -> Cancellable<Vec<InlayHint>> {
504        self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
505    }
506    pub fn inlay_hints_resolve(
507        &self,
508        config: &InlayHintsConfig<'_>,
509        file_id: FileId,
510        resolve_range: TextRange,
511        hash: u64,
512        hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe,
513    ) -> Cancellable<Option<InlayHint>> {
514        self.with_db(|db| {
515            inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher)
516        })
517    }
518
519    /// Returns the set of folding ranges.
520    pub fn folding_ranges(&self, file_id: FileId, collapsed_text: bool) -> Cancellable<Vec<Fold>> {
521        self.with_db(|db| {
522            let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
523
524            folding_ranges::folding_ranges(
525                &editioned_file_id_wrapper.parse(db).tree(),
526                collapsed_text,
527            )
528        })
529    }
530
531    /// Fuzzy searches for a symbol.
532    pub fn symbol_search(&self, query: Query, limit: usize) -> Cancellable<Vec<NavigationTarget>> {
533        // `world_symbols` currently clones the database to run stuff in parallel, which will make any query panic
534        // if we were to attach it here.
535        Cancelled::catch(|| {
536            let symbols = symbol_index::world_symbols(&self.db, query);
537            hir::attach_db(&self.db, || {
538                symbols
539                    .into_iter()
540                    .filter_map(|s| s.try_to_nav(&Semantics::new(&self.db)))
541                    .take(limit)
542                    .map(UpmappingResult::call_site)
543                    .collect::<Vec<_>>()
544            })
545        })
546    }
547
548    /// Returns the definitions from the symbol at `position`.
549    pub fn goto_definition(
550        &self,
551        position: FilePosition,
552        config: &GotoDefinitionConfig<'_>,
553    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
554        self.with_db(|db| goto_definition::goto_definition(db, position, config))
555    }
556
557    /// Returns the declaration from the symbol at `position`.
558    pub fn goto_declaration(
559        &self,
560        position: FilePosition,
561        config: &GotoDefinitionConfig<'_>,
562    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
563        self.with_db(|db| goto_declaration::goto_declaration(db, position, config))
564    }
565
566    /// Returns the impls from the symbol at `position`.
567    pub fn goto_implementation(
568        &self,
569        config: &GotoImplementationConfig,
570        position: FilePosition,
571    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
572        self.with_db(|db| goto_implementation::goto_implementation(db, config, position))
573    }
574
575    /// Returns the type definitions for the symbol at `position`.
576    pub fn goto_type_definition(
577        &self,
578        position: FilePosition,
579    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
580        self.with_db(|db| goto_type_definition::goto_type_definition(db, position))
581    }
582
583    pub fn find_all_refs(
584        &self,
585        position: FilePosition,
586        config: &FindAllRefsConfig<'_>,
587    ) -> Cancellable<Option<Vec<ReferenceSearchResult>>> {
588        let config = AssertUnwindSafe(config);
589        self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, &config))
590    }
591
592    /// Returns a short text describing element at position.
593    pub fn hover(
594        &self,
595        config: &HoverConfig<'_>,
596        range: FileRange,
597    ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
598        self.with_db(|db| hover::hover(db, range, config))
599    }
600
601    /// Returns moniker of symbol at position.
602    pub fn moniker(
603        &self,
604        position: FilePosition,
605    ) -> Cancellable<Option<RangeInfo<Vec<moniker::MonikerResult>>>> {
606        self.with_db(|db| moniker::moniker(db, position))
607    }
608
609    /// Returns URL(s) for the documentation of the symbol under the cursor.
610    /// # Arguments
611    /// * `position` - Position in the file.
612    /// * `target_dir` - Directory where the build output is stored.
613    pub fn external_docs(
614        &self,
615        position: FilePosition,
616        target_dir: Option<&str>,
617        sysroot: Option<&str>,
618    ) -> Cancellable<doc_links::DocumentationLinks> {
619        self.with_db(|db| {
620            doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default()
621        })
622    }
623
624    /// Computes parameter information at the given position.
625    pub fn signature_help(&self, position: FilePosition) -> Cancellable<Option<SignatureHelp>> {
626        self.with_db(|db| signature_help::signature_help(db, position))
627    }
628
629    /// Computes call hierarchy candidates for the given file position.
630    pub fn call_hierarchy(
631        &self,
632        position: FilePosition,
633        config: &CallHierarchyConfig<'_>,
634    ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
635        self.with_db(|db| call_hierarchy::call_hierarchy(db, position, config))
636    }
637
638    /// Computes incoming calls for the given file position.
639    pub fn incoming_calls(
640        &self,
641        config: &CallHierarchyConfig<'_>,
642        position: FilePosition,
643    ) -> Cancellable<Option<Vec<CallItem>>> {
644        self.with_db(|db| call_hierarchy::incoming_calls(db, config, position))
645    }
646
647    /// Computes outgoing calls for the given file position.
648    pub fn outgoing_calls(
649        &self,
650        config: &CallHierarchyConfig<'_>,
651        position: FilePosition,
652    ) -> Cancellable<Option<Vec<CallItem>>> {
653        self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position))
654    }
655
656    /// Returns a `mod name;` declaration which created the current module.
657    pub fn parent_module(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
658        self.with_db(|db| parent_module::parent_module(db, position))
659    }
660
661    /// Returns vec of `mod name;` declaration which are created by the current module.
662    pub fn child_modules(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
663        self.with_db(|db| child_modules::child_modules(db, position))
664    }
665
666    /// Returns crates that this file belongs to.
667    pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
668        self.with_db(|db| parent_module::crates_for(db, file_id))
669    }
670
671    /// Returns crates that this file belongs to.
672    pub fn transitive_rev_deps(&self, crate_id: Crate) -> Cancellable<Vec<Crate>> {
673        self.with_db(|db| Vec::from_iter(crate_id.transitive_rev_deps(db)))
674    }
675
676    /// Returns crates that this file *might* belong to.
677    pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
678        self.with_db(|db| relevant_crates(db, file_id).to_vec())
679    }
680
681    /// Returns the edition of the given crate.
682    pub fn crate_edition(&self, crate_id: Crate) -> Cancellable<Edition> {
683        self.with_db(|db| crate_id.data(db).edition)
684    }
685
686    /// Returns whether the given crate is a proc macro.
687    pub fn is_proc_macro_crate(&self, crate_id: Crate) -> Cancellable<bool> {
688        self.with_db(|db| crate_id.data(db).is_proc_macro)
689    }
690
691    /// Returns true if this crate has `no_std` or `no_core` specified.
692    pub fn is_crate_no_std(&self, crate_id: Crate) -> Cancellable<bool> {
693        self.with_db(|db| crate_def_map(db, crate_id).is_no_std())
694    }
695
696    /// Returns the root file of the given crate.
697    pub fn crate_root(&self, crate_id: Crate) -> Cancellable<FileId> {
698        self.with_db(|db| crate_id.data(db).root_file_id)
699    }
700
701    /// Returns the set of possible targets to run for the current file.
702    pub fn runnables(&self, file_id: FileId) -> Cancellable<Vec<Runnable>> {
703        self.with_db(|db| runnables::runnables(db, file_id))
704    }
705
706    /// Returns the set of tests for the given file position.
707    pub fn related_tests(
708        &self,
709        position: FilePosition,
710        search_scope: Option<SearchScope>,
711    ) -> Cancellable<Vec<Runnable>> {
712        let search_scope = AssertUnwindSafe(search_scope);
713        self.with_db(|db| {
714            let _ = &search_scope;
715            runnables::related_tests(db, position, search_scope.0)
716        })
717    }
718
719    /// Computes all ranges to highlight for a given item in a file.
720    pub fn highlight_related(
721        &self,
722        config: HighlightRelatedConfig,
723        position: FilePosition,
724    ) -> Cancellable<Option<Vec<HighlightedRange>>> {
725        self.with_db(|db| {
726            highlight_related::highlight_related(&Semantics::new(db), config, position)
727        })
728    }
729
730    /// Computes syntax highlighting for the given file
731    pub fn highlight(
732        &self,
733        highlight_config: HighlightConfig<'_>,
734        file_id: FileId,
735    ) -> Cancellable<Vec<HlRange>> {
736        self.with_db(|db| syntax_highlighting::highlight(db, &highlight_config, file_id, None))
737    }
738
739    /// Computes syntax highlighting for the given file range.
740    pub fn highlight_range(
741        &self,
742        highlight_config: HighlightConfig<'_>,
743        frange: FileRange,
744    ) -> Cancellable<Vec<HlRange>> {
745        self.with_db(|db| {
746            syntax_highlighting::highlight(
747                db,
748                &highlight_config,
749                frange.file_id,
750                Some(frange.range),
751            )
752        })
753    }
754
755    /// Computes syntax highlighting for the given file.
756    pub fn highlight_as_html_with_config(
757        &self,
758        config: HighlightConfig<'_>,
759        file_id: FileId,
760        rainbow: bool,
761    ) -> Cancellable<String> {
762        self.with_db(|db| {
763            syntax_highlighting::highlight_as_html_with_config(db, &config, file_id, rainbow)
764        })
765    }
766
767    /// Computes syntax highlighting for the given file.
768    pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
769        self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
770    }
771
772    /// Computes completions at the given position.
773    pub fn completions(
774        &self,
775        config: &CompletionConfig<'_>,
776        position: FilePosition,
777        trigger_character: Option<char>,
778    ) -> Cancellable<Option<Vec<CompletionItem>>> {
779        self.with_db(|db| ide_completion::completions(db, config, position, trigger_character))
780    }
781
782    /// Resolves additional completion data at the position given.
783    pub fn resolve_completion_edits(
784        &self,
785        config: &CompletionConfig<'_>,
786        position: FilePosition,
787        imports: impl IntoIterator<Item = CompletionItemImport> + std::panic::UnwindSafe,
788    ) -> Cancellable<Vec<TextEdit>> {
789        Ok(self
790            .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))?
791            .unwrap_or_default())
792    }
793
794    /// Computes the set of parser level diagnostics for the given file.
795    pub fn syntax_diagnostics(
796        &self,
797        config: &DiagnosticsConfig,
798        file_id: FileId,
799    ) -> Cancellable<Vec<Diagnostic>> {
800        self.with_db(|db| ide_diagnostics::syntax_diagnostics(db, config, file_id))
801    }
802
803    /// Computes the set of semantic diagnostics for the given file.
804    pub fn semantic_diagnostics(
805        &self,
806        config: &DiagnosticsConfig,
807        resolve: AssistResolveStrategy,
808        file_id: FileId,
809    ) -> Cancellable<Vec<Diagnostic>> {
810        self.with_db(|db| ide_diagnostics::semantic_diagnostics(db, config, &resolve, file_id))
811    }
812
813    /// Computes the set of both syntax and semantic diagnostics for the given file.
814    pub fn full_diagnostics(
815        &self,
816        config: &DiagnosticsConfig,
817        resolve: AssistResolveStrategy,
818        file_id: FileId,
819    ) -> Cancellable<Vec<Diagnostic>> {
820        self.with_db(|db| ide_diagnostics::full_diagnostics(db, config, &resolve, file_id))
821    }
822
823    /// Convenience function to return assists + quick fixes for diagnostics
824    pub fn assists_with_fixes(
825        &self,
826        assist_config: &AssistConfig,
827        diagnostics_config: &DiagnosticsConfig,
828        resolve: AssistResolveStrategy,
829        frange: FileRange,
830    ) -> Cancellable<Vec<Assist>> {
831        let include_fixes = match &assist_config.allowed {
832            Some(it) => it.contains(&AssistKind::QuickFix),
833            None => true,
834        };
835
836        self.with_db(|db| {
837            let diagnostic_assists = if diagnostics_config.enabled && include_fixes {
838                ide_diagnostics::full_diagnostics(db, diagnostics_config, &resolve, frange.file_id)
839                    .into_iter()
840                    .flat_map(|it| it.fixes.unwrap_or_default())
841                    .filter(|it| it.target.intersect(frange.range).is_some())
842                    .collect()
843            } else {
844                Vec::new()
845            };
846            let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
847            let assists = ide_assists::assists(db, assist_config, resolve, frange);
848
849            let mut res = diagnostic_assists;
850            res.extend(ssr_assists);
851            res.extend(assists);
852
853            res
854        })
855    }
856
857    /// Returns the edit required to rename reference at the position to the new
858    /// name.
859    pub fn rename(
860        &self,
861        position: FilePosition,
862        new_name: &str,
863        config: &RenameConfig,
864    ) -> Cancellable<Result<SourceChange, RenameError>> {
865        self.with_db(|db| rename::rename(db, position, new_name, config))
866    }
867
868    pub fn prepare_rename(
869        &self,
870        position: FilePosition,
871    ) -> Cancellable<Result<RangeInfo<()>, RenameError>> {
872        self.with_db(|db| rename::prepare_rename(db, position))
873    }
874
875    pub fn will_rename_file(
876        &self,
877        file_id: FileId,
878        new_name_stem: &str,
879        config: &RenameConfig,
880    ) -> Cancellable<Option<SourceChange>> {
881        self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem, config))
882    }
883
884    pub fn structural_search_replace(
885        &self,
886        query: &str,
887        parse_only: bool,
888        resolve_context: FilePosition,
889        selections: Vec<FileRange>,
890    ) -> Cancellable<Result<SourceChange, SsrError>> {
891        self.with_db(|db| {
892            let rule: ide_ssr::SsrRule = query.parse()?;
893            let mut match_finder =
894                ide_ssr::MatchFinder::in_context(db, resolve_context, selections)?;
895            match_finder.add_rule(rule)?;
896            let edits = if parse_only { Default::default() } else { match_finder.edits() };
897            Ok(SourceChange::from_iter(edits))
898        })
899    }
900
901    pub fn annotations(
902        &self,
903        config: &AnnotationConfig<'_>,
904        file_id: FileId,
905    ) -> Cancellable<Vec<Annotation>> {
906        self.with_db(|db| annotations::annotations(db, config, file_id))
907    }
908
909    pub fn resolve_annotation(
910        &self,
911        config: &AnnotationConfig<'_>,
912        annotation: Annotation,
913    ) -> Cancellable<Annotation> {
914        self.with_db(|db| annotations::resolve_annotation(db, config, annotation))
915    }
916
917    pub fn move_item(
918        &self,
919        range: FileRange,
920        direction: Direction,
921    ) -> Cancellable<Option<TextEdit>> {
922        self.with_db(|db| move_item::move_item(db, range, direction))
923    }
924
925    pub fn get_recursive_memory_layout(
926        &self,
927        position: FilePosition,
928    ) -> Cancellable<Option<RecursiveMemoryLayout>> {
929        self.with_db(|db| view_memory_layout(db, position))
930    }
931
932    pub fn get_failed_obligations(&self, offset: TextSize, file_id: FileId) -> Cancellable<String> {
933        self.with_db(|db| {
934            let sema = Semantics::new(db);
935            let source_file = sema.parse_guess_edition(file_id);
936
937            let Some(token) = source_file.syntax().token_at_offset(offset).next() else {
938                return String::new();
939            };
940            sema.get_failed_obligations(token).unwrap_or_default()
941        })
942    }
943
944    pub fn editioned_file_id_to_vfs(&self, file_id: hir::EditionedFileId) -> FileId {
945        file_id.file_id(&self.db)
946    }
947
948    /// Performs an operation on the database that may be canceled.
949    ///
950    /// rust-analyzer needs to be able to answer semantic questions about the
951    /// code while the code is being modified. A common problem is that a
952    /// long-running query is being calculated when a new change arrives.
953    ///
954    /// We can't just apply the change immediately: this will cause the pending
955    /// query to see inconsistent state (it will observe an absence of
956    /// repeatable read). So what we do is we **cancel** all pending queries
957    /// before applying the change.
958    ///
959    /// Salsa implements cancellation by unwinding with a special value and
960    /// catching it on the API boundary.
961    fn with_db<F, T>(&self, f: F) -> Cancellable<T>
962    where
963        F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
964    {
965        // We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database.
966        hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db)))
967    }
968}
969
970#[test]
971fn analysis_is_send() {
972    fn is_send<T: Send>() {}
973    is_send::<Analysis>();
974}