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 {
143 #[track_caller]
145 fn with_single_file(
146 #[rust_analyzer::rust_fixture] ra_fixture: &str,
147 ) -> (Self, EditionedFileId) {
148 let mut db = Self::default();
149 let fixture = ChangeFixture::parse(ra_fixture);
150 fixture.change.apply(&mut db);
151 assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture");
152 let file = EditionedFileId::from_span_guess_origin(&db, fixture.files[0]);
153 (db, file)
154 }
155
156 #[track_caller]
158 fn with_many_files(
159 #[rust_analyzer::rust_fixture] ra_fixture: &str,
160 ) -> (Self, Vec<EditionedFileId>) {
161 let mut db = Self::default();
162 let fixture = ChangeFixture::parse(ra_fixture);
163 fixture.change.apply(&mut db);
164 assert!(fixture.file_position.is_none());
165 let files = fixture
166 .files
167 .into_iter()
168 .map(|file| EditionedFileId::from_span_guess_origin(&db, file))
169 .collect();
170 (db, files)
171 }
172
173 #[track_caller]
175 fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
176 let mut db = Self::default();
177 let fixture = ChangeFixture::parse(ra_fixture);
178 fixture.change.apply(&mut db);
179 assert!(fixture.file_position.is_none());
180 db
181 }
182
183 #[track_caller]
185 fn with_files_extra_proc_macros(
186 #[rust_analyzer::rust_fixture] ra_fixture: &str,
187 proc_macros: Vec<(String, ProcMacro)>,
188 ) -> Self {
189 let mut db = Self::default();
190 let fixture =
191 ChangeFixture::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, proc_macros);
192 fixture.change.apply(&mut db);
193 assert!(fixture.file_position.is_none());
194 db
195 }
196
197 #[track_caller]
199 fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) {
200 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
201 let offset = range_or_offset.expect_offset();
202 (db, FilePosition { file_id, offset })
203 }
204
205 #[track_caller]
207 fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) {
208 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
209 let range = range_or_offset.expect_range();
210 (db, FileRange { file_id, range })
211 }
212
213 #[track_caller]
215 fn with_range_or_offset(
216 #[rust_analyzer::rust_fixture] ra_fixture: &str,
217 ) -> (Self, EditionedFileId, RangeOrOffset) {
218 let mut db = Self::default();
219 let fixture = ChangeFixture::parse(ra_fixture);
220 fixture.change.apply(&mut db);
221
222 let (file_id, range_or_offset) = fixture
223 .file_position
224 .expect("Could not find file position in fixture. Did you forget to add an `$0`?");
225 let file_id = EditionedFileId::from_span_guess_origin(&db, file_id);
226 (db, file_id, range_or_offset)
227 }
228
229 fn test_crate(&self) -> Crate {
230 self.all_crates().iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap()
231 }
232}
233
234impl<DB: ExpandDatabase + SourceDatabase + Default + 'static> WithFixture for DB {}
235
236pub struct ChangeFixture {
237 pub file_position: Option<(span::EditionedFileId, RangeOrOffset)>,
238 pub file_lines: Vec<usize>,
239 pub files: Vec<span::EditionedFileId>,
240 pub change: ChangeWithProcMacros,
241 pub sysroot_files: Vec<FileId>,
242}
243
244const SOURCE_ROOT_PREFIX: &str = "/";
245
246impl ChangeFixture {
247 pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> ChangeFixture {
248 Self::parse_with_proc_macros(ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
249 }
250
251 pub fn parse_with_proc_macros(
252 #[rust_analyzer::rust_fixture] ra_fixture: &str,
253 minicore_raw: &str,
254 mut proc_macro_defs: Vec<(String, ProcMacro)>,
255 ) -> ChangeFixture {
256 let FixtureWithProjectMeta {
257 fixture,
258 mini_core,
259 proc_macro_names,
260 toolchain,
261 target_data_layout,
262 target_arch,
263 } = FixtureWithProjectMeta::parse(ra_fixture);
264 let target_data_layout = target_data_layout.into();
265 let target_arch = parse_target_arch(&target_arch);
266 let target = Ok(TargetData { arch: target_arch, data_layout: target_data_layout });
267 let toolchain = Some({
268 let channel = toolchain.as_deref().unwrap_or("stable");
269 Version::parse(&format!("1.76.0-{channel}")).unwrap()
270 });
271 let mut source_change = FileChange::default();
272
273 let mut files = Vec::new();
274 let mut sysroot_files = Vec::new();
275 let mut file_lines = Vec::new();
276 let mut crate_graph = CrateGraphBuilder::default();
277 let mut crates = FxIndexMap::default();
278 let mut crate_deps = Vec::new();
279 let mut default_crate_root: Option<FileId> = None;
280 let mut default_edition = Edition::CURRENT;
281 let mut default_cfg = CfgOptions::default();
282 let mut default_env = Env::from_iter([(
283 String::from("__ra_is_test_fixture"),
284 String::from("__ra_is_test_fixture"),
285 )]);
286
287 let mut file_set = FileSet::default();
288 let mut current_source_root_kind = SourceRootKind::Local;
289 let mut file_id = FileId::from_raw(0);
290 let mut roots = Vec::new();
291
292 let mut file_position = None;
293
294 let crate_ws_data = Arc::new(CrateWorkspaceData { target, toolchain });
295
296 let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()));
298
299 for entry in fixture {
300 file_lines.push(entry.line);
301
302 let mut range_or_offset = None;
303 let text = if entry.text.contains(CURSOR_MARKER) {
304 if entry.text.contains(ESCAPED_CURSOR_MARKER) {
305 entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER)
306 } else {
307 let (roo, text) = extract_range_or_offset(&entry.text);
308 assert!(file_position.is_none());
309 range_or_offset = Some(roo);
310 text
311 }
312 } else {
313 entry.text.as_str().into()
314 };
315
316 let meta = FileMeta::from_fixture(entry, current_source_root_kind);
317 if let Some(range_or_offset) = range_or_offset {
318 file_position =
319 Some((span::EditionedFileId::new(file_id, meta.edition), range_or_offset));
320 }
321
322 assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX));
323 if !meta.deps.is_empty() {
324 assert!(meta.krate.is_some(), "can't specify deps without naming the crate")
325 }
326
327 if let Some(kind) = meta.introduce_new_source_root {
328 assert!(
329 meta.krate.is_some(),
330 "new_source_root meta doesn't make sense without crate meta"
331 );
332 let prev_kind = mem::replace(&mut current_source_root_kind, kind);
333 let prev_root = match prev_kind {
334 SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)),
335 SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)),
336 };
337 roots.push(prev_root);
338 }
339
340 if let Some((krate, origin, version)) = meta.krate {
341 let crate_name = CrateName::normalize_dashes(&krate);
342 let crate_id = crate_graph.add_crate_root(
343 file_id,
344 meta.edition,
345 Some(crate_name.clone().into()),
346 version,
347 meta.cfg.clone(),
348 Some(meta.cfg),
349 meta.env,
350 origin,
351 meta.crate_attrs,
352 false,
353 proc_macro_cwd.clone(),
354 crate_ws_data.clone(),
355 );
356 let prev = crates.insert(crate_name.clone(), crate_id);
357 assert!(prev.is_none(), "multiple crates with same name: {crate_name}");
358 for dep in meta.deps {
359 let prelude = match &meta.extern_prelude {
360 Some(v) => v.contains(&dep),
361 None => true,
362 };
363 let dep = CrateName::normalize_dashes(&dep);
364 crate_deps.push((crate_name.clone(), dep, prelude))
365 }
366 } else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
367 assert!(default_crate_root.is_none());
368 default_crate_root = Some(file_id);
369 default_edition = meta.edition;
370 default_cfg.append(meta.cfg);
371 default_env.extend_from_other(&meta.env);
372 }
373
374 source_change.change_file(file_id, Some(text));
375 let path = VfsPath::new_virtual_path(meta.path);
376 file_set.insert(file_id, path);
377 files.push(span::EditionedFileId::new(file_id, meta.edition));
378 file_id = FileId::from_raw(file_id.index() + 1);
379 }
380
381 let mini_core = mini_core.map(|mini_core| {
382 let core_file = file_id;
383 file_id = FileId::from_raw(file_id.index() + 1);
384
385 let mut fs = FileSet::default();
386 fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_owned()));
387 roots.push(SourceRoot::new_library(fs));
388
389 sysroot_files.push(core_file);
390
391 source_change.change_file(core_file, Some(mini_core.source_code(minicore_raw)));
392
393 let core_crate = crate_graph.add_crate_root(
394 core_file,
395 Edition::CURRENT,
396 Some(CrateDisplayName::from_canonical_name("core")),
397 None,
398 Default::default(),
399 Default::default(),
400 Env::from_iter([(
401 String::from("__ra_is_test_fixture"),
402 String::from("__ra_is_test_fixture"),
403 )]),
404 CrateOrigin::Lang(LangCrateOrigin::Core),
405 Vec::new(),
406 false,
407 proc_macro_cwd.clone(),
408 crate_ws_data.clone(),
409 );
410
411 (
412 move || {
413 DependencyBuilder::with_prelude(
414 CrateName::new("core").unwrap(),
415 core_crate,
416 true,
417 true,
418 )
419 },
420 core_crate,
421 )
422 });
423
424 if crates.is_empty() {
425 let crate_root = default_crate_root
426 .expect("missing default crate root, specify a main.rs or lib.rs");
427 let root = crate_graph.add_crate_root(
428 crate_root,
429 default_edition,
430 Some(CrateName::new("ra_test_fixture").unwrap().into()),
431 None,
432 default_cfg.clone(),
433 Some(default_cfg),
434 default_env,
435 CrateOrigin::Local { repo: None, name: None },
436 Vec::new(),
437 false,
438 proc_macro_cwd.clone(),
439 crate_ws_data.clone(),
440 );
441 if let Some((mini_core, _)) = mini_core {
442 crate_graph.add_dep(root, mini_core()).unwrap();
443 }
444 } else {
445 if let Some((mini_core, core_crate)) = mini_core {
447 let all_crates = crate_graph.iter().collect::<Vec<_>>();
448 for krate in all_crates {
449 if krate == core_crate {
450 continue;
451 }
452 crate_graph.add_dep(krate, mini_core()).unwrap();
453 }
454 }
455
456 for (from, to, prelude) in crate_deps {
457 let from_id = crates[&from];
458 let to_id = crates[&to];
459 let sysroot = crate_graph[to_id].basic.origin.is_lang();
460 crate_graph
461 .add_dep(
462 from_id,
463 DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot),
464 )
465 .unwrap();
466 }
467 }
468
469 let mut proc_macros = ProcMacrosBuilder::default();
470 if !proc_macro_names.is_empty() {
471 let proc_lib_file = file_id;
472
473 proc_macro_defs.extend(default_test_proc_macros());
474 let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs);
475 let mut fs = FileSet::default();
476 fs.insert(
477 proc_lib_file,
478 VfsPath::new_virtual_path("/sysroot/proc_macros/lib.rs".to_owned()),
479 );
480 roots.push(SourceRoot::new_library(fs));
481
482 sysroot_files.push(proc_lib_file);
483
484 source_change.change_file(proc_lib_file, Some(source));
485
486 let all_crates = crate_graph.iter().collect::<Vec<_>>();
487
488 let proc_macros_crate = crate_graph.add_crate_root(
489 proc_lib_file,
490 Edition::CURRENT,
491 Some(CrateDisplayName::from_canonical_name("proc_macros")),
492 None,
493 Default::default(),
494 Default::default(),
495 Env::from_iter([(
496 String::from("__ra_is_test_fixture"),
497 String::from("__ra_is_test_fixture"),
498 )]),
499 CrateOrigin::Local { repo: None, name: None },
500 Vec::new(),
501 true,
502 proc_macro_cwd,
503 crate_ws_data,
504 );
505 proc_macros.insert(proc_macros_crate, Ok(proc_macro));
506
507 for krate in all_crates {
508 crate_graph
509 .add_dep(
510 krate,
511 DependencyBuilder::new(
512 CrateName::new("proc_macros").unwrap(),
513 proc_macros_crate,
514 ),
515 )
516 .unwrap();
517 }
518 }
519
520 let _ = file_id;
521
522 let root = match current_source_root_kind {
523 SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)),
524 SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)),
525 };
526 roots.push(root);
527
528 let mut change = ChangeWithProcMacros { source_change, proc_macros: Some(proc_macros) };
529
530 change.source_change.set_roots(roots);
531 change.source_change.set_crate_graph(crate_graph);
532
533 ChangeFixture { file_position, file_lines, files, change, sysroot_files }
534 }
535}
536
537fn parse_target_arch(arch: &str) -> base_db::target::Arch {
538 use base_db::target::Arch::*;
539 match arch {
540 "wasm32" => Wasm32,
541 "wasm64" => Wasm64,
542 _ => Other,
543 }
544}
545
546fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> {
547 Box::new([
548 (
549 r#"
550#[proc_macro_attribute]
551pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
552 item
553}
554"#
555 .into(),
556 ProcMacro {
557 name: Symbol::intern("identity"),
558 kind: ProcMacroKind::Attr,
559 expander: sync::Arc::new(IdentityProcMacroExpander),
560 disabled: false,
561 },
562 ),
563 (
564 r#"
565#[proc_macro_derive(DeriveIdentity)]
566pub fn derive_identity(item: TokenStream) -> TokenStream {
567 item
568}
569"#
570 .into(),
571 ProcMacro {
572 name: Symbol::intern("DeriveIdentity"),
573 kind: ProcMacroKind::CustomDerive,
574 expander: sync::Arc::new(IdentityProcMacroExpander),
575 disabled: false,
576 },
577 ),
578 (
579 r#"
580#[proc_macro_attribute]
581pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
582 attr
583}
584"#
585 .into(),
586 ProcMacro {
587 name: Symbol::intern("input_replace"),
588 kind: ProcMacroKind::Attr,
589 expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
590 disabled: false,
591 },
592 ),
593 (
594 r#"
595#[proc_macro]
596pub fn mirror(input: TokenStream) -> TokenStream {
597 input
598}
599"#
600 .into(),
601 ProcMacro {
602 name: Symbol::intern("mirror"),
603 kind: ProcMacroKind::Bang,
604 expander: sync::Arc::new(MirrorProcMacroExpander),
605 disabled: false,
606 },
607 ),
608 (
609 r#"
610#[proc_macro]
611pub fn shorten(input: TokenStream) -> TokenStream {
612 loop {}
613}
614"#
615 .into(),
616 ProcMacro {
617 name: Symbol::intern("shorten"),
618 kind: ProcMacroKind::Bang,
619 expander: sync::Arc::new(ShortenProcMacroExpander),
620 disabled: false,
621 },
622 ),
623 (
624 r#"
625#[proc_macro_attribute]
626pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream {
627 loop {}
628}
629"#
630 .into(),
631 ProcMacro {
632 name: Symbol::intern("issue_18089"),
633 kind: ProcMacroKind::Attr,
634 expander: sync::Arc::new(Issue18089ProcMacroExpander),
635 disabled: false,
636 },
637 ),
638 (
639 r#"
640#[proc_macro_attribute]
641pub fn issue_18840(_attr: TokenStream, _item: TokenStream) -> TokenStream {
642 loop {}
643}
644"#
645 .into(),
646 ProcMacro {
647 name: Symbol::intern("issue_18840"),
648 kind: ProcMacroKind::Attr,
649 expander: sync::Arc::new(Issue18840ProcMacroExpander),
650 disabled: false,
651 },
652 ),
653 (
654 r#"
655#[proc_macro]
656pub fn issue_17479(input: TokenStream) -> TokenStream {
657 input
658}
659"#
660 .into(),
661 ProcMacro {
662 name: Symbol::intern("issue_17479"),
663 kind: ProcMacroKind::Bang,
664 expander: sync::Arc::new(Issue17479ProcMacroExpander),
665 disabled: false,
666 },
667 ),
668 (
669 r#"
670#[proc_macro_attribute]
671pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream {
672 input
673}
674"#
675 .into(),
676 ProcMacro {
677 name: Symbol::intern("issue_18898"),
678 kind: ProcMacroKind::Bang,
679 expander: sync::Arc::new(Issue18898ProcMacroExpander),
680 disabled: false,
681 },
682 ),
683 (
684 r#"
685#[proc_macro_attribute]
686pub fn disallow_cfg(_attr: TokenStream, input: TokenStream) -> TokenStream {
687 input
688}
689"#
690 .into(),
691 ProcMacro {
692 name: Symbol::intern("disallow_cfg"),
693 kind: ProcMacroKind::Attr,
694 expander: sync::Arc::new(DisallowCfgProcMacroExpander),
695 disabled: false,
696 },
697 ),
698 (
699 r#"
700#[proc_macro_attribute]
701pub fn generate_suffixed_type(_attr: TokenStream, input: TokenStream) -> TokenStream {
702 input
703}
704"#
705 .into(),
706 ProcMacro {
707 name: Symbol::intern("generate_suffixed_type"),
708 kind: ProcMacroKind::Attr,
709 expander: sync::Arc::new(GenerateSuffixedTypeProcMacroExpander),
710 disabled: false,
711 },
712 ),
713 ])
714}
715
716fn filter_test_proc_macros(
717 proc_macro_names: &[String],
718 proc_macro_defs: Vec<(String, ProcMacro)>,
719) -> (Vec<ProcMacro>, String) {
720 let mut source = String::new();
722 let mut proc_macros = Vec::new();
723
724 for (c, p) in proc_macro_defs {
725 if !proc_macro_names.iter().any(|name| name == &stdx::to_lower_snake_case(p.name.as_str()))
726 {
727 continue;
728 }
729 proc_macros.push(p);
730 source += &c;
731 }
732
733 (proc_macros, source)
734}
735
736#[derive(Debug, Clone, Copy)]
737enum SourceRootKind {
738 Local,
739 Library,
740}
741
742#[derive(Debug)]
743struct FileMeta {
744 path: String,
745 krate: Option<(String, CrateOrigin, Option<String>)>,
746 deps: Vec<String>,
747 extern_prelude: Option<Vec<String>>,
748 cfg: CfgOptions,
749 edition: Edition,
750 env: Env,
751 crate_attrs: Vec<String>,
752 introduce_new_source_root: Option<SourceRootKind>,
753}
754
755impl FileMeta {
756 fn from_fixture(f: Fixture, current_source_root_kind: SourceRootKind) -> Self {
757 let mut cfg = CfgOptions::default();
758 for (k, v) in f.cfgs {
759 if let Some(v) = v {
760 cfg.insert_key_value(Symbol::intern(&k), Symbol::intern(&v));
761 } else {
762 cfg.insert_atom(Symbol::intern(&k));
763 }
764 }
765
766 let introduce_new_source_root = f.introduce_new_source_root.map(|kind| match &*kind {
767 "local" => SourceRootKind::Local,
768 "library" => SourceRootKind::Library,
769 invalid => panic!("invalid source root kind '{invalid}'"),
770 });
771 let current_source_root_kind =
772 introduce_new_source_root.unwrap_or(current_source_root_kind);
773
774 let deps = f.deps;
775 Self {
776 path: f.path,
777 krate: f.krate.map(|it| parse_crate(it, current_source_root_kind, f.library)),
778 extern_prelude: f.extern_prelude,
779 deps,
780 cfg,
781 edition: f.edition.map_or(Edition::CURRENT, |v| Edition::from_str(&v).unwrap()),
782 env: f.env.into_iter().collect(),
783 crate_attrs: f.crate_attrs,
784 introduce_new_source_root,
785 }
786 }
787}
788
789#[derive(Debug, Clone, Copy, PartialEq, Eq)]
790enum ForceNoneLangOrigin {
791 Yes,
792 No,
793}
794
795fn parse_crate(
796 crate_str: String,
797 current_source_root_kind: SourceRootKind,
798 explicit_non_workspace_member: bool,
799) -> (String, CrateOrigin, Option<String>) {
800 let (crate_str, force_non_lang_origin) = if let Some(s) = crate_str.strip_prefix("r#") {
801 (s.to_owned(), ForceNoneLangOrigin::Yes)
802 } else {
803 (crate_str, ForceNoneLangOrigin::No)
804 };
805
806 let (name, repo, version) = if let Some((name, remain)) = crate_str.split_once('@') {
810 let (version, repo) =
811 remain.split_once(',').expect("crate meta: found '@' without version and url");
812 (name.to_owned(), Some(repo.to_owned()), Some(version.to_owned()))
813 } else {
814 (crate_str, None, None)
815 };
816
817 let non_workspace_member = explicit_non_workspace_member
818 || matches!(current_source_root_kind, SourceRootKind::Library);
819
820 let origin = if force_non_lang_origin == ForceNoneLangOrigin::Yes {
821 let name = Symbol::intern(&name);
822 if non_workspace_member {
823 CrateOrigin::Library { repo, name }
824 } else {
825 CrateOrigin::Local { repo, name: Some(name) }
826 }
827 } else {
828 match LangCrateOrigin::from(&*name) {
829 LangCrateOrigin::Other => {
830 let name = Symbol::intern(&name);
831 if non_workspace_member {
832 CrateOrigin::Library { repo, name }
833 } else {
834 CrateOrigin::Local { repo, name: Some(name) }
835 }
836 }
837 origin => CrateOrigin::Lang(origin),
838 }
839 };
840
841 (name, origin, version)
842}
843
844#[derive(Debug)]
846struct IdentityProcMacroExpander;
847impl ProcMacroExpander for IdentityProcMacroExpander {
848 fn expand(
849 &self,
850 _: &dyn ExpandDatabase,
851 subtree: &TopSubtree,
852 _: Option<&TopSubtree>,
853 _: &Env,
854 _: Span,
855 _: Span,
856 _: Span,
857 _: String,
858 ) -> Result<TopSubtree, ProcMacroExpansionError> {
859 Ok(subtree.clone())
860 }
861
862 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
863 other.type_id() == TypeId::of::<Self>()
864 }
865}
866
867#[derive(Debug)]
869struct Issue18089ProcMacroExpander;
870impl ProcMacroExpander for Issue18089ProcMacroExpander {
871 fn expand(
872 &self,
873 _: &dyn ExpandDatabase,
874 subtree: &TopSubtree,
875 _: Option<&TopSubtree>,
876 _: &Env,
877 _: Span,
878 call_site: Span,
879 _: Span,
880 _: String,
881 ) -> Result<TopSubtree, ProcMacroExpansionError> {
882 let Some(tt::TtElement::Leaf(macro_name)) = subtree.iter().nth(1) else {
883 return Err(ProcMacroExpansionError::Panic("incorrect input".to_owned()));
884 };
885 Ok(quote! { call_site =>
886 #[macro_export]
887 macro_rules! my_macro___ {
888 ($($token:tt)*) => {{
889 }};
890 }
891
892 pub use my_macro___ as #macro_name;
893
894 #subtree
895 })
896 }
897
898 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
899 other.type_id() == TypeId::of::<Self>()
900 }
901}
902
903#[derive(Debug)]
905struct AttributeInputReplaceProcMacroExpander;
906impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
907 fn expand(
908 &self,
909 _: &dyn ExpandDatabase,
910 _: &TopSubtree,
911 attrs: Option<&TopSubtree>,
912 _: &Env,
913 _: Span,
914 _: Span,
915 _: Span,
916 _: String,
917 ) -> Result<TopSubtree, ProcMacroExpansionError> {
918 attrs
919 .cloned()
920 .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
921 }
922
923 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
924 other.type_id() == TypeId::of::<Self>()
925 }
926}
927
928#[derive(Debug)]
929struct Issue18840ProcMacroExpander;
930impl ProcMacroExpander for Issue18840ProcMacroExpander {
931 fn expand(
932 &self,
933 _: &dyn ExpandDatabase,
934 fn_: &TopSubtree,
935 _: Option<&TopSubtree>,
936 _: &Env,
937 def_site: Span,
938 _: Span,
939 _: Span,
940 _: String,
941 ) -> Result<TopSubtree, ProcMacroExpansionError> {
942 let mut iter = fn_.iter();
950 iter.nth(2);
951 let (_, mut fn_body) = iter.expect_subtree().unwrap();
952 let fixed_up_span = fn_body.nth(1).unwrap().first_span();
953 let mut result =
954 quote! {fixed_up_span => ::core::compile_error! { "my cool compile_error!" } };
955 result.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(def_site));
957 Ok(result)
958 }
959
960 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
961 other.type_id() == TypeId::of::<Self>()
962 }
963}
964
965#[derive(Debug)]
966struct MirrorProcMacroExpander;
967impl ProcMacroExpander for MirrorProcMacroExpander {
968 fn expand(
969 &self,
970 _: &dyn ExpandDatabase,
971 input: &TopSubtree,
972 _: Option<&TopSubtree>,
973 _: &Env,
974 _: Span,
975 _: Span,
976 _: Span,
977 _: String,
978 ) -> Result<TopSubtree, ProcMacroExpansionError> {
979 fn traverse(builder: &mut TopSubtreeBuilder, iter: TtIter<'_>) {
980 for tt in iter.collect_vec().into_iter().rev() {
981 match tt {
982 TtElement::Leaf(leaf) => builder.push(leaf.clone()),
983 TtElement::Subtree(subtree, subtree_iter) => {
984 builder.open(subtree.delimiter.kind, subtree.delimiter.open);
985 traverse(builder, subtree_iter);
986 builder.close(subtree.delimiter.close);
987 }
988 }
989 }
990 }
991 let mut builder = TopSubtreeBuilder::new(input.top_subtree().delimiter);
992 traverse(&mut builder, input.iter());
993 Ok(builder.build())
994 }
995
996 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
997 other.type_id() == TypeId::of::<Self>()
998 }
999}
1000
1001#[derive(Debug)]
1005struct ShortenProcMacroExpander;
1006impl ProcMacroExpander for ShortenProcMacroExpander {
1007 fn expand(
1008 &self,
1009 _: &dyn ExpandDatabase,
1010 input: &TopSubtree,
1011 _: Option<&TopSubtree>,
1012 _: &Env,
1013 _: Span,
1014 _: Span,
1015 _: Span,
1016 _: String,
1017 ) -> Result<TopSubtree, ProcMacroExpansionError> {
1018 let mut result = input.clone();
1019 for (idx, it) in input.as_token_trees().iter_flat_tokens().enumerate() {
1020 if let TokenTree::Leaf(mut leaf) = it {
1021 modify_leaf(&mut leaf);
1022 result.set_token(idx, leaf);
1023 }
1024 }
1025 return Ok(result);
1026
1027 fn modify_leaf(leaf: &mut Leaf) {
1028 match leaf {
1029 Leaf::Literal(it) => {
1030 it.text_and_suffix = Symbol::empty();
1033 it.suffix_len = 0;
1034 }
1035 Leaf::Punct(_) => {}
1036 Leaf::Ident(it) => {
1037 it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::<String>());
1038 }
1039 }
1040 }
1041 }
1042
1043 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1044 other.type_id() == TypeId::of::<Self>()
1045 }
1046}
1047
1048#[derive(Debug)]
1050struct Issue17479ProcMacroExpander;
1051impl ProcMacroExpander for Issue17479ProcMacroExpander {
1052 fn expand(
1053 &self,
1054 _: &dyn ExpandDatabase,
1055 subtree: &TopSubtree,
1056 _: Option<&TopSubtree>,
1057 _: &Env,
1058 _: Span,
1059 _: Span,
1060 _: Span,
1061 _: String,
1062 ) -> Result<TopSubtree, ProcMacroExpansionError> {
1063 let mut iter = subtree.iter();
1064 let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = iter.next() else {
1065 return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1066 };
1067 let symbol = Symbol::intern(lit.text());
1068 let span = lit.span;
1069 Ok(quote! { span =>
1070 #symbol()
1071 })
1072 }
1073
1074 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1075 other.type_id() == TypeId::of::<Self>()
1076 }
1077}
1078
1079#[derive(Debug)]
1081struct Issue18898ProcMacroExpander;
1082impl ProcMacroExpander for Issue18898ProcMacroExpander {
1083 fn expand(
1084 &self,
1085 _: &dyn ExpandDatabase,
1086 subtree: &TopSubtree,
1087 _: Option<&TopSubtree>,
1088 _: &Env,
1089 def_site: Span,
1090 _: Span,
1091 _: Span,
1092 _: String,
1093 ) -> Result<TopSubtree, ProcMacroExpansionError> {
1094 let span = subtree
1095 .token_trees()
1096 .last_span()
1097 .ok_or_else(|| ProcMacroExpansionError::Panic("malformed input".to_owned()))?;
1098 let overly_long_subtree = quote! {span =>
1099 {
1100 let a = 5;
1101 let a = 5;
1102 let a = 5;
1103 let a = 5;
1104 let a = 5;
1105 let a = 5;
1106 let a = 5;
1107 let a = 5;
1108 let a = 5;
1109 let a = 5;
1110 let a = 5;
1111 let a = 5;
1112 let a = 5;
1113 let a = 5;
1114 let a = 5;
1115 let a = 5;
1116 let a = 5;
1117 let a = 5;
1118 let a = 5;
1119 }
1120 };
1121 Ok(quote! { def_site =>
1122 fn foo() {
1123 #overly_long_subtree
1124 }
1125 })
1126 }
1127
1128 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1129 other.type_id() == TypeId::of::<Self>()
1130 }
1131}
1132
1133#[derive(Debug)]
1135struct DisallowCfgProcMacroExpander;
1136impl ProcMacroExpander for DisallowCfgProcMacroExpander {
1137 fn expand(
1138 &self,
1139 _: &dyn ExpandDatabase,
1140 subtree: &TopSubtree,
1141 _: Option<&TopSubtree>,
1142 _: &Env,
1143 _: Span,
1144 _: Span,
1145 _: Span,
1146 _: String,
1147 ) -> Result<TopSubtree, ProcMacroExpansionError> {
1148 for tt in subtree.token_trees().iter_flat_tokens() {
1149 if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt
1150 && (ident.sym == sym::cfg || ident.sym == sym::cfg_attr)
1151 {
1152 return Err(ProcMacroExpansionError::Panic(
1153 "cfg or cfg_attr found in DisallowCfgProcMacroExpander".to_owned(),
1154 ));
1155 }
1156 }
1157 Ok(subtree.clone())
1158 }
1159
1160 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1161 other.type_id() == TypeId::of::<Self>()
1162 }
1163}
1164
1165#[derive(Debug)]
1167struct GenerateSuffixedTypeProcMacroExpander;
1168impl ProcMacroExpander for GenerateSuffixedTypeProcMacroExpander {
1169 fn expand(
1170 &self,
1171 _: &dyn ExpandDatabase,
1172 subtree: &TopSubtree,
1173 _attrs: Option<&TopSubtree>,
1174 _env: &Env,
1175 _def_site: Span,
1176 call_site: Span,
1177 _mixed_site: Span,
1178 _current_dir: String,
1179 ) -> Result<TopSubtree, ProcMacroExpansionError> {
1180 let mut iter = subtree.iter();
1181 let Some(TtElement::Leaf(tt::Leaf::Ident(ident))) = iter.next() else {
1182 return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1183 };
1184
1185 let ident = match ident.sym.as_str() {
1186 "struct" => {
1187 let Some(TtElement::Leaf(tt::Leaf::Ident(ident))) = iter.next() else {
1188 return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1189 };
1190 ident
1191 }
1192
1193 "enum" => {
1194 iter.next();
1195 let (_, mut iter) = iter.expect_subtree().unwrap();
1196 let Some(TtElement::Leaf(tt::Leaf::Ident(ident))) = iter.next() else {
1197 return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1198 };
1199 ident
1200 }
1201
1202 _ => {
1203 return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
1204 }
1205 };
1206
1207 let generated_ident = tt::Ident {
1208 sym: Symbol::intern(&format!("{}Suffix", ident.sym)),
1209 span: ident.span,
1210 is_raw: tt::IdentIsRaw::No,
1211 };
1212
1213 let ret = quote! { call_site =>
1214 #subtree
1215
1216 struct #generated_ident;
1217 };
1218
1219 Ok(ret)
1220 }
1221
1222 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
1223 other.type_id() == TypeId::of::<Self>()
1224 }
1225}