]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/base-db/src/lib.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / base-db / src / lib.rs
1 //! base_db defines basic database traits. The concrete DB is defined by ide.
2
3 #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
4
5 mod input;
6 mod change;
7 pub mod fixture;
8
9 use std::{panic, sync::Arc};
10
11 use stdx::hash::NoHashHashSet;
12 use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
13
14 pub use crate::{
15 change::Change,
16 input::{
17 CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
18 Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
19 ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId,
20 TargetLayoutLoadResult,
21 },
22 };
23 pub use salsa::{self, Cancelled};
24 pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath};
25
26 #[macro_export]
27 macro_rules! impl_intern_key {
28 ($name:ident) => {
29 impl $crate::salsa::InternKey for $name {
30 fn from_intern_id(v: $crate::salsa::InternId) -> Self {
31 $name(v)
32 }
33 fn as_intern_id(&self) -> $crate::salsa::InternId {
34 self.0
35 }
36 }
37 };
38 }
39
40 pub trait Upcast<T: ?Sized> {
41 fn upcast(&self) -> &T;
42 }
43
44 #[derive(Clone, Copy, Debug)]
45 pub struct FilePosition {
46 pub file_id: FileId,
47 pub offset: TextSize,
48 }
49
50 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
51 pub struct FileRange {
52 pub file_id: FileId,
53 pub range: TextRange,
54 }
55
56 pub const DEFAULT_LRU_CAP: usize = 128;
57
58 pub trait FileLoader {
59 /// Text of the file.
60 fn file_text(&self, file_id: FileId) -> Arc<String>;
61 fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
62 fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>>;
63 }
64
65 /// Database which stores all significant input facts: source code and project
66 /// model. Everything else in rust-analyzer is derived from these queries.
67 #[salsa::query_group(SourceDatabaseStorage)]
68 pub trait SourceDatabase: FileLoader + std::fmt::Debug {
69 // Parses the file into the syntax tree.
70 #[salsa::invoke(parse_query)]
71 fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>;
72
73 /// The crate graph.
74 #[salsa::input]
75 fn crate_graph(&self) -> Arc<CrateGraph>;
76 }
77
78 fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
79 let _p = profile::span("parse_query").detail(|| format!("{file_id:?}"));
80 let text = db.file_text(file_id);
81 SourceFile::parse(&text)
82 }
83
84 /// We don't want to give HIR knowledge of source roots, hence we extract these
85 /// methods into a separate DB.
86 #[salsa::query_group(SourceDatabaseExtStorage)]
87 pub trait SourceDatabaseExt: SourceDatabase {
88 #[salsa::input]
89 fn file_text(&self, file_id: FileId) -> Arc<String>;
90 /// Path to a file, relative to the root of its source root.
91 /// Source root of the file.
92 #[salsa::input]
93 fn file_source_root(&self, file_id: FileId) -> SourceRootId;
94 /// Contents of the source root.
95 #[salsa::input]
96 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
97
98 fn source_root_crates(&self, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>>;
99 }
100
101 fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>> {
102 let graph = db.crate_graph();
103 let res = graph
104 .iter()
105 .filter(|&krate| {
106 let root_file = graph[krate].root_file_id;
107 db.file_source_root(root_file) == id
108 })
109 .collect();
110 Arc::new(res)
111 }
112
113 /// Silly workaround for cyclic deps between the traits
114 pub struct FileLoaderDelegate<T>(pub T);
115
116 impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
117 fn file_text(&self, file_id: FileId) -> Arc<String> {
118 SourceDatabaseExt::file_text(self.0, file_id)
119 }
120 fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
121 // FIXME: this *somehow* should be platform agnostic...
122 let source_root = self.0.file_source_root(path.anchor);
123 let source_root = self.0.source_root(source_root);
124 source_root.resolve_path(path)
125 }
126
127 fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
128 let _p = profile::span("relevant_crates");
129 let source_root = self.0.file_source_root(file_id);
130 self.0.source_root_crates(source_root)
131 }
132 }