1#![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 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 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 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 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#[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#[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#[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 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 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#[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 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#[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#[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#[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#[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}