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