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