|  | //! A set of high-level utility fixture methods to use in tests. | 
|  | use std::{any::TypeId, mem, str::FromStr, sync}; | 
|  |  | 
|  | use base_db::{ | 
|  | Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, | 
|  | DependencyBuilder, Env, FileChange, FileSet, LangCrateOrigin, SourceDatabase, SourceRoot, | 
|  | Version, VfsPath, salsa, | 
|  | }; | 
|  | use cfg::CfgOptions; | 
|  | use hir_expand::{ | 
|  | EditionedFileId, FileRange, | 
|  | change::ChangeWithProcMacros, | 
|  | db::ExpandDatabase, | 
|  | files::FilePosition, | 
|  | proc_macro::{ | 
|  | ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder, | 
|  | }, | 
|  | quote, | 
|  | tt::{Leaf, TokenTree, TopSubtree, TopSubtreeBuilder, TtElement, TtIter}, | 
|  | }; | 
|  | use intern::{Symbol, sym}; | 
|  | use paths::AbsPathBuf; | 
|  | use rustc_hash::FxHashMap; | 
|  | use span::{Edition, FileId, Span}; | 
|  | use stdx::itertools::Itertools; | 
|  | use test_utils::{ | 
|  | CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, RangeOrOffset, | 
|  | extract_range_or_offset, | 
|  | }; | 
|  | use triomphe::Arc; | 
|  |  | 
|  | pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); | 
|  |  | 
|  | pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { | 
|  | #[track_caller] | 
|  | fn with_single_file( | 
|  | #[rust_analyzer::rust_fixture] ra_fixture: &str, | 
|  | ) -> (Self, EditionedFileId) { | 
|  | let mut db = Self::default(); | 
|  | let fixture = ChangeFixture::parse(&db, ra_fixture); | 
|  | fixture.change.apply(&mut db); | 
|  | assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture"); | 
|  | (db, fixture.files[0]) | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn with_many_files( | 
|  | #[rust_analyzer::rust_fixture] ra_fixture: &str, | 
|  | ) -> (Self, Vec<EditionedFileId>) { | 
|  | let mut db = Self::default(); | 
|  | let fixture = ChangeFixture::parse(&db, ra_fixture); | 
|  | fixture.change.apply(&mut db); | 
|  | assert!(fixture.file_position.is_none()); | 
|  | (db, fixture.files) | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self { | 
|  | let mut db = Self::default(); | 
|  | let fixture = ChangeFixture::parse(&db, ra_fixture); | 
|  | fixture.change.apply(&mut db); | 
|  | assert!(fixture.file_position.is_none()); | 
|  | db | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn with_files_extra_proc_macros( | 
|  | #[rust_analyzer::rust_fixture] ra_fixture: &str, | 
|  | proc_macros: Vec<(String, ProcMacro)>, | 
|  | ) -> Self { | 
|  | let mut db = Self::default(); | 
|  | let fixture = ChangeFixture::parse_with_proc_macros(&db, ra_fixture, proc_macros); | 
|  | fixture.change.apply(&mut db); | 
|  | assert!(fixture.file_position.is_none()); | 
|  | db | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) { | 
|  | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 
|  | let offset = range_or_offset.expect_offset(); | 
|  | (db, FilePosition { file_id, offset }) | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) { | 
|  | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 
|  | let range = range_or_offset.expect_range(); | 
|  | (db, FileRange { file_id, range }) | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn with_range_or_offset( | 
|  | #[rust_analyzer::rust_fixture] ra_fixture: &str, | 
|  | ) -> (Self, EditionedFileId, RangeOrOffset) { | 
|  | let mut db = Self::default(); | 
|  | let fixture = ChangeFixture::parse(&db, ra_fixture); | 
|  | fixture.change.apply(&mut db); | 
|  |  | 
|  | let (file_id, range_or_offset) = fixture | 
|  | .file_position | 
|  | .expect("Could not find file position in fixture. Did you forget to add an `$0`?"); | 
|  | (db, file_id, range_or_offset) | 
|  | } | 
|  |  | 
|  | fn test_crate(&self) -> Crate { | 
|  | self.all_crates().iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap() | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<DB: ExpandDatabase + SourceDatabase + Default + 'static> WithFixture for DB {} | 
|  |  | 
|  | pub struct ChangeFixture { | 
|  | pub file_position: Option<(EditionedFileId, RangeOrOffset)>, | 
|  | pub files: Vec<EditionedFileId>, | 
|  | pub change: ChangeWithProcMacros, | 
|  | } | 
|  |  | 
|  | const SOURCE_ROOT_PREFIX: &str = "/"; | 
|  |  | 
|  | impl ChangeFixture { | 
|  | pub fn parse( | 
|  | db: &dyn salsa::Database, | 
|  | #[rust_analyzer::rust_fixture] ra_fixture: &str, | 
|  | ) -> ChangeFixture { | 
|  | Self::parse_with_proc_macros(db, ra_fixture, Vec::new()) | 
|  | } | 
|  |  | 
|  | pub fn parse_with_proc_macros( | 
|  | db: &dyn salsa::Database, | 
|  | #[rust_analyzer::rust_fixture] ra_fixture: &str, | 
|  | mut proc_macro_defs: Vec<(String, ProcMacro)>, | 
|  | ) -> ChangeFixture { | 
|  | let FixtureWithProjectMeta { | 
|  | fixture, | 
|  | mini_core, | 
|  | proc_macro_names, | 
|  | toolchain, | 
|  | target_data_layout, | 
|  | } = FixtureWithProjectMeta::parse(ra_fixture); | 
|  | let target_data_layout = Ok(target_data_layout.into()); | 
|  | let toolchain = Some({ | 
|  | let channel = toolchain.as_deref().unwrap_or("stable"); | 
|  | Version::parse(&format!("1.76.0-{channel}")).unwrap() | 
|  | }); | 
|  | let mut source_change = FileChange::default(); | 
|  |  | 
|  | let mut files = Vec::new(); | 
|  | let mut crate_graph = CrateGraphBuilder::default(); | 
|  | let mut crates = FxHashMap::default(); | 
|  | let mut crate_deps = Vec::new(); | 
|  | let mut default_crate_root: Option<FileId> = None; | 
|  | let mut default_edition = Edition::CURRENT; | 
|  | let mut default_cfg = CfgOptions::default(); | 
|  | let mut default_env = Env::from_iter([( | 
|  | String::from("__ra_is_test_fixture"), | 
|  | String::from("__ra_is_test_fixture"), | 
|  | )]); | 
|  |  | 
|  | let mut file_set = FileSet::default(); | 
|  | let mut current_source_root_kind = SourceRootKind::Local; | 
|  | let mut file_id = FileId::from_raw(0); | 
|  | let mut roots = Vec::new(); | 
|  |  | 
|  | let mut file_position = None; | 
|  |  | 
|  | let crate_ws_data = | 
|  | Arc::new(CrateWorkspaceData { data_layout: target_data_layout, toolchain }); | 
|  |  | 
|  | // FIXME: This is less than ideal | 
|  | let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())); | 
|  |  | 
|  | for entry in fixture { | 
|  | let mut range_or_offset = None; | 
|  | let text = if entry.text.contains(CURSOR_MARKER) { | 
|  | if entry.text.contains(ESCAPED_CURSOR_MARKER) { | 
|  | entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER) | 
|  | } else { | 
|  | let (roo, text) = extract_range_or_offset(&entry.text); | 
|  | assert!(file_position.is_none()); | 
|  | range_or_offset = Some(roo); | 
|  | text | 
|  | } | 
|  | } else { | 
|  | entry.text.as_str().into() | 
|  | }; | 
|  |  | 
|  | let meta = FileMeta::from_fixture(entry, current_source_root_kind); | 
|  | if let Some(range_or_offset) = range_or_offset { | 
|  | file_position = | 
|  | Some((EditionedFileId::new(db, file_id, meta.edition), range_or_offset)); | 
|  | } | 
|  |  | 
|  | assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX)); | 
|  | if !meta.deps.is_empty() { | 
|  | assert!(meta.krate.is_some(), "can't specify deps without naming the crate") | 
|  | } | 
|  |  | 
|  | if let Some(kind) = meta.introduce_new_source_root { | 
|  | assert!( | 
|  | meta.krate.is_some(), | 
|  | "new_source_root meta doesn't make sense without crate meta" | 
|  | ); | 
|  | let prev_kind = mem::replace(&mut current_source_root_kind, kind); | 
|  | let prev_root = match prev_kind { | 
|  | SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)), | 
|  | SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)), | 
|  | }; | 
|  | roots.push(prev_root); | 
|  | } | 
|  |  | 
|  | if let Some((krate, origin, version)) = meta.krate { | 
|  | let crate_name = CrateName::normalize_dashes(&krate); | 
|  | let crate_id = crate_graph.add_crate_root( | 
|  | file_id, | 
|  | meta.edition, | 
|  | Some(crate_name.clone().into()), | 
|  | version, | 
|  | meta.cfg.clone(), | 
|  | Some(meta.cfg), | 
|  | meta.env, | 
|  | origin, | 
|  | false, | 
|  | proc_macro_cwd.clone(), | 
|  | crate_ws_data.clone(), | 
|  | ); | 
|  | let prev = crates.insert(crate_name.clone(), crate_id); | 
|  | assert!(prev.is_none(), "multiple crates with same name: {crate_name}"); | 
|  | for dep in meta.deps { | 
|  | let prelude = match &meta.extern_prelude { | 
|  | Some(v) => v.contains(&dep), | 
|  | None => true, | 
|  | }; | 
|  | let dep = CrateName::normalize_dashes(&dep); | 
|  | crate_deps.push((crate_name.clone(), dep, prelude)) | 
|  | } | 
|  | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { | 
|  | assert!(default_crate_root.is_none()); | 
|  | default_crate_root = Some(file_id); | 
|  | default_edition = meta.edition; | 
|  | default_cfg.extend(meta.cfg.into_iter()); | 
|  | default_env.extend_from_other(&meta.env); | 
|  | } | 
|  |  | 
|  | source_change.change_file(file_id, Some(text)); | 
|  | let path = VfsPath::new_virtual_path(meta.path); | 
|  | file_set.insert(file_id, path); | 
|  | files.push(EditionedFileId::new(db, file_id, meta.edition)); | 
|  | file_id = FileId::from_raw(file_id.index() + 1); | 
|  | } | 
|  |  | 
|  | if crates.is_empty() { | 
|  | let crate_root = default_crate_root | 
|  | .expect("missing default crate root, specify a main.rs or lib.rs"); | 
|  | crate_graph.add_crate_root( | 
|  | crate_root, | 
|  | default_edition, | 
|  | Some(CrateName::new("ra_test_fixture").unwrap().into()), | 
|  | None, | 
|  | default_cfg.clone(), | 
|  | Some(default_cfg), | 
|  | default_env, | 
|  | CrateOrigin::Local { repo: None, name: None }, | 
|  | false, | 
|  | proc_macro_cwd.clone(), | 
|  | crate_ws_data.clone(), | 
|  | ); | 
|  | } else { | 
|  | for (from, to, prelude) in crate_deps { | 
|  | let from_id = crates[&from]; | 
|  | let to_id = crates[&to]; | 
|  | let sysroot = crate_graph[to_id].basic.origin.is_lang(); | 
|  | crate_graph | 
|  | .add_dep( | 
|  | from_id, | 
|  | DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot), | 
|  | ) | 
|  | .unwrap(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if let Some(mini_core) = mini_core { | 
|  | let core_file = file_id; | 
|  | 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_owned())); | 
|  | roots.push(SourceRoot::new_library(fs)); | 
|  |  | 
|  | source_change.change_file(core_file, Some(mini_core.source_code())); | 
|  |  | 
|  | let all_crates = crate_graph.iter().collect::<Vec<_>>(); | 
|  |  | 
|  | let core_crate = crate_graph.add_crate_root( | 
|  | core_file, | 
|  | Edition::CURRENT, | 
|  | Some(CrateDisplayName::from_canonical_name("core")), | 
|  | None, | 
|  | Default::default(), | 
|  | Default::default(), | 
|  | Env::from_iter([( | 
|  | String::from("__ra_is_test_fixture"), | 
|  | String::from("__ra_is_test_fixture"), | 
|  | )]), | 
|  | CrateOrigin::Lang(LangCrateOrigin::Core), | 
|  | false, | 
|  | proc_macro_cwd.clone(), | 
|  | crate_ws_data.clone(), | 
|  | ); | 
|  |  | 
|  | for krate in all_crates { | 
|  | crate_graph | 
|  | .add_dep( | 
|  | krate, | 
|  | DependencyBuilder::with_prelude( | 
|  | CrateName::new("core").unwrap(), | 
|  | core_crate, | 
|  | true, | 
|  | true, | 
|  | ), | 
|  | ) | 
|  | .unwrap(); | 
|  | } | 
|  | } | 
|  |  | 
|  | let mut proc_macros = ProcMacrosBuilder::default(); | 
|  | if !proc_macro_names.is_empty() { | 
|  | let proc_lib_file = file_id; | 
|  |  | 
|  | proc_macro_defs.extend(default_test_proc_macros()); | 
|  | let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs); | 
|  | let mut fs = FileSet::default(); | 
|  | fs.insert( | 
|  | proc_lib_file, | 
|  | VfsPath::new_virtual_path("/sysroot/proc_macros/lib.rs".to_owned()), | 
|  | ); | 
|  | roots.push(SourceRoot::new_library(fs)); | 
|  |  | 
|  | source_change.change_file(proc_lib_file, Some(source)); | 
|  |  | 
|  | let all_crates = crate_graph.iter().collect::<Vec<_>>(); | 
|  |  | 
|  | let proc_macros_crate = crate_graph.add_crate_root( | 
|  | proc_lib_file, | 
|  | Edition::CURRENT, | 
|  | Some(CrateDisplayName::from_canonical_name("proc_macros")), | 
|  | None, | 
|  | Default::default(), | 
|  | Default::default(), | 
|  | Env::from_iter([( | 
|  | String::from("__ra_is_test_fixture"), | 
|  | String::from("__ra_is_test_fixture"), | 
|  | )]), | 
|  | CrateOrigin::Local { repo: None, name: None }, | 
|  | true, | 
|  | proc_macro_cwd, | 
|  | crate_ws_data, | 
|  | ); | 
|  | proc_macros.insert(proc_macros_crate, Ok(proc_macro)); | 
|  |  | 
|  | for krate in all_crates { | 
|  | crate_graph | 
|  | .add_dep( | 
|  | krate, | 
|  | DependencyBuilder::new( | 
|  | 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)), | 
|  | }; | 
|  | roots.push(root); | 
|  |  | 
|  | let mut change = ChangeWithProcMacros { source_change, proc_macros: Some(proc_macros) }; | 
|  |  | 
|  | change.source_change.set_roots(roots); | 
|  | change.source_change.set_crate_graph(crate_graph); | 
|  |  | 
|  | ChangeFixture { file_position, files, change } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> { | 
|  | Box::new([ | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_attribute] | 
|  | pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream { | 
|  | item | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("identity"), | 
|  | kind: ProcMacroKind::Attr, | 
|  | expander: sync::Arc::new(IdentityProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_derive(DeriveIdentity)] | 
|  | pub fn derive_identity(item: TokenStream) -> TokenStream { | 
|  | item | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("DeriveIdentity"), | 
|  | kind: ProcMacroKind::CustomDerive, | 
|  | expander: sync::Arc::new(IdentityProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_attribute] | 
|  | pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream { | 
|  | attr | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("input_replace"), | 
|  | kind: ProcMacroKind::Attr, | 
|  | expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro] | 
|  | pub fn mirror(input: TokenStream) -> TokenStream { | 
|  | input | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("mirror"), | 
|  | kind: ProcMacroKind::Bang, | 
|  | expander: sync::Arc::new(MirrorProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro] | 
|  | pub fn shorten(input: TokenStream) -> TokenStream { | 
|  | loop {} | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("shorten"), | 
|  | kind: ProcMacroKind::Bang, | 
|  | expander: sync::Arc::new(ShortenProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_attribute] | 
|  | pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream { | 
|  | loop {} | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("issue_18089"), | 
|  | kind: ProcMacroKind::Attr, | 
|  | expander: sync::Arc::new(Issue18089ProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_attribute] | 
|  | pub fn issue_18840(_attr: TokenStream, _item: TokenStream) -> TokenStream { | 
|  | loop {} | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("issue_18840"), | 
|  | kind: ProcMacroKind::Attr, | 
|  | expander: sync::Arc::new(Issue18840ProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro] | 
|  | pub fn issue_17479(input: TokenStream) -> TokenStream { | 
|  | input | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("issue_17479"), | 
|  | kind: ProcMacroKind::Bang, | 
|  | expander: sync::Arc::new(Issue17479ProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_attribute] | 
|  | pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream { | 
|  | input | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("issue_18898"), | 
|  | kind: ProcMacroKind::Bang, | 
|  | expander: sync::Arc::new(Issue18898ProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_attribute] | 
|  | pub fn disallow_cfg(_attr: TokenStream, input: TokenStream) -> TokenStream { | 
|  | input | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("disallow_cfg"), | 
|  | kind: ProcMacroKind::Attr, | 
|  | expander: sync::Arc::new(DisallowCfgProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ( | 
|  | r#" | 
|  | #[proc_macro_attribute] | 
|  | pub fn generate_suffixed_type(_attr: TokenStream, input: TokenStream) -> TokenStream { | 
|  | input | 
|  | } | 
|  | "# | 
|  | .into(), | 
|  | ProcMacro { | 
|  | name: Symbol::intern("generate_suffixed_type"), | 
|  | kind: ProcMacroKind::Attr, | 
|  | expander: sync::Arc::new(GenerateSuffixedTypeProcMacroExpander), | 
|  | disabled: false, | 
|  | }, | 
|  | ), | 
|  | ]) | 
|  | } | 
|  |  | 
|  | fn filter_test_proc_macros( | 
|  | proc_macro_names: &[String], | 
|  | proc_macro_defs: Vec<(String, ProcMacro)>, | 
|  | ) -> (Vec<ProcMacro>, String) { | 
|  | // The source here is only required so that paths to the macros exist and are resolvable. | 
|  | let mut source = String::new(); | 
|  | let mut proc_macros = Vec::new(); | 
|  |  | 
|  | for (c, p) in proc_macro_defs { | 
|  | if !proc_macro_names.iter().any(|name| name == &stdx::to_lower_snake_case(p.name.as_str())) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | proc_macros.push(p); | 
|  | source += &c; | 
|  | } | 
|  |  | 
|  | (proc_macros, source) | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Clone, Copy)] | 
|  | enum SourceRootKind { | 
|  | Local, | 
|  | Library, | 
|  | } | 
|  |  | 
|  | #[derive(Debug)] | 
|  | struct FileMeta { | 
|  | path: String, | 
|  | krate: Option<(String, CrateOrigin, Option<String>)>, | 
|  | deps: Vec<String>, | 
|  | extern_prelude: Option<Vec<String>>, | 
|  | cfg: CfgOptions, | 
|  | edition: Edition, | 
|  | env: Env, | 
|  | introduce_new_source_root: Option<SourceRootKind>, | 
|  | } | 
|  |  | 
|  | impl FileMeta { | 
|  | fn from_fixture(f: Fixture, current_source_root_kind: SourceRootKind) -> Self { | 
|  | let mut cfg = CfgOptions::default(); | 
|  | for (k, v) in f.cfgs { | 
|  | if let Some(v) = v { | 
|  | cfg.insert_key_value(Symbol::intern(&k), Symbol::intern(&v)); | 
|  | } else { | 
|  | cfg.insert_atom(Symbol::intern(&k)); | 
|  | } | 
|  | } | 
|  |  | 
|  | let introduce_new_source_root = f.introduce_new_source_root.map(|kind| match &*kind { | 
|  | "local" => SourceRootKind::Local, | 
|  | "library" => SourceRootKind::Library, | 
|  | invalid => panic!("invalid source root kind '{invalid}'"), | 
|  | }); | 
|  | let current_source_root_kind = | 
|  | introduce_new_source_root.unwrap_or(current_source_root_kind); | 
|  |  | 
|  | let deps = f.deps; | 
|  | Self { | 
|  | path: f.path, | 
|  | krate: f.krate.map(|it| parse_crate(it, current_source_root_kind, f.library)), | 
|  | extern_prelude: f.extern_prelude, | 
|  | deps, | 
|  | cfg, | 
|  | edition: f.edition.map_or(Edition::CURRENT, |v| Edition::from_str(&v).unwrap()), | 
|  | env: f.env.into_iter().collect(), | 
|  | introduce_new_source_root, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn parse_crate( | 
|  | crate_str: String, | 
|  | current_source_root_kind: SourceRootKind, | 
|  | explicit_non_workspace_member: bool, | 
|  | ) -> (String, CrateOrigin, Option<String>) { | 
|  | // syntax: | 
|  | //   "my_awesome_crate" | 
|  | //   "my_awesome_crate@0.0.1,http://example.com" | 
|  | let (name, repo, version) = if let Some((name, remain)) = crate_str.split_once('@') { | 
|  | let (version, repo) = | 
|  | remain.split_once(',').expect("crate meta: found '@' without version and url"); | 
|  | (name.to_owned(), Some(repo.to_owned()), Some(version.to_owned())) | 
|  | } else { | 
|  | (crate_str, None, None) | 
|  | }; | 
|  |  | 
|  | let non_workspace_member = explicit_non_workspace_member | 
|  | || matches!(current_source_root_kind, SourceRootKind::Library); | 
|  |  | 
|  | let origin = match LangCrateOrigin::from(&*name) { | 
|  | LangCrateOrigin::Other => { | 
|  | let name = Symbol::intern(&name); | 
|  | if non_workspace_member { | 
|  | CrateOrigin::Library { repo, name } | 
|  | } else { | 
|  | CrateOrigin::Local { repo, name: Some(name) } | 
|  | } | 
|  | } | 
|  | origin => CrateOrigin::Lang(origin), | 
|  | }; | 
|  |  | 
|  | (name, origin, version) | 
|  | } | 
|  |  | 
|  | // Identity mapping | 
|  | #[derive(Debug)] | 
|  | struct IdentityProcMacroExpander; | 
|  | impl ProcMacroExpander for IdentityProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | subtree: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | Ok(subtree.clone()) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Expands to a macro_rules! macro, for issue #18089. | 
|  | #[derive(Debug)] | 
|  | struct Issue18089ProcMacroExpander; | 
|  | impl ProcMacroExpander for Issue18089ProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | subtree: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | _: Span, | 
|  | call_site: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | let tt::TokenTree::Leaf(macro_name) = &subtree.0[2] else { | 
|  | return Err(ProcMacroExpansionError::Panic("incorrect input".to_owned())); | 
|  | }; | 
|  | Ok(quote! { call_site => | 
|  | #[macro_export] | 
|  | macro_rules! my_macro___ { | 
|  | ($($token:tt)*) => {{ | 
|  | }}; | 
|  | } | 
|  |  | 
|  | pub use my_macro___ as #macro_name; | 
|  |  | 
|  | #subtree | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Pastes the attribute input as its output | 
|  | #[derive(Debug)] | 
|  | struct AttributeInputReplaceProcMacroExpander; | 
|  | impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | _: &TopSubtree, | 
|  | attrs: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | attrs | 
|  | .cloned() | 
|  | .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into())) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Debug)] | 
|  | struct Issue18840ProcMacroExpander; | 
|  | impl ProcMacroExpander for Issue18840ProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | fn_: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | def_site: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | // Input: | 
|  | // ``` | 
|  | // #[issue_18840] | 
|  | // fn foo() { let loop {} } | 
|  | // ``` | 
|  |  | 
|  | // The span that was created by the fixup infra. | 
|  | let fixed_up_span = fn_.token_trees().flat_tokens()[5].first_span(); | 
|  | let mut result = | 
|  | quote! {fixed_up_span => ::core::compile_error! { "my cool compile_error!" } }; | 
|  | // Make it so we won't remove the top subtree when reversing fixups. | 
|  | let top_subtree_delimiter_mut = result.top_subtree_delimiter_mut(); | 
|  | top_subtree_delimiter_mut.open = def_site; | 
|  | top_subtree_delimiter_mut.close = def_site; | 
|  | Ok(result) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Debug)] | 
|  | struct MirrorProcMacroExpander; | 
|  | impl ProcMacroExpander for MirrorProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | input: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | fn traverse(builder: &mut TopSubtreeBuilder, iter: TtIter<'_>) { | 
|  | for tt in iter.collect_vec().into_iter().rev() { | 
|  | match tt { | 
|  | TtElement::Leaf(leaf) => builder.push(leaf.clone()), | 
|  | TtElement::Subtree(subtree, subtree_iter) => { | 
|  | builder.open(subtree.delimiter.kind, subtree.delimiter.open); | 
|  | traverse(builder, subtree_iter); | 
|  | builder.close(subtree.delimiter.close); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | let mut builder = TopSubtreeBuilder::new(input.top_subtree().delimiter); | 
|  | traverse(&mut builder, input.iter()); | 
|  | Ok(builder.build()) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Replaces every literal with an empty string literal and every identifier with its first letter, | 
|  | // but retains all tokens' span. Useful for testing we don't assume token hasn't been modified by | 
|  | // macros even if it retains its span. | 
|  | #[derive(Debug)] | 
|  | struct ShortenProcMacroExpander; | 
|  | impl ProcMacroExpander for ShortenProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | input: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | let mut result = input.0.clone(); | 
|  | for it in &mut result { | 
|  | if let TokenTree::Leaf(leaf) = it { | 
|  | modify_leaf(leaf) | 
|  | } | 
|  | } | 
|  | return Ok(tt::TopSubtree(result)); | 
|  |  | 
|  | fn modify_leaf(leaf: &mut Leaf) { | 
|  | match leaf { | 
|  | Leaf::Literal(it) => { | 
|  | // XXX Currently replaces any literals with an empty string, but supporting | 
|  | // "shortening" other literals would be nice. | 
|  | it.symbol = Symbol::empty(); | 
|  | } | 
|  | Leaf::Punct(_) => {} | 
|  | Leaf::Ident(it) => { | 
|  | it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::<String>()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Reads ident type within string quotes, for issue #17479. | 
|  | #[derive(Debug)] | 
|  | struct Issue17479ProcMacroExpander; | 
|  | impl ProcMacroExpander for Issue17479ProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | subtree: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | let TokenTree::Leaf(Leaf::Literal(lit)) = &subtree.0[1] else { | 
|  | return Err(ProcMacroExpansionError::Panic("incorrect Input".into())); | 
|  | }; | 
|  | let symbol = &lit.symbol; | 
|  | let span = lit.span; | 
|  | Ok(quote! { span => | 
|  | #symbol() | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Reads ident type within string quotes, for issue #17479. | 
|  | #[derive(Debug)] | 
|  | struct Issue18898ProcMacroExpander; | 
|  | impl ProcMacroExpander for Issue18898ProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | subtree: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | def_site: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | let span = subtree | 
|  | .token_trees() | 
|  | .flat_tokens() | 
|  | .last() | 
|  | .ok_or_else(|| ProcMacroExpansionError::Panic("malformed input".to_owned()))? | 
|  | .first_span(); | 
|  | let overly_long_subtree = quote! {span => | 
|  | { | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | let a = 5; | 
|  | } | 
|  | }; | 
|  | Ok(quote! { def_site => | 
|  | fn foo() { | 
|  | #overly_long_subtree | 
|  | } | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Reads ident type within string quotes, for issue #17479. | 
|  | #[derive(Debug)] | 
|  | struct DisallowCfgProcMacroExpander; | 
|  | impl ProcMacroExpander for DisallowCfgProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | subtree: &TopSubtree, | 
|  | _: Option<&TopSubtree>, | 
|  | _: &Env, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: Span, | 
|  | _: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | for tt in subtree.token_trees().flat_tokens() { | 
|  | if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt { | 
|  | if ident.sym == sym::cfg || ident.sym == sym::cfg_attr { | 
|  | return Err(ProcMacroExpansionError::Panic( | 
|  | "cfg or cfg_attr found in DisallowCfgProcMacroExpander".to_owned(), | 
|  | )); | 
|  | } | 
|  | } | 
|  | } | 
|  | Ok(subtree.clone()) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Generates a new type by adding a suffix to the original name | 
|  | #[derive(Debug)] | 
|  | struct GenerateSuffixedTypeProcMacroExpander; | 
|  | impl ProcMacroExpander for GenerateSuffixedTypeProcMacroExpander { | 
|  | fn expand( | 
|  | &self, | 
|  | subtree: &TopSubtree, | 
|  | _attrs: Option<&TopSubtree>, | 
|  | _env: &Env, | 
|  | _def_site: Span, | 
|  | call_site: Span, | 
|  | _mixed_site: Span, | 
|  | _current_dir: String, | 
|  | ) -> Result<TopSubtree, ProcMacroExpansionError> { | 
|  | let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[1] else { | 
|  | return Err(ProcMacroExpansionError::Panic("incorrect Input".into())); | 
|  | }; | 
|  |  | 
|  | let ident = match ident.sym.as_str() { | 
|  | "struct" => { | 
|  | let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[2] else { | 
|  | return Err(ProcMacroExpansionError::Panic("incorrect Input".into())); | 
|  | }; | 
|  | ident | 
|  | } | 
|  |  | 
|  | "enum" => { | 
|  | let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[4] else { | 
|  | return Err(ProcMacroExpansionError::Panic("incorrect Input".into())); | 
|  | }; | 
|  | ident | 
|  | } | 
|  |  | 
|  | _ => { | 
|  | return Err(ProcMacroExpansionError::Panic("incorrect Input".into())); | 
|  | } | 
|  | }; | 
|  |  | 
|  | let generated_ident = tt::Ident { | 
|  | sym: Symbol::intern(&format!("{}Suffix", ident.sym)), | 
|  | span: ident.span, | 
|  | is_raw: tt::IdentIsRaw::No, | 
|  | }; | 
|  |  | 
|  | let ret = quote! { call_site => | 
|  | #subtree | 
|  |  | 
|  | struct #generated_ident; | 
|  | }; | 
|  |  | 
|  | Ok(ret) | 
|  | } | 
|  |  | 
|  | fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool { | 
|  | other.type_id() == TypeId::of::<Self>() | 
|  | } | 
|  | } |