summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/base-db
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /src/tools/rust-analyzer/crates/base-db
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/base-db')
-rw-r--r--src/tools/rust-analyzer/crates/base-db/Cargo.toml8
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/fixture.rs80
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs229
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs16
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/span.rs208
5 files changed, 459 insertions, 82 deletions
diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
index 171c113a9..393ffe155 100644
--- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
@@ -12,12 +12,10 @@ rust-version.workspace = true
doctest = false
[dependencies]
-salsa = "0.17.0-pre.2"
-rustc-hash = "1.1.0"
-
-triomphe.workspace = true
-
la-arena.workspace = true
+rust-analyzer-salsa.workspace = true
+rustc-hash.workspace = true
+triomphe.workspace = true
# local deps
cfg.workspace = true
diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
index 3f5ccb621..bfdd21555 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
@@ -8,14 +8,15 @@ use test_utils::{
ESCAPED_CURSOR_MARKER,
};
use triomphe::Arc;
-use tt::token_id::{Leaf, Subtree, TokenTree};
+use tt::{Leaf, Subtree, TokenTree};
use vfs::{file_set::FileSet, VfsPath};
use crate::{
input::{CrateName, CrateOrigin, LangCrateOrigin},
- Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
- FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
- SourceDatabaseExt, SourceRoot, SourceRootId,
+ span::SpanData,
+ Change, CrateDisplayName, CrateGraph, CrateId, Dependency, DependencyKind, Edition, Env,
+ FileId, FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
+ ProcMacros, ReleaseChannel, SourceDatabaseExt, SourceRoot, SourceRootId,
};
pub const WORKSPACE: SourceRootId = SourceRootId(0);
@@ -134,7 +135,7 @@ impl ChangeFixture {
let mut file_set = FileSet::default();
let mut current_source_root_kind = SourceRootKind::Local;
- let mut file_id = FileId(0);
+ let mut file_id = FileId::from_raw(0);
let mut roots = Vec::new();
let mut file_position = None;
@@ -209,7 +210,7 @@ impl ChangeFixture {
let path = VfsPath::new_virtual_path(meta.path);
file_set.insert(file_id, path);
files.push(file_id);
- file_id.0 += 1;
+ file_id = FileId::from_raw(file_id.index() + 1);
}
if crates.is_empty() {
@@ -237,7 +238,12 @@ impl ChangeFixture {
crate_graph
.add_dep(
from_id,
- Dependency::with_prelude(CrateName::new(&to).unwrap(), to_id, prelude),
+ Dependency::with_prelude(
+ CrateName::new(&to).unwrap(),
+ to_id,
+ prelude,
+ DependencyKind::Normal,
+ ),
)
.unwrap();
}
@@ -249,7 +255,7 @@ impl ChangeFixture {
if let Some(mini_core) = mini_core {
let core_file = file_id;
- file_id.0 += 1;
+ file_id = FileId::from_raw(file_id.index() + 1);
let mut fs = FileSet::default();
fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
@@ -275,7 +281,14 @@ impl ChangeFixture {
for krate in all_crates {
crate_graph
- .add_dep(krate, Dependency::new(CrateName::new("core").unwrap(), core_crate))
+ .add_dep(
+ krate,
+ Dependency::new(
+ CrateName::new("core").unwrap(),
+ core_crate,
+ DependencyKind::Normal,
+ ),
+ )
.unwrap();
}
}
@@ -283,7 +296,6 @@ impl ChangeFixture {
let mut proc_macros = ProcMacros::default();
if !proc_macro_names.is_empty() {
let proc_lib_file = file_id;
- file_id.0 += 1;
proc_macro_defs.extend(default_test_proc_macros());
let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs);
@@ -317,7 +329,11 @@ impl ChangeFixture {
crate_graph
.add_dep(
krate,
- Dependency::new(CrateName::new("proc_macros").unwrap(), proc_macros_crate),
+ Dependency::new(
+ CrateName::new("proc_macros").unwrap(),
+ proc_macros_crate,
+ DependencyKind::Normal,
+ ),
)
.unwrap();
}
@@ -523,10 +539,13 @@ struct IdentityProcMacroExpander;
impl ProcMacroExpander for IdentityProcMacroExpander {
fn expand(
&self,
- subtree: &Subtree,
- _: Option<&Subtree>,
+ subtree: &Subtree<SpanData>,
+ _: Option<&Subtree<SpanData>>,
_: &Env,
- ) -> Result<Subtree, ProcMacroExpansionError> {
+ _: SpanData,
+ _: SpanData,
+ _: SpanData,
+ ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
Ok(subtree.clone())
}
}
@@ -537,10 +556,13 @@ struct AttributeInputReplaceProcMacroExpander;
impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
fn expand(
&self,
- _: &Subtree,
- attrs: Option<&Subtree>,
+ _: &Subtree<SpanData>,
+ attrs: Option<&Subtree<SpanData>>,
_: &Env,
- ) -> Result<Subtree, ProcMacroExpansionError> {
+ _: SpanData,
+ _: SpanData,
+ _: SpanData,
+ ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
attrs
.cloned()
.ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
@@ -552,11 +574,14 @@ struct MirrorProcMacroExpander;
impl ProcMacroExpander for MirrorProcMacroExpander {
fn expand(
&self,
- input: &Subtree,
- _: Option<&Subtree>,
+ input: &Subtree<SpanData>,
+ _: Option<&Subtree<SpanData>>,
_: &Env,
- ) -> Result<Subtree, ProcMacroExpansionError> {
- fn traverse(input: &Subtree) -> Subtree {
+ _: SpanData,
+ _: SpanData,
+ _: SpanData,
+ ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
+ fn traverse(input: &Subtree<SpanData>) -> Subtree<SpanData> {
let mut token_trees = vec![];
for tt in input.token_trees.iter().rev() {
let tt = match tt {
@@ -579,13 +604,16 @@ struct ShortenProcMacroExpander;
impl ProcMacroExpander for ShortenProcMacroExpander {
fn expand(
&self,
- input: &Subtree,
- _: Option<&Subtree>,
+ input: &Subtree<SpanData>,
+ _: Option<&Subtree<SpanData>>,
_: &Env,
- ) -> Result<Subtree, ProcMacroExpansionError> {
+ _: SpanData,
+ _: SpanData,
+ _: SpanData,
+ ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
return Ok(traverse(input));
- fn traverse(input: &Subtree) -> Subtree {
+ fn traverse(input: &Subtree<SpanData>) -> Subtree<SpanData> {
let token_trees = input
.token_trees
.iter()
@@ -597,7 +625,7 @@ impl ProcMacroExpander for ShortenProcMacroExpander {
Subtree { delimiter: input.delimiter, token_trees }
}
- fn modify_leaf(leaf: &Leaf) -> Leaf {
+ fn modify_leaf(leaf: &Leaf<SpanData>) -> Leaf<SpanData> {
let mut leaf = leaf.clone();
match &mut leaf {
Leaf::Literal(it) => {
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index b75c7079b..c2472363a 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -13,9 +13,10 @@ use la_arena::{Arena, Idx};
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::SmolStr;
use triomphe::Arc;
-use tt::token_id::Subtree;
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
+use crate::span::SpanData;
+
// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
// then the crate for the proc-macro hasn't been build yet as the build data is missing.
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
@@ -155,6 +156,10 @@ impl CrateOrigin {
pub fn is_local(&self) -> bool {
matches!(self, CrateOrigin::Local { .. })
}
+
+ pub fn is_lib(&self) -> bool {
+ matches!(self, CrateOrigin::Library { .. })
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -238,6 +243,9 @@ impl CrateDisplayName {
}
}
+// FIXME: These should not be defined in here? Why does base db know about proc-macros
+// ProcMacroKind is used in [`fixture`], but that module probably shouldn't be in this crate either.
+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ProcMacroId(pub u32);
@@ -251,12 +259,16 @@ pub enum ProcMacroKind {
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
fn expand(
&self,
- subtree: &Subtree,
- attrs: Option<&Subtree>,
+ subtree: &tt::Subtree<SpanData>,
+ attrs: Option<&tt::Subtree<SpanData>>,
env: &Env,
- ) -> Result<Subtree, ProcMacroExpansionError>;
+ def_site: SpanData,
+ call_site: SpanData,
+ mixed_site: SpanData,
+ ) -> Result<tt::Subtree<SpanData>, ProcMacroExpansionError>;
}
+#[derive(Debug)]
pub enum ProcMacroExpansionError {
Panic(String),
/// Things like "proc macro server was killed by OOM".
@@ -318,11 +330,69 @@ pub struct CrateData {
pub dependencies: Vec<Dependency>,
pub origin: CrateOrigin,
pub is_proc_macro: bool,
- // FIXME: These things should not be per crate! These are more per workspace crate graph level things
+ // FIXME: These things should not be per crate! These are more per workspace crate graph level
+ // things. This info does need to be somewhat present though as to prevent deduplication from
+ // happening across different workspaces with different layouts.
pub target_layout: TargetLayoutLoadResult,
pub channel: Option<ReleaseChannel>,
}
+impl CrateData {
+ /// Check if [`other`] is almost equal to [`self`] ignoring `CrateOrigin` value.
+ pub fn eq_ignoring_origin_and_deps(&self, other: &CrateData, ignore_dev_deps: bool) -> bool {
+ // This method has some obscure bits. These are mostly there to be compliant with
+ // some patches. References to the patches are given.
+ if self.root_file_id != other.root_file_id {
+ return false;
+ }
+
+ if self.display_name != other.display_name {
+ return false;
+ }
+
+ if self.is_proc_macro != other.is_proc_macro {
+ return false;
+ }
+
+ if self.edition != other.edition {
+ return false;
+ }
+
+ if self.version != other.version {
+ return false;
+ }
+
+ let mut opts = self.cfg_options.difference(&other.cfg_options);
+ if let Some(it) = opts.next() {
+ // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs.
+ // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894
+ if it.to_string() != "rust_analyzer" {
+ return false;
+ }
+
+ if let Some(_) = opts.next() {
+ return false;
+ }
+ }
+
+ if self.env != other.env {
+ return false;
+ }
+
+ let slf_deps = self.dependencies.iter();
+ let other_deps = other.dependencies.iter();
+
+ if ignore_dev_deps {
+ return slf_deps
+ .clone()
+ .filter(|it| it.kind != DependencyKind::Dev)
+ .eq(other_deps.clone().filter(|it| it.kind != DependencyKind::Dev));
+ }
+
+ slf_deps.eq(other_deps)
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Edition {
Edition2015,
@@ -350,26 +420,43 @@ impl Env {
}
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum DependencyKind {
+ Normal,
+ Dev,
+ Build,
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Dependency {
pub crate_id: CrateId,
pub name: CrateName,
+ kind: DependencyKind,
prelude: bool,
}
impl Dependency {
- pub fn new(name: CrateName, crate_id: CrateId) -> Self {
- Self { name, crate_id, prelude: true }
+ pub fn new(name: CrateName, crate_id: CrateId, kind: DependencyKind) -> Self {
+ Self { name, crate_id, prelude: true, kind }
}
- pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self {
- Self { name, crate_id, prelude }
+ pub fn with_prelude(
+ name: CrateName,
+ crate_id: CrateId,
+ prelude: bool,
+ kind: DependencyKind,
+ ) -> Self {
+ Self { name, crate_id, prelude, kind }
}
/// Whether this dependency is to be added to the depending crate's extern prelude.
pub fn is_prelude(&self) -> bool {
self.prelude
}
+
+ pub fn kind(&self) -> DependencyKind {
+ self.kind
+ }
}
impl CrateGraph {
@@ -573,23 +660,46 @@ impl CrateGraph {
pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
let topo = other.crates_in_topological_order();
let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
-
for topo in topo {
let crate_data = &mut other.arena[topo];
+
crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
-
- let res = self.arena.iter().find_map(
- |(id, data)| {
- if data == crate_data {
- Some(id)
- } else {
- None
+ let res = self.arena.iter().find_map(|(id, data)| {
+ match (&data.origin, &crate_data.origin) {
+ (a, b) if a == b => {
+ if data.eq_ignoring_origin_and_deps(&crate_data, false) {
+ return Some((id, false));
+ }
+ }
+ (a @ CrateOrigin::Local { .. }, CrateOrigin::Library { .. })
+ | (a @ CrateOrigin::Library { .. }, CrateOrigin::Local { .. }) => {
+ // If the origins differ, check if the two crates are equal without
+ // considering the dev dependencies, if they are, they most likely are in
+ // different loaded workspaces which may cause issues. We keep the local
+ // version and discard the library one as the local version may have
+ // dev-dependencies that we want to keep resolving. See #15656 for more
+ // information.
+ if data.eq_ignoring_origin_and_deps(&crate_data, true) {
+ return Some((id, if a.is_local() { false } else { true }));
+ }
}
- },
- );
- if let Some(res) = res {
+ (_, _) => return None,
+ }
+
+ None
+ });
+
+ if let Some((res, should_update_lib_to_local)) = res {
id_map.insert(topo, res);
+ if should_update_lib_to_local {
+ assert!(self.arena[res].origin.is_lib());
+ assert!(crate_data.origin.is_local());
+ self.arena[res].origin = crate_data.origin.clone();
+
+ // Move local's dev dependencies into the newly-local-formerly-lib crate.
+ self.arena[res].dependencies = crate_data.dependencies.clone();
+ }
} else {
let id = self.arena.alloc(crate_data.clone());
id_map.insert(topo, id);
@@ -635,9 +745,11 @@ impl CrateGraph {
match (cfg_if, std) {
(Some(cfg_if), Some(std)) => {
self.arena[cfg_if].dependencies.clear();
- self.arena[std]
- .dependencies
- .push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
+ self.arena[std].dependencies.push(Dependency::new(
+ CrateName::new("cfg_if").unwrap(),
+ cfg_if,
+ DependencyKind::Normal,
+ ));
true
}
_ => false,
@@ -657,6 +769,8 @@ impl ops::Index<CrateId> for CrateGraph {
}
impl CrateData {
+ /// Add a dependency to `self` without checking if the dependency
+ // is existent among `self.dependencies`.
fn add_dep(&mut self, dep: Dependency) {
self.dependencies.push(dep)
}
@@ -758,7 +872,7 @@ impl fmt::Display for CyclicDependenciesError {
#[cfg(test)]
mod tests {
- use crate::CrateOrigin;
+ use crate::{CrateOrigin, DependencyKind};
use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
@@ -766,7 +880,7 @@ mod tests {
fn detect_cyclic_dependency_indirect() {
let mut graph = CrateGraph::default();
let crate1 = graph.add_crate_root(
- FileId(1u32),
+ FileId::from_raw(1u32),
Edition2018,
None,
None,
@@ -779,7 +893,7 @@ mod tests {
None,
);
let crate2 = graph.add_crate_root(
- FileId(2u32),
+ FileId::from_raw(2u32),
Edition2018,
None,
None,
@@ -792,7 +906,7 @@ mod tests {
None,
);
let crate3 = graph.add_crate_root(
- FileId(3u32),
+ FileId::from_raw(3u32),
Edition2018,
None,
None,
@@ -805,13 +919,22 @@ mod tests {
None,
);
assert!(graph
- .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
+ .add_dep(
+ crate1,
+ Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
+ )
.is_ok());
assert!(graph
- .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3))
+ .add_dep(
+ crate2,
+ Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal)
+ )
.is_ok());
assert!(graph
- .add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1))
+ .add_dep(
+ crate3,
+ Dependency::new(CrateName::new("crate1").unwrap(), crate1, DependencyKind::Normal)
+ )
.is_err());
}
@@ -819,7 +942,7 @@ mod tests {
fn detect_cyclic_dependency_direct() {
let mut graph = CrateGraph::default();
let crate1 = graph.add_crate_root(
- FileId(1u32),
+ FileId::from_raw(1u32),
Edition2018,
None,
None,
@@ -832,7 +955,7 @@ mod tests {
None,
);
let crate2 = graph.add_crate_root(
- FileId(2u32),
+ FileId::from_raw(2u32),
Edition2018,
None,
None,
@@ -845,10 +968,16 @@ mod tests {
None,
);
assert!(graph
- .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
+ .add_dep(
+ crate1,
+ Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
+ )
.is_ok());
assert!(graph
- .add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
+ .add_dep(
+ crate2,
+ Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
+ )
.is_err());
}
@@ -856,7 +985,7 @@ mod tests {
fn it_works() {
let mut graph = CrateGraph::default();
let crate1 = graph.add_crate_root(
- FileId(1u32),
+ FileId::from_raw(1u32),
Edition2018,
None,
None,
@@ -869,7 +998,7 @@ mod tests {
None,
);
let crate2 = graph.add_crate_root(
- FileId(2u32),
+ FileId::from_raw(2u32),
Edition2018,
None,
None,
@@ -882,7 +1011,7 @@ mod tests {
None,
);
let crate3 = graph.add_crate_root(
- FileId(3u32),
+ FileId::from_raw(3u32),
Edition2018,
None,
None,
@@ -895,10 +1024,16 @@ mod tests {
None,
);
assert!(graph
- .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
+ .add_dep(
+ crate1,
+ Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal)
+ )
.is_ok());
assert!(graph
- .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3))
+ .add_dep(
+ crate2,
+ Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal)
+ )
.is_ok());
}
@@ -906,7 +1041,7 @@ mod tests {
fn dashes_are_normalized() {
let mut graph = CrateGraph::default();
let crate1 = graph.add_crate_root(
- FileId(1u32),
+ FileId::from_raw(1u32),
Edition2018,
None,
None,
@@ -919,7 +1054,7 @@ mod tests {
None,
);
let crate2 = graph.add_crate_root(
- FileId(2u32),
+ FileId::from_raw(2u32),
Edition2018,
None,
None,
@@ -934,12 +1069,20 @@ mod tests {
assert!(graph
.add_dep(
crate1,
- Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
+ Dependency::new(
+ CrateName::normalize_dashes("crate-name-with-dashes"),
+ crate2,
+ DependencyKind::Normal
+ )
)
.is_ok());
assert_eq!(
graph[crate1].dependencies,
- vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2)]
+ vec![Dependency::new(
+ CrateName::new("crate_name_with_dashes").unwrap(),
+ crate2,
+ DependencyKind::Normal
+ )]
);
}
}
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index af204e44e..57e793436 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -1,10 +1,11 @@
//! base_db defines basic database traits. The concrete DB is defined by ide.
-#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
+#![warn(rust_2018_idioms, unused_lifetimes)]
mod input;
mod change;
pub mod fixture;
+pub mod span;
use std::panic;
@@ -16,9 +17,9 @@ pub use crate::{
change::Change,
input::{
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
- Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
- ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros,
- ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
+ DependencyKind, Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander,
+ ProcMacroExpansionError, ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths,
+ ProcMacros, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
},
};
pub use salsa::{self, Cancelled};
@@ -67,20 +68,19 @@ pub trait FileLoader {
/// model. Everything else in rust-analyzer is derived from these queries.
#[salsa::query_group(SourceDatabaseStorage)]
pub trait SourceDatabase: FileLoader + std::fmt::Debug {
- // Parses the file into the syntax tree.
- #[salsa::invoke(parse_query)]
+ /// Parses the file into the syntax tree.
fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>;
/// The crate graph.
#[salsa::input]
fn crate_graph(&self) -> Arc<CrateGraph>;
- /// The crate graph.
+ /// The proc macros.
#[salsa::input]
fn proc_macros(&self) -> Arc<ProcMacros>;
}
-fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
+fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
let _p = profile::span("parse_query").detail(|| format!("{file_id:?}"));
let text = db.file_text(file_id);
SourceFile::parse(&text)
diff --git a/src/tools/rust-analyzer/crates/base-db/src/span.rs b/src/tools/rust-analyzer/crates/base-db/src/span.rs
new file mode 100644
index 000000000..d8990eb7c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/base-db/src/span.rs
@@ -0,0 +1,208 @@
+//! File and span related types.
+// FIXME: This should probably be moved into its own crate.
+use std::fmt;
+
+use salsa::InternId;
+use tt::SyntaxContext;
+use vfs::FileId;
+
+pub type ErasedFileAstId = la_arena::Idx<syntax::SyntaxNodePtr>;
+
+// The first inde is always the root node's AstId
+pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId =
+ la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(0));
+
+pub type SpanData = tt::SpanData<SpanAnchor, SyntaxContextId>;
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SyntaxContextId(InternId);
+
+impl fmt::Debug for SyntaxContextId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if *self == Self::SELF_REF {
+ f.debug_tuple("SyntaxContextId")
+ .field(&{
+ #[derive(Debug)]
+ #[allow(non_camel_case_types)]
+ struct SELF_REF;
+ SELF_REF
+ })
+ .finish()
+ } else {
+ f.debug_tuple("SyntaxContextId").field(&self.0).finish()
+ }
+ }
+}
+crate::impl_intern_key!(SyntaxContextId);
+
+impl fmt::Display for SyntaxContextId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.0.as_u32())
+ }
+}
+
+impl SyntaxContext for SyntaxContextId {
+ const DUMMY: Self = Self::ROOT;
+}
+// inherent trait impls please tyvm
+impl SyntaxContextId {
+ pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) });
+ // veykril(HACK): FIXME salsa doesn't allow us fetching the id of the current input to be allocated so
+ // we need a special value that behaves as the current context.
+ pub const SELF_REF: Self =
+ SyntaxContextId(unsafe { InternId::new_unchecked(InternId::MAX - 1) });
+
+ pub fn is_root(self) -> bool {
+ self == Self::ROOT
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct SpanAnchor {
+ pub file_id: FileId,
+ pub ast_id: ErasedFileAstId,
+}
+
+impl fmt::Debug for SpanAnchor {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id.into_raw()).finish()
+ }
+}
+
+impl tt::SpanAnchor for SpanAnchor {
+ const DUMMY: Self = SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID };
+}
+
+/// Input to the analyzer is a set of files, where each file is identified by
+/// `FileId` and contains source code. However, another source of source code in
+/// Rust are macros: each macro can be thought of as producing a "temporary
+/// file". To assign an id to such a file, we use the id of the macro call that
+/// produced the file. So, a `HirFileId` is either a `FileId` (source code
+/// written by user), or a `MacroCallId` (source code produced by macro).
+///
+/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
+/// containing the call plus the offset of the macro call in the file. Note that
+/// this is a recursive definition! However, the size_of of `HirFileId` is
+/// finite (because everything bottoms out at the real `FileId`) and small
+/// (`MacroCallId` uses the location interning. You can check details here:
+/// <https://en.wikipedia.org/wiki/String_interning>).
+///
+/// The two variants are encoded in a single u32 which are differentiated by the MSB.
+/// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
+/// `MacroCallId`.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct HirFileId(u32);
+
+impl From<HirFileId> for u32 {
+ fn from(value: HirFileId) -> Self {
+ value.0
+ }
+}
+
+impl From<MacroCallId> for HirFileId {
+ fn from(value: MacroCallId) -> Self {
+ value.as_file()
+ }
+}
+
+impl fmt::Debug for HirFileId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.repr().fmt(f)
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct MacroFileId {
+ pub macro_call_id: MacroCallId,
+}
+
+/// `MacroCallId` identifies a particular macro invocation, like
+/// `println!("Hello, {}", world)`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct MacroCallId(salsa::InternId);
+crate::impl_intern_key!(MacroCallId);
+
+impl MacroCallId {
+ pub fn as_file(self) -> HirFileId {
+ MacroFileId { macro_call_id: self }.into()
+ }
+
+ pub fn as_macro_file(self) -> MacroFileId {
+ MacroFileId { macro_call_id: self }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub enum HirFileIdRepr {
+ FileId(FileId),
+ MacroFile(MacroFileId),
+}
+
+impl fmt::Debug for HirFileIdRepr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.index()).finish(),
+ Self::MacroFile(arg0) => {
+ f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish()
+ }
+ }
+ }
+}
+
+impl From<FileId> for HirFileId {
+ fn from(id: FileId) -> Self {
+ _ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
+ assert!(id.index() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.index());
+ HirFileId(id.index())
+ }
+}
+
+impl From<MacroFileId> for HirFileId {
+ fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self {
+ _ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
+ let id = id.as_u32();
+ assert!(id <= Self::MAX_HIR_FILE_ID, "MacroCallId index {} is too large", id);
+ HirFileId(id | Self::MACRO_FILE_TAG_MASK)
+ }
+}
+
+impl HirFileId {
+ const ASSERT_MAX_FILE_ID_IS_SAME: () =
+ [()][(Self::MAX_HIR_FILE_ID != FileId::MAX_FILE_ID) as usize];
+
+ const MAX_HIR_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
+ const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
+
+ #[inline]
+ pub fn is_macro(self) -> bool {
+ self.0 & Self::MACRO_FILE_TAG_MASK != 0
+ }
+
+ #[inline]
+ pub fn macro_file(self) -> Option<MacroFileId> {
+ match self.0 & Self::MACRO_FILE_TAG_MASK {
+ 0 => None,
+ _ => Some(MacroFileId {
+ macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
+ }),
+ }
+ }
+
+ #[inline]
+ pub fn file_id(self) -> Option<FileId> {
+ match self.0 & Self::MACRO_FILE_TAG_MASK {
+ 0 => Some(FileId::from_raw(self.0)),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ pub fn repr(self) -> HirFileIdRepr {
+ match self.0 & Self::MACRO_FILE_TAG_MASK {
+ 0 => HirFileIdRepr::FileId(FileId::from_raw(self.0)),
+ _ => HirFileIdRepr::MacroFile(MacroFileId {
+ macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
+ }),
+ }
+ }
+}