diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 8025d6398c42..417c081c6bb1 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -6,11 +6,13 @@ use rustc_hash::FxHashMap; use test_utils::{ extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER, }; +use tt::Subtree; use vfs::{file_set::FileSet, VfsPath}; use crate::{ input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Edition, Env, FileId, - FilePosition, FileRange, SourceDatabaseExt, SourceRoot, SourceRootId, + FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, + SourceDatabaseExt, SourceRoot, SourceRootId, }; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -81,7 +83,7 @@ pub struct ChangeFixture { impl ChangeFixture { pub fn parse(ra_fixture: &str) -> ChangeFixture { - let (mini_core, fixture) = Fixture::parse(ra_fixture); + let (mini_core, proc_macros, fixture) = Fixture::parse(ra_fixture); let mut change = Change::new(); let mut files = Vec::new(); @@ -203,6 +205,39 @@ impl ChangeFixture { crate_graph.add_dep(krate, CrateName::new("core").unwrap(), core_crate).unwrap(); } } + + if !proc_macros.is_empty() { + let proc_lib_file = file_id; + file_id.0 += 1; + + let mut fs = FileSet::default(); + fs.insert( + proc_lib_file, + VfsPath::new_virtual_path("/sysroot/proc_macros/lib.rs".to_string()), + ); + roots.push(SourceRoot::new_library(fs)); + + change.change_file(proc_lib_file, Some(Arc::new(String::new()))); + + let all_crates = crate_graph.crates_in_topological_order(); + + let proc_macros_crate = crate_graph.add_crate_root( + proc_lib_file, + Edition::Edition2021, + Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())), + CfgOptions::default(), + CfgOptions::default(), + Env::default(), + test_proc_macros(&proc_macros), + ); + + for krate in all_crates { + crate_graph + .add_dep(krate, CrateName::new("proc_macros").unwrap(), proc_macros_crate) + .unwrap(); + } + } + let root = match current_source_root_kind { SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)), SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)), @@ -215,6 +250,16 @@ impl ChangeFixture { } } +fn test_proc_macros(proc_macros: &[String]) -> Vec { + std::array::IntoIter::new([ProcMacro { + name: "identity".into(), + kind: crate::ProcMacroKind::Attr, + expander: Arc::new(IdentityProcMacroExpander), + }]) + .filter(|pm| proc_macros.iter().any(|name| name == &pm.name)) + .collect() +} + #[derive(Debug, Clone, Copy)] enum SourceRootKind { Local, @@ -253,3 +298,16 @@ impl From for FileMeta { } } } + +#[derive(Debug)] +pub struct IdentityProcMacroExpander; +impl ProcMacroExpander for IdentityProcMacroExpander { + fn expand( + &self, + subtree: &Subtree, + _: Option<&Subtree>, + _: &Env, + ) -> Result { + Ok(subtree.clone()) + } +} diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 5cf9158082d4..99ae418f7c11 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -78,7 +78,8 @@ impl<'a> Project<'a> { profile::init_from(crate::PROFILE); }); - let (mini_core, fixtures) = Fixture::parse(self.fixture); + let (mini_core, proc_macros, fixtures) = Fixture::parse(self.fixture); + assert!(proc_macros.is_empty()); assert!(mini_core.is_none()); for entry in fixtures { let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]); diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs index 166df3ea017f..b66065471de9 100644 --- a/crates/test_utils/src/fixture.rs +++ b/crates/test_utils/src/fixture.rs @@ -92,19 +92,32 @@ impl Fixture { /// //- other meta /// ``` /// - /// Fixture can also start with a minicore declaration: + /// Fixture can also start with a proc_macros and minicore declaration(in that order): /// /// ``` + /// //- proc_macros: identity /// //- minicore: sized /// ``` /// - /// That will include a subset of `libcore` into the fixture, see + /// That will include predefined proc macros and a subset of `libcore` into the fixture, see /// `minicore.rs` for what's available. - pub fn parse(ra_fixture: &str) -> (Option, Vec) { + pub fn parse(ra_fixture: &str) -> (Option, Vec, Vec) { let fixture = trim_indent(ra_fixture); let mut fixture = fixture.as_str(); let mut mini_core = None; let mut res: Vec = Vec::new(); + let mut test_proc_macros = vec![]; + + if fixture.starts_with("//- proc_macros:") { + let first_line = fixture.split_inclusive('\n').next().unwrap(); + test_proc_macros = first_line + .strip_prefix("//- proc_macros:") + .unwrap() + .split(',') + .map(|it| it.trim().to_string()) + .collect(); + fixture = &fixture[first_line.len()..]; + } if fixture.starts_with("//- minicore:") { let first_line = fixture.split_inclusive('\n').next().unwrap(); @@ -144,7 +157,7 @@ impl Fixture { } } - (mini_core, res) + (mini_core, test_proc_macros, res) } //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo @@ -355,13 +368,15 @@ fn parse_fixture_checks_further_indented_metadata() { #[test] fn parse_fixture_gets_full_meta() { - let (mini_core, parsed) = Fixture::parse( + let (mini_core, proc_macros, parsed) = Fixture::parse( r#" +//- proc_macros: identity //- minicore: coerce_unsized //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo mod m; "#, ); + assert_eq!(proc_macros, vec!["identity".to_string()]); assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]); assert_eq!(1, parsed.len());