test_fixture/
lib.rs

1//! A set of high-level utility fixture methods to use in tests.
2
3#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
4
5#[cfg(feature = "in-rust-tree")]
6extern crate rustc_driver as _;
7
8use std::{any::TypeId, mem, str::FromStr, sync};
9
10use base_db::target::TargetData;
11use base_db::{
12    Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
13    DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase,
14    SourceRoot, Version, VfsPath,
15};
16use cfg::CfgOptions;
17use hir_expand::{
18    EditionedFileId, FileRange,
19    change::ChangeWithProcMacros,
20    db::ExpandDatabase,
21    files::FilePosition,
22    proc_macro::{
23        ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder,
24    },
25    quote,
26    tt::{Leaf, TokenTree, TopSubtree, TopSubtreeBuilder, TtElement, TtIter},
27};
28use intern::{Symbol, sym};
29use paths::AbsPathBuf;
30use span::{Edition, FileId, Span};
31use stdx::itertools::Itertools;
32use test_utils::{
33    CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, MiniCore, RangeOrOffset,
34    extract_range_or_offset,
35};
36use triomphe::Arc;
37
38pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0);
39
40pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
41    #[track_caller]
42    fn with_single_file(
43        #[rust_analyzer::rust_fixture] ra_fixture: &str,
44    ) -> (Self, EditionedFileId) {
45        let mut db = Self::default();
46        let fixture = ChangeFixture::parse(ra_fixture);
47        fixture.change.apply(&mut db);
48        assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture");
49        let file = EditionedFileId::from_span_guess_origin(&db, fixture.files[0]);
50        (db, file)
51    }
52
53    #[track_caller]
54    fn with_many_files(
55        #[rust_analyzer::rust_fixture] ra_fixture: &str,
56    ) -> (Self, Vec<EditionedFileId>) {
57        let mut db = Self::default();
58        let fixture = ChangeFixture::parse(ra_fixture);
59        fixture.change.apply(&mut db);
60        assert!(fixture.file_position.is_none());
61        let files = fixture
62            .files
63            .into_iter()
64            .map(|file| EditionedFileId::from_span_guess_origin(&db, file))
65            .collect();
66        (db, files)
67    }
68
69    #[track_caller]
70    fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
71        let mut db = Self::default();
72        let fixture = ChangeFixture::parse(ra_fixture);
73        fixture.change.apply(&mut db);
74        assert!(fixture.file_position.is_none());
75        db
76    }
77
78    #[track_caller]
79    fn with_files_extra_proc_macros(
80        #[rust_analyzer::rust_fixture] ra_fixture: &str,
81        proc_macros: Vec<(String, ProcMacro)>,
82    ) -> Self {
83        let mut db = Self::default();
84        let fixture =
85            ChangeFixture::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, proc_macros);
86        fixture.change.apply(&mut db);
87        assert!(fixture.file_position.is_none());
88        db
89    }
90
91    #[track_caller]
92    fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) {
93        let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
94        let offset = range_or_offset.expect_offset();
95        (db, FilePosition { file_id, offset })
96    }
97
98    #[track_caller]
99    fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) {
100        let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
101        let range = range_or_offset.expect_range();
102        (db, FileRange { file_id, range })
103    }
104
105    #[track_caller]
106    fn with_range_or_offset(
107        #[rust_analyzer::rust_fixture] ra_fixture: &str,
108    ) -> (Self, EditionedFileId, RangeOrOffset) {
109        let mut db = Self::default();
110        let fixture = ChangeFixture::parse(ra_fixture);
111        fixture.change.apply(&mut db);
112
113        let (file_id, range_or_offset) = fixture
114            .file_position
115            .expect("Could not find file position in fixture. Did you forget to add an `$0`?");
116        let file_id = EditionedFileId::from_span_guess_origin(&db, file_id);
117        (db, file_id, range_or_offset)
118    }
119
120    fn test_crate(&self) -> Crate {
121        self.all_crates().iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap()
122    }
123}
124
125impl<DB: ExpandDatabase + SourceDatabase + Default + 'static> WithFixture for DB {}
126
127pub struct ChangeFixture {
128    pub file_position: Option<(span::EditionedFileId, RangeOrOffset)>,
129    pub file_lines: Vec<usize>,
130    pub files: Vec<span::EditionedFileId>,
131    pub change: ChangeWithProcMacros,
132    pub sysroot_files: Vec<FileId>,
133}
134
135const SOURCE_ROOT_PREFIX: &str = "/";
136
137impl ChangeFixture {
138    pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> ChangeFixture {
139        Self::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
140    }
141
142    pub fn parse_with_proc_macros(
143        #[rust_analyzer::rust_fixture] ra_fixture: &str,
144        minicore_raw: &str,
145        mut proc_macro_defs: Vec<(String, ProcMacro)>,
146    ) -> ChangeFixture {
147        let FixtureWithProjectMeta {
148            fixture,
149            mini_core,
150            proc_macro_names,
151            toolchain,
152            target_data_layout,
153            target_arch,
154        } = FixtureWithProjectMeta::parse(ra_fixture);
155        let target_data_layout = target_data_layout.into();
156        let target_arch = parse_target_arch(&target_arch);
157        let target = Ok(TargetData { arch: target_arch, data_layout: target_data_layout });
158        let toolchain = Some({
159            let channel = toolchain.as_deref().unwrap_or("stable");
160            Version::parse(&format!("1.76.0-{channel}")).unwrap()
161        });
162        let mut source_change = FileChange::default();
163
164        let mut files = Vec::new();
165        let mut sysroot_files = Vec::new();
166        let mut file_lines = Vec::new();
167        let mut crate_graph = CrateGraphBuilder::default();
168        let mut crates = FxIndexMap::default();
169        let mut crate_deps = Vec::new();
170        let mut default_crate_root: Option<FileId> = None;
171        let mut default_edition = Edition::CURRENT;
172        let mut default_cfg = CfgOptions::default();
173        let mut default_env = Env::from_iter([(
174            String::from("__ra_is_test_fixture"),
175            String::from("__ra_is_test_fixture"),
176        )]);
177
178        let mut file_set = FileSet::default();
179        let mut current_source_root_kind = SourceRootKind::Local;
180        let mut file_id = FileId::from_raw(0);
181        let mut roots = Vec::new();
182
183        let mut file_position = None;
184
185        let crate_ws_data = Arc::new(CrateWorkspaceData { target, toolchain });
186
187        // FIXME: This is less than ideal
188        let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()));
189
190        for entry in fixture {
191            file_lines.push(entry.line);
192
193            let mut range_or_offset = None;
194            let text = if entry.text.contains(CURSOR_MARKER) {
195                if entry.text.contains(ESCAPED_CURSOR_MARKER) {
196                    entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER)
197                } else {
198                    let (roo, text) = extract_range_or_offset(&entry.text);
199                    assert!(file_position.is_none());
200                    range_or_offset = Some(roo);
201                    text
202                }
203            } else {
204                entry.text.as_str().into()
205            };
206
207            let meta = FileMeta::from_fixture(entry, current_source_root_kind);
208            if let Some(range_or_offset) = range_or_offset {
209                file_position =
210                    Some((span::EditionedFileId::new(file_id, meta.edition), range_or_offset));
211            }
212
213            assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX));
214            if !meta.deps.is_empty() {
215                assert!(meta.krate.is_some(), "can't specify deps without naming the crate")
216            }
217
218            if let Some(kind) = meta.introduce_new_source_root {
219                assert!(
220                    meta.krate.is_some(),
221                    "new_source_root meta doesn't make sense without crate meta"
222                );
223                let prev_kind = mem::replace(&mut current_source_root_kind, kind);
224                let prev_root = match prev_kind {
225                    SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)),
226                    SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)),
227                };
228                roots.push(prev_root);
229            }
230
231            if let Some((krate, origin, version)) = meta.krate {
232                let crate_name = CrateName::normalize_dashes(&krate);
233                let crate_id = crate_graph.add_crate_root(
234                    file_id,
235                    meta.edition,
236                    Some(crate_name.clone().into()),
237                    version,
238                    meta.cfg.clone(),
239                    Some(meta.cfg),
240                    meta.env,
241                    origin,
242                    false,
243                    proc_macro_cwd.clone(),
244                    crate_ws_data.clone(),
245                );
246                let prev = crates.insert(crate_name.clone(), crate_id);
247                assert!(prev.is_none(), "multiple crates with same name: {crate_name}");
248                for dep in meta.deps {
249                    let prelude = match &meta.extern_prelude {
250                        Some(v) => v.contains(&dep),
251                        None => true,
252                    };
253                    let dep = CrateName::normalize_dashes(&dep);
254                    crate_deps.push((crate_name.clone(), dep, prelude))
255                }
256            } else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
257                assert!(default_crate_root.is_none());
258                default_crate_root = Some(file_id);
259                default_edition = meta.edition;
260                default_cfg.append(meta.cfg);
261                default_env.extend_from_other(&meta.env);
262            }
263
264            source_change.change_file(file_id, Some(text));
265            let path = VfsPath::new_virtual_path(meta.path);
266            file_set.insert(file_id, path);
267            files.push(span::EditionedFileId::new(file_id, meta.edition));
268            file_id = FileId::from_raw(file_id.index() + 1);
269        }
270
271        let mini_core = mini_core.map(|mini_core| {
272            let core_file = file_id;
273            file_id = FileId::from_raw(file_id.index() + 1);
274
275            let mut fs = FileSet::default();
276            fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_owned()));
277            roots.push(SourceRoot::new_library(fs));
278
279            sysroot_files.push(core_file);
280
281            source_change.change_file(core_file, Some(mini_core.source_code(minicore_raw)));
282
283            let core_crate = crate_graph.add_crate_root(
284                core_file,
285                Edition::CURRENT,
286                Some(CrateDisplayName::from_canonical_name("core")),
287                None,
288                Default::default(),
289                Default::default(),
290                Env::from_iter([(
291                    String::from("__ra_is_test_fixture"),
292                    String::from("__ra_is_test_fixture"),
293                )]),
294                CrateOrigin::Lang(LangCrateOrigin::Core),
295                false,
296                proc_macro_cwd.clone(),
297                crate_ws_data.clone(),
298            );
299
300            (
301                move || {
302                    DependencyBuilder::with_prelude(
303                        CrateName::new("core").unwrap(),
304                        core_crate,
305                        true,
306                        true,
307                    )
308                },
309                core_crate,
310            )
311        });
312
313        if crates.is_empty() {
314            let crate_root = default_crate_root
315                .expect("missing default crate root, specify a main.rs or lib.rs");
316            let root = crate_graph.add_crate_root(
317                crate_root,
318                default_edition,
319                Some(CrateName::new("ra_test_fixture").unwrap().into()),
320                None,
321                default_cfg.clone(),
322                Some(default_cfg),
323                default_env,
324                CrateOrigin::Local { repo: None, name: None },
325                false,
326                proc_macro_cwd.clone(),
327                crate_ws_data.clone(),
328            );
329            if let Some((mini_core, _)) = mini_core {
330                crate_graph.add_dep(root, mini_core()).unwrap();
331            }
332        } else {
333            // Insert minicore first to match with `project-model::workspace`
334            if let Some((mini_core, core_crate)) = mini_core {
335                let all_crates = crate_graph.iter().collect::<Vec<_>>();
336                for krate in all_crates {
337                    if krate == core_crate {
338                        continue;
339                    }
340                    crate_graph.add_dep(krate, mini_core()).unwrap();
341                }
342            }
343
344            for (from, to, prelude) in crate_deps {
345                let from_id = crates[&from];
346                let to_id = crates[&to];
347                let sysroot = crate_graph[to_id].basic.origin.is_lang();
348                crate_graph
349                    .add_dep(
350                        from_id,
351                        DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot),
352                    )
353                    .unwrap();
354            }
355        }
356
357        let mut proc_macros = ProcMacrosBuilder::default();
358        if !proc_macro_names.is_empty() {
359            let proc_lib_file = file_id;
360
361            proc_macro_defs.extend(default_test_proc_macros());
362            let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs);
363            let mut fs = FileSet::default();
364            fs.insert(
365                proc_lib_file,
366                VfsPath::new_virtual_path("/sysroot/proc_macros/lib.rs".to_owned()),
367            );
368            roots.push(SourceRoot::new_library(fs));
369
370            sysroot_files.push(proc_lib_file);
371
372            source_change.change_file(proc_lib_file, Some(source));
373
374            let all_crates = crate_graph.iter().collect::<Vec<_>>();
375
376            let proc_macros_crate = crate_graph.add_crate_root(
377                proc_lib_file,
378                Edition::CURRENT,
379                Some(CrateDisplayName::from_canonical_name("proc_macros")),
380                None,
381                Default::default(),
382                Default::default(),
383                Env::from_iter([(
384                    String::from("__ra_is_test_fixture"),
385                    String::from("__ra_is_test_fixture"),
386                )]),
387                CrateOrigin::Local { repo: None, name: None },
388                true,
389                proc_macro_cwd,
390                crate_ws_data,
391            );
392            proc_macros.insert(proc_macros_crate, Ok(proc_macro));
393
394            for krate in all_crates {
395                crate_graph
396                    .add_dep(
397                        krate,
398                        DependencyBuilder::new(
399                            CrateName::new("proc_macros").unwrap(),
400                            proc_macros_crate,
401                        ),
402                    )
403                    .unwrap();
404            }
405        }
406
407        let _ = file_id;
408
409        let root = match current_source_root_kind {
410            SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)),
411            SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)),
412        };
413        roots.push(root);
414
415        let mut change = ChangeWithProcMacros { source_change, proc_macros: Some(proc_macros) };
416
417        change.source_change.set_roots(roots);
418        change.source_change.set_crate_graph(crate_graph);
419
420        ChangeFixture { file_position, file_lines, files, change, sysroot_files }
421    }
422}
423
424fn parse_target_arch(arch: &str) -> base_db::target::Arch {
425    use base_db::target::Arch::*;
426    match arch {
427        "wasm32" => Wasm32,
428        "wasm64" => Wasm64,
429        _ => Other,
430    }
431}
432
433fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> {
434    Box::new([
435        (
436            r#"
437#[proc_macro_attribute]
438pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
439    item
440}
441"#
442            .into(),
443            ProcMacro {
444                name: Symbol::intern("identity"),
445                kind: ProcMacroKind::Attr,
446                expander: sync::Arc::new(IdentityProcMacroExpander),
447                disabled: false,
448            },
449        ),
450        (
451            r#"
452#[proc_macro_derive(DeriveIdentity)]
453pub fn derive_identity(item: TokenStream) -> TokenStream {
454    item
455}
456"#
457            .into(),
458            ProcMacro {
459                name: Symbol::intern("DeriveIdentity"),
460                kind: ProcMacroKind::CustomDerive,
461                expander: sync::Arc::new(IdentityProcMacroExpander),
462                disabled: false,
463            },
464        ),
465        (
466            r#"
467#[proc_macro_attribute]
468pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
469    attr
470}
471"#
472            .into(),
473            ProcMacro {
474                name: Symbol::intern("input_replace"),
475                kind: ProcMacroKind::Attr,
476                expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
477                disabled: false,
478            },
479        ),
480        (
481            r#"
482#[proc_macro]
483pub fn mirror(input: TokenStream) -> TokenStream {
484    input
485}
486"#
487            .into(),
488            ProcMacro {
489                name: Symbol::intern("mirror"),
490                kind: ProcMacroKind::Bang,
491                expander: sync::Arc::new(MirrorProcMacroExpander),
492                disabled: false,
493            },
494        ),
495        (
496            r#"
497#[proc_macro]
498pub fn shorten(input: TokenStream) -> TokenStream {
499    loop {}
500}
501"#
502            .into(),
503            ProcMacro {
504                name: Symbol::intern("shorten"),
505                kind: ProcMacroKind::Bang,
506                expander: sync::Arc::new(ShortenProcMacroExpander),
507                disabled: false,
508            },
509        ),
510        (
511            r#"
512#[proc_macro_attribute]
513pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream {
514    loop {}
515}
516"#
517            .into(),
518            ProcMacro {
519                name: Symbol::intern("issue_18089"),
520                kind: ProcMacroKind::Attr,
521                expander: sync::Arc::new(Issue18089ProcMacroExpander),
522                disabled: false,
523            },
524        ),
525        (
526            r#"
527#[proc_macro_attribute]
528pub fn issue_18840(_attr: TokenStream, _item: TokenStream) -> TokenStream {
529    loop {}
530}
531"#
532            .into(),
533            ProcMacro {
534                name: Symbol::intern("issue_18840"),
535                kind: ProcMacroKind::Attr,
536                expander: sync::Arc::new(Issue18840ProcMacroExpander),
537                disabled: false,
538            },
539        ),
540        (
541            r#"
542#[proc_macro]
543pub fn issue_17479(input: TokenStream) -> TokenStream {
544    input
545}
546"#
547            .into(),
548            ProcMacro {
549                name: Symbol::intern("issue_17479"),
550                kind: ProcMacroKind::Bang,
551                expander: sync::Arc::new(Issue17479ProcMacroExpander),
552                disabled: false,
553            },
554        ),
555        (
556            r#"
557#[proc_macro_attribute]
558pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream {
559    input
560}
561"#
562            .into(),
563            ProcMacro {
564                name: Symbol::intern("issue_18898"),
565                kind: ProcMacroKind::Bang,
566                expander: sync::Arc::new(Issue18898ProcMacroExpander),
567                disabled: false,
568            },
569        ),
570        (
571            r#"
572#[proc_macro_attribute]
573pub fn disallow_cfg(_attr: TokenStream, input: TokenStream) -> TokenStream {
574    input
575}
576"#
577            .into(),
578            ProcMacro {
579                name: Symbol::intern("disallow_cfg"),
580                kind: ProcMacroKind::Attr,
581                expander: sync::Arc::new(DisallowCfgProcMacroExpander),
582                disabled: false,
583            },
584        ),
585        (
586            r#"
587#[proc_macro_attribute]
588pub fn generate_suffixed_type(_attr: TokenStream, input: TokenStream) -> TokenStream {
589    input
590}
591"#
592            .into(),
593            ProcMacro {
594                name: Symbol::intern("generate_suffixed_type"),
595                kind: ProcMacroKind::Attr,
596                expander: sync::Arc::new(GenerateSuffixedTypeProcMacroExpander),
597                disabled: false,
598            },
599        ),
600    ])
601}
602
603fn filter_test_proc_macros(
604    proc_macro_names: &[String],
605    proc_macro_defs: Vec<(String, ProcMacro)>,
606) -> (Vec<ProcMacro>, String) {
607    // The source here is only required so that paths to the macros exist and are resolvable.
608    let mut source = String::new();
609    let mut proc_macros = Vec::new();
610
611    for (c, p) in proc_macro_defs {
612        if !proc_macro_names.iter().any(|name| name == &stdx::to_lower_snake_case(p.name.as_str()))
613        {
614            continue;
615        }
616        proc_macros.push(p);
617        source += &c;
618    }
619
620    (proc_macros, source)
621}
622
623#[derive(Debug, Clone, Copy)]
624enum SourceRootKind {
625    Local,
626    Library,
627}
628
629#[derive(Debug)]
630struct FileMeta {
631    path: String,
632    krate: Option<(String, CrateOrigin, Option<String>)>,
633    deps: Vec<String>,
634    extern_prelude: Option<Vec<String>>,
635    cfg: CfgOptions,
636    edition: Edition,
637    env: Env,
638    introduce_new_source_root: Option<SourceRootKind>,
639}
640
641impl FileMeta {
642    fn from_fixture(f: Fixture, current_source_root_kind: SourceRootKind) -> Self {
643        let mut cfg = CfgOptions::default();
644        for (k, v) in f.cfgs {
645            if let Some(v) = v {
646                cfg.insert_key_value(Symbol::intern(&k), Symbol::intern(&v));
647            } else {
648                cfg.insert_atom(Symbol::intern(&k));
649            }
650        }
651
652        let introduce_new_source_root = f.introduce_new_source_root.map(|kind| match &*kind {
653            "local" => SourceRootKind::Local,
654            "library" => SourceRootKind::Library,
655            invalid => panic!("invalid source root kind '{invalid}'"),
656        });
657        let current_source_root_kind =
658            introduce_new_source_root.unwrap_or(current_source_root_kind);
659
660        let deps = f.deps;
661        Self {
662            path: f.path,
663            krate: f.krate.map(|it| parse_crate(it, current_source_root_kind, f.library)),
664            extern_prelude: f.extern_prelude,
665            deps,
666            cfg,
667            edition: f.edition.map_or(Edition::CURRENT, |v| Edition::from_str(&v).unwrap()),
668            env: f.env.into_iter().collect(),
669            introduce_new_source_root,
670        }
671    }
672}
673
674#[derive(Debug, Clone, Copy, PartialEq, Eq)]
675enum ForceNoneLangOrigin {
676    Yes,
677    No,
678}
679
680fn parse_crate(
681    crate_str: String,
682    current_source_root_kind: SourceRootKind,
683    explicit_non_workspace_member: bool,
684) -> (String, CrateOrigin, Option<String>) {
685    let (crate_str, force_non_lang_origin) = if let Some(s) = crate_str.strip_prefix("r#") {
686        (s.to_owned(), ForceNoneLangOrigin::Yes)
687    } else {
688        (crate_str, ForceNoneLangOrigin::No)
689    };
690
691    // syntax:
692    //   "my_awesome_crate"
693    //   "my_awesome_crate@0.0.1,http://example.com"
694    let (name, repo, version) = if let Some((name, remain)) = crate_str.split_once('@') {
695        let (version, repo) =
696            remain.split_once(',').expect("crate meta: found '@' without version and url");
697        (name.to_owned(), Some(repo.to_owned()), Some(version.to_owned()))
698    } else {
699        (crate_str, None, None)
700    };
701
702    let non_workspace_member = explicit_non_workspace_member
703        || matches!(current_source_root_kind, SourceRootKind::Library);
704
705    let origin = if force_non_lang_origin == ForceNoneLangOrigin::Yes {
706        let name = Symbol::intern(&name);
707        if non_workspace_member {
708            CrateOrigin::Library { repo, name }
709        } else {
710            CrateOrigin::Local { repo, name: Some(name) }
711        }
712    } else {
713        match LangCrateOrigin::from(&*name) {
714            LangCrateOrigin::Other => {
715                let name = Symbol::intern(&name);
716                if non_workspace_member {
717                    CrateOrigin::Library { repo, name }
718                } else {
719                    CrateOrigin::Local { repo, name: Some(name) }
720                }
721            }
722            origin => CrateOrigin::Lang(origin),
723        }
724    };
725
726    (name, origin, version)
727}
728
729// Identity mapping
730#[derive(Debug)]
731struct IdentityProcMacroExpander;
732impl ProcMacroExpander for IdentityProcMacroExpander {
733    fn expand(
734        &self,
735        subtree: &TopSubtree,
736        _: Option<&TopSubtree>,
737        _: &Env,
738        _: Span,
739        _: Span,
740        _: Span,
741        _: String,
742    ) -> Result<TopSubtree, ProcMacroExpansionError> {
743        Ok(subtree.clone())
744    }
745
746    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
747        other.type_id() == TypeId::of::<Self>()
748    }
749}
750
751// Expands to a macro_rules! macro, for issue #18089.
752#[derive(Debug)]
753struct Issue18089ProcMacroExpander;
754impl ProcMacroExpander for Issue18089ProcMacroExpander {
755    fn expand(
756        &self,
757        subtree: &TopSubtree,
758        _: Option<&TopSubtree>,
759        _: &Env,
760        _: Span,
761        call_site: Span,
762        _: Span,
763        _: String,
764    ) -> Result<TopSubtree, ProcMacroExpansionError> {
765        let tt::TokenTree::Leaf(macro_name) = &subtree.0[2] else {
766            return Err(ProcMacroExpansionError::Panic("incorrect input".to_owned()));
767        };
768        Ok(quote! { call_site =>
769            #[macro_export]
770            macro_rules! my_macro___ {
771                ($($token:tt)*) => {{
772                }};
773            }
774
775            pub use my_macro___ as #macro_name;
776
777            #subtree
778        })
779    }
780
781    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
782        other.type_id() == TypeId::of::<Self>()
783    }
784}
785
786// Pastes the attribute input as its output
787#[derive(Debug)]
788struct AttributeInputReplaceProcMacroExpander;
789impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
790    fn expand(
791        &self,
792        _: &TopSubtree,
793        attrs: Option<&TopSubtree>,
794        _: &Env,
795        _: Span,
796        _: Span,
797        _: Span,
798        _: String,
799    ) -> Result<TopSubtree, ProcMacroExpansionError> {
800        attrs
801            .cloned()
802            .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
803    }
804
805    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
806        other.type_id() == TypeId::of::<Self>()
807    }
808}
809
810#[derive(Debug)]
811struct Issue18840ProcMacroExpander;
812impl ProcMacroExpander for Issue18840ProcMacroExpander {
813    fn expand(
814        &self,
815        fn_: &TopSubtree,
816        _: Option<&TopSubtree>,
817        _: &Env,
818        def_site: Span,
819        _: Span,
820        _: Span,
821        _: String,
822    ) -> Result<TopSubtree, ProcMacroExpansionError> {
823        // Input:
824        // ```
825        // #[issue_18840]
826        // fn foo() { let loop {} }
827        // ```
828
829        // The span that was created by the fixup infra.
830        let fixed_up_span = fn_.token_trees().flat_tokens()[5].first_span();
831        let mut result =
832            quote! {fixed_up_span => ::core::compile_error! { "my cool compile_error!" } };
833        // Make it so we won't remove the top subtree when reversing fixups.
834        let top_subtree_delimiter_mut = result.top_subtree_delimiter_mut();
835        top_subtree_delimiter_mut.open = def_site;
836        top_subtree_delimiter_mut.close = def_site;
837        Ok(result)
838    }
839
840    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
841        other.type_id() == TypeId::of::<Self>()
842    }
843}
844
845#[derive(Debug)]
846struct MirrorProcMacroExpander;
847impl ProcMacroExpander for MirrorProcMacroExpander {
848    fn expand(
849        &self,
850        input: &TopSubtree,
851        _: Option<&TopSubtree>,
852        _: &Env,
853        _: Span,
854        _: Span,
855        _: Span,
856        _: String,
857    ) -> Result<TopSubtree, ProcMacroExpansionError> {
858        fn traverse(builder: &mut TopSubtreeBuilder, iter: TtIter<'_>) {
859            for tt in iter.collect_vec().into_iter().rev() {
860                match tt {
861                    TtElement::Leaf(leaf) => builder.push(leaf.clone()),
862                    TtElement::Subtree(subtree, subtree_iter) => {
863                        builder.open(subtree.delimiter.kind, subtree.delimiter.open);
864                        traverse(builder, subtree_iter);
865                        builder.close(subtree.delimiter.close);
866                    }
867                }
868            }
869        }
870        let mut builder = TopSubtreeBuilder::new(input.top_subtree().delimiter);
871        traverse(&mut builder, input.iter());
872        Ok(builder.build())
873    }
874
875    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
876        other.type_id() == TypeId::of::<Self>()
877    }
878}
879
880// Replaces every literal with an empty string literal and every identifier with its first letter,
881// but retains all tokens' span. Useful for testing we don't assume token hasn't been modified by
882// macros even if it retains its span.
883#[derive(Debug)]
884struct ShortenProcMacroExpander;
885impl ProcMacroExpander for ShortenProcMacroExpander {
886    fn expand(
887        &self,
888        input: &TopSubtree,
889        _: Option<&TopSubtree>,
890        _: &Env,
891        _: Span,
892        _: Span,
893        _: Span,
894        _: String,
895    ) -> Result<TopSubtree, ProcMacroExpansionError> {
896        let mut result = input.0.clone();
897        for it in &mut result {
898            if let TokenTree::Leaf(leaf) = it {
899                modify_leaf(leaf)
900            }
901        }
902        return Ok(tt::TopSubtree(result));
903
904        fn modify_leaf(leaf: &mut Leaf) {
905            match leaf {
906                Leaf::Literal(it) => {
907                    // XXX Currently replaces any literals with an empty string, but supporting
908                    // "shortening" other literals would be nice.
909                    it.symbol = Symbol::empty();
910                }
911                Leaf::Punct(_) => {}
912                Leaf::Ident(it) => {
913                    it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::<String>());
914                }
915            }
916        }
917    }
918
919    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
920        other.type_id() == TypeId::of::<Self>()
921    }
922}
923
924// Reads ident type within string quotes, for issue #17479.
925#[derive(Debug)]
926struct Issue17479ProcMacroExpander;
927impl ProcMacroExpander for Issue17479ProcMacroExpander {
928    fn expand(
929        &self,
930        subtree: &TopSubtree,
931        _: Option<&TopSubtree>,
932        _: &Env,
933        _: Span,
934        _: Span,
935        _: Span,
936        _: String,
937    ) -> Result<TopSubtree, ProcMacroExpansionError> {
938        let TokenTree::Leaf(Leaf::Literal(lit)) = &subtree.0[1] else {
939            return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
940        };
941        let symbol = &lit.symbol;
942        let span = lit.span;
943        Ok(quote! { span =>
944            #symbol()
945        })
946    }
947
948    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
949        other.type_id() == TypeId::of::<Self>()
950    }
951}
952
953// Reads ident type within string quotes, for issue #17479.
954#[derive(Debug)]
955struct Issue18898ProcMacroExpander;
956impl ProcMacroExpander for Issue18898ProcMacroExpander {
957    fn expand(
958        &self,
959        subtree: &TopSubtree,
960        _: Option<&TopSubtree>,
961        _: &Env,
962        def_site: Span,
963        _: Span,
964        _: Span,
965        _: String,
966    ) -> Result<TopSubtree, ProcMacroExpansionError> {
967        let span = subtree
968            .token_trees()
969            .flat_tokens()
970            .last()
971            .ok_or_else(|| ProcMacroExpansionError::Panic("malformed input".to_owned()))?
972            .first_span();
973        let overly_long_subtree = quote! {span =>
974            {
975                let a = 5;
976                let a = 5;
977                let a = 5;
978                let a = 5;
979                let a = 5;
980                let a = 5;
981                let a = 5;
982                let a = 5;
983                let a = 5;
984                let a = 5;
985                let a = 5;
986                let a = 5;
987                let a = 5;
988                let a = 5;
989                let a = 5;
990                let a = 5;
991                let a = 5;
992                let a = 5;
993                let a = 5;
994            }
995        };
996        Ok(quote! { def_site =>
997            fn foo() {
998                #overly_long_subtree
999            }
1000        })
1001    }
1002
1003    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1004        other.type_id() == TypeId::of::<Self>()
1005    }
1006}
1007
1008// Reads ident type within string quotes, for issue #17479.
1009#[derive(Debug)]
1010struct DisallowCfgProcMacroExpander;
1011impl ProcMacroExpander for DisallowCfgProcMacroExpander {
1012    fn expand(
1013        &self,
1014        subtree: &TopSubtree,
1015        _: Option<&TopSubtree>,
1016        _: &Env,
1017        _: Span,
1018        _: Span,
1019        _: Span,
1020        _: String,
1021    ) -> Result<TopSubtree, ProcMacroExpansionError> {
1022        for tt in subtree.token_trees().flat_tokens() {
1023            if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt
1024                && (ident.sym == sym::cfg || ident.sym == sym::cfg_attr)
1025            {
1026                return Err(ProcMacroExpansionError::Panic(
1027                    "cfg or cfg_attr found in DisallowCfgProcMacroExpander".to_owned(),
1028                ));
1029            }
1030        }
1031        Ok(subtree.clone())
1032    }
1033
1034    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1035        other.type_id() == TypeId::of::<Self>()
1036    }
1037}
1038
1039// Generates a new type by adding a suffix to the original name
1040#[derive(Debug)]
1041struct GenerateSuffixedTypeProcMacroExpander;
1042impl ProcMacroExpander for GenerateSuffixedTypeProcMacroExpander {
1043    fn expand(
1044        &self,
1045        subtree: &TopSubtree,
1046        _attrs: Option<&TopSubtree>,
1047        _env: &Env,
1048        _def_site: Span,
1049        call_site: Span,
1050        _mixed_site: Span,
1051        _current_dir: String,
1052    ) -> Result<TopSubtree, ProcMacroExpansionError> {
1053        let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[1] else {
1054            return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1055        };
1056
1057        let ident = match ident.sym.as_str() {
1058            "struct" => {
1059                let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[2] else {
1060                    return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1061                };
1062                ident
1063            }
1064
1065            "enum" => {
1066                let TokenTree::Leaf(Leaf::Ident(ident)) = &subtree.0[4] else {
1067                    return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1068                };
1069                ident
1070            }
1071
1072            _ => {
1073                return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1074            }
1075        };
1076
1077        let generated_ident = tt::Ident {
1078            sym: Symbol::intern(&format!("{}Suffix", ident.sym)),
1079            span: ident.span,
1080            is_raw: tt::IdentIsRaw::No,
1081        };
1082
1083        let ret = quote! { call_site =>
1084            #subtree
1085
1086            struct #generated_ident;
1087        };
1088
1089        Ok(ret)
1090    }
1091
1092    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1093        other.type_id() == TypeId::of::<Self>()
1094    }
1095}