diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /src/tools/rust-analyzer/crates/base-db | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-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.toml | 8 | ||||
-rw-r--r-- | src/tools/rust-analyzer/crates/base-db/src/fixture.rs | 80 | ||||
-rw-r--r-- | src/tools/rust-analyzer/crates/base-db/src/input.rs | 229 | ||||
-rw-r--r-- | src/tools/rust-analyzer/crates/base-db/src/lib.rs | 16 | ||||
-rw-r--r-- | src/tools/rust-analyzer/crates/base-db/src/span.rs | 208 |
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)), + }), + } + } +} |