1use std::{ops::Deref, path::PathBuf, sync::Arc};
2
3use anyhow::Context as _;
4use dashmap::DashMap;
5use faststr::FastStr;
6use fxhash::{FxHashMap, FxHashSet};
7use heck::ToShoutySnakeCase;
8use itertools::Itertools;
9use normpath::PathExt;
10use quote::format_ident;
11use salsa::ParallelDatabase;
12
13use crate::{
14 db::{RirDatabase, RootDatabase},
15 Plugin,
16 rir::{self, Field, Item, ItemPath, Literal},
17 symbol::{DefId, IdentName, Symbol},
18 tags::{EnumMode, TagId, Tags},
19 ty::{AdtDef, AdtKind, CodegenTy, Visitor},
20};
21
22use super::{
23 adjust::Adjust,
24 resolver::{DefaultPathResolver, PathResolver, WorkspacePathResolver},
25 rir::NodeKind,
26};
27
28use self::tls::{CUR_ITEM, with_cur_item};
29
30#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
31pub(crate) enum DefLocation {
32 Fixed(ItemPath),
33 Dynamic,
34}
35
36pub enum CollectMode {
37 All,
38 OnlyUsed {
39 touches: Vec<(std::path::PathBuf, Vec<String>)>,
40 },
41}
42
43#[derive(Debug)]
44pub struct WorkspaceInfo {
45 pub(crate) dir: PathBuf,
46 pub(crate) location_map: FxHashMap<DefId, DefLocation>,
47}
48
49#[derive(Debug)]
50pub enum Mode {
51 Workspace(WorkspaceInfo),
52 SingleFile { file_path: std::path::PathBuf },
53}
54
55pub struct Context {
56 pub source_type: SourceType,
57 pub db: salsa::Snapshot<RootDatabase>,
58 pub adjusts: Arc<DashMap<DefId, Adjust>>,
59 pub services: Arc<[crate::IdlService]>,
60 pub(crate) change_case: bool,
61 pub(crate) codegen_items: Arc<Vec<DefId>>,
62 pub(crate) path_resolver: Arc<dyn PathResolver>,
63 pub(crate) mode: Arc<Mode>,
64 pub(crate) doc_header: Arc<String>,
65}
66
67impl Clone for Context {
68 fn clone(&self) -> Self {
69 Self {
70 source_type: self.source_type,
71 db: self.db.snapshot(),
72 adjusts: self.adjusts.clone(),
73 change_case: self.change_case,
74 codegen_items: self.codegen_items.clone(),
75 path_resolver: self.path_resolver.clone(),
76 mode: self.mode.clone(),
77 services: self.services.clone(),
78 doc_header: self.doc_header.clone(),
79 }
80 }
81}
82
83pub(crate) struct ContextBuilder {
84 db: RootDatabase,
85 pub(crate) codegen_items: Vec<DefId>,
86 input_items: Vec<DefId>,
87 mode: Mode,
88}
89
90impl ContextBuilder {
91 pub fn new(db: RootDatabase, mode: Mode, input_items: Vec<DefId>) -> Self {
92 ContextBuilder {
93 db,
94 mode,
95 input_items,
96 codegen_items: Default::default(),
97 }
98 }
99 pub(crate) fn collect(&mut self, mode: CollectMode) {
100 match mode {
101 CollectMode::All => {
102 let nodes = self.db.nodes();
103 self.codegen_items.extend(nodes.iter().filter_map(|(k, v)| {
104 if let NodeKind::Item(i) = &v.kind {
105 if !matches!(&**i, Item::Mod(_)) {
106 Some(k)
107 } else {
108 None
109 }
110 } else {
111 None
112 }
113 }));
114 }
115 CollectMode::OnlyUsed { touches } => {
116 let extra_def_ids = touches
117 .into_iter()
118 .flat_map(|s| {
119 let path = s.0.normalize().unwrap().into_path_buf();
120 let file_id = *self.db.file_ids_map().get(&path).unwrap();
121 s.1.into_iter()
122 .filter_map(|item_name| {
123 let def_id = self
124 .db
125 .files()
126 .get(&file_id)
127 .unwrap()
128 .items
129 .iter()
130 .find(|def_id| {
131 *self.db.item(**def_id).unwrap().symbol_name() == item_name
132 })
133 .cloned();
134 if let Some(def_id) = def_id {
135 Some(def_id)
136 } else {
137 println!(
138 "cargo:warning=item `{}` of `{}` not exists",
139 item_name,
140 path.display(),
141 );
142 None
143 }
144 })
145 .collect::<Vec<_>>()
146 })
147 .collect::<Vec<_>>();
148
149 self.input_items.extend(extra_def_ids);
150
151 let def_ids = self.collect_items(&self.input_items);
152 self.codegen_items.extend(def_ids.iter());
153 }
154 }
155 if matches!(self.mode, Mode::Workspace(_)) {
156 let location_map = self.workspace_collect_def_ids(&self.codegen_items);
157
158 if let Mode::Workspace(info) = &mut self.mode {
159 info.location_map = location_map
160 }
161 }
162 }
163
164 pub(crate) fn collect_items(&self, input: &[DefId]) -> FxHashSet<DefId> {
165 struct PathCollector<'a> {
166 set: &'a mut FxHashSet<DefId>,
167 cx: &'a ContextBuilder,
168 }
169
170 impl super::ty::Visitor for PathCollector<'_> {
171 fn visit_path(&mut self, path: &crate::rir::Path) {
172 collect(self.cx, path.did, self.set)
173 }
174 }
175
176 fn collect(cx: &ContextBuilder, def_id: DefId, set: &mut FxHashSet<DefId>) {
177 if set.contains(&def_id) {
178 return;
179 }
180
181 let node = cx.db.node(def_id).unwrap();
182
183 match node.kind {
184 NodeKind::Item(_) => {}
185 _ => return collect(cx, node.parent.unwrap(), set),
186 }
187
188 if !matches!(&*cx.db.item(def_id).unwrap(), rir::Item::Mod(_)) {
189 set.insert(def_id);
190 }
191
192 let node = cx.db.node(def_id).unwrap();
193 tracing::trace!("collecting {:?}", node.expect_item().symbol_name());
194
195 node.related_nodes
196 .iter()
197 .for_each(|def_id| collect(cx, *def_id, set));
198
199 let item = node.expect_item();
200
201 match item {
202 rir::Item::Message(m) => m.fields.iter().for_each(|f| {
203 PathCollector { cx, set }.visit(&f.ty);
204 if let Some(Literal::Path(p)) = &f.default {
205 PathCollector { cx, set }.visit_path(&p);
206 }
207 }),
208 rir::Item::Enum(e) => e
209 .variants
210 .iter()
211 .flat_map(|v| &v.fields)
212 .for_each(|ty| PathCollector { cx, set }.visit(ty)),
213 rir::Item::Service(s) => {
214 s.extend.iter().for_each(|p| collect(cx, p.did, set));
215 s.methods
216 .iter()
217 .flat_map(|m| m.args.iter().map(|f| &f.ty).chain(std::iter::once(&m.ret)))
218 .for_each(|ty| PathCollector { cx, set }.visit(ty));
219 }
220 rir::Item::NewType(n) => PathCollector { cx, set }.visit(&n.ty),
221 rir::Item::Const(c) => {
222 PathCollector { cx, set }.visit(&c.ty);
223 }
224 rir::Item::Mod(m) => {
225 m.items.iter().for_each(|i| collect(cx, *i, set));
226 }
227 }
228 }
229 let mut set = FxHashSet::default();
230
231 input.iter().for_each(|def_id| {
232 collect(self, *def_id, &mut set);
233 });
234
235 self.db.nodes().iter().for_each(|(def_id, node)| {
236 if let NodeKind::Item(item) = &node.kind {
237 if let rir::Item::Const(_) = &**item {
238 collect(self, *def_id, &mut set);
239 }
240 }
241 });
242
243 set
244 }
245
246 pub(crate) fn workspace_collect_def_ids(
247 &self,
248 input: &[DefId],
249 ) -> FxHashMap<DefId, DefLocation> {
250 struct PathCollector<'a> {
251 map: &'a mut FxHashMap<DefId, DefLocation>,
252 cx: &'a ContextBuilder,
253 }
254
255 impl crate::ty::Visitor for PathCollector<'_> {
256 fn visit_path(&mut self, path: &crate::rir::Path) {
257 collect(self.cx, path.did, self.map)
258 }
259 }
260
261 fn collect(cx: &ContextBuilder, def_id: DefId, map: &mut FxHashMap<DefId, DefLocation>) {
262 if let Some(_location) = map.get_mut(&def_id) {
263 return;
264 }
265 if !matches!(&*cx.db.item(def_id).unwrap(), rir::Item::Mod(_)) {
266 let file_id = cx.db.node(def_id).unwrap().file_id;
267 if cx.db.input_files().contains(&file_id) {
268 let file = cx.db.file(file_id).unwrap();
269 map.insert(def_id, DefLocation::Fixed(file.package.clone()));
270 } else {
271 map.insert(def_id, DefLocation::Dynamic);
272 }
273 }
274
275 let node = cx.db.node(def_id).unwrap();
276 tracing::trace!("collecting {:?}", node.expect_item().symbol_name());
277
278 node.related_nodes
279 .iter()
280 .for_each(|def_id| collect(cx, *def_id, map));
281
282 let item = node.expect_item();
283
284 match item {
285 rir::Item::Message(m) => m
286 .fields
287 .iter()
288 .for_each(|f| PathCollector { cx, map }.visit(&f.ty)),
289 rir::Item::Enum(e) => e
290 .variants
291 .iter()
292 .flat_map(|v| &v.fields)
293 .for_each(|ty| PathCollector { cx, map }.visit(ty)),
294 rir::Item::Service(s) => {
295 s.extend.iter().for_each(|p| collect(cx, p.did, map));
296 s.methods
297 .iter()
298 .flat_map(|m| m.args.iter().map(|f| &f.ty).chain(std::iter::once(&m.ret)))
299 .for_each(|ty| PathCollector { cx, map }.visit(ty));
300 }
301 rir::Item::NewType(n) => PathCollector { cx, map }.visit(&n.ty),
302 rir::Item::Const(c) => {
303 PathCollector { cx, map }.visit(&c.ty);
304 }
305 rir::Item::Mod(m) => {
306 m.items.iter().for_each(|i| collect(cx, *i, map));
307 }
308 }
309 }
310 let mut map = FxHashMap::default();
311
312 input.iter().for_each(|def_id| {
313 collect(self, *def_id, &mut map);
314 });
315
316 map
317 }
318
319 pub(crate) fn build(
320 self,
321 services: Arc<[crate::IdlService]>,
322 source_type: SourceType,
323 change_case: bool,
324 doc_header: Arc<String>,
325 ) -> Context {
326 Context {
327 adjusts: Default::default(),
328 source_type,
329 db: self.db.snapshot(),
330 change_case,
331 services,
332 codegen_items: Arc::new(self.codegen_items),
333 path_resolver: match &self.mode {
334 Mode::Workspace(_) => Arc::new(WorkspacePathResolver),
335 Mode::SingleFile { .. } => Arc::new(DefaultPathResolver),
336 },
337 mode: Arc::new(self.mode),
338 doc_header,
339 }
340 }
341}
342
343impl Deref for Context {
344 type Target = salsa::Snapshot<RootDatabase>;
345
346 fn deref(&self) -> &Self::Target {
347 &self.db
348 }
349}
350
351#[derive(Clone, Copy)]
352pub enum SourceType {
353 Thrift,
354 Protobuf,
355}
356
357impl Context {
358 pub fn with_adjust<T, F>(&self, def_id: DefId, f: F) -> T
359 where
360 F: FnOnce(Option<&Adjust>) -> T,
361 {
362 match self.adjusts.get(&def_id) {
363 Some(adj) => f(Some(&*adj)),
364 None => f(None),
365 }
366 }
367
368 pub fn with_adjust_mut<T, F>(&self, def_id: DefId, f: F) -> T
369 where
370 F: FnOnce(&mut Adjust) -> T,
371 {
372 let adjust = &mut *self.adjusts.entry(def_id).or_insert_with(Default::default);
373 f(adjust)
374 }
375
376 pub fn tags(&self, tags_id: TagId) -> Option<Arc<Tags>> {
377 self.db.tags_map().get(&tags_id).cloned()
378 }
379
380 pub fn node_tags(&self, def_id: DefId) -> Option<Arc<Tags>> {
381 let tags_id = self.node(def_id).unwrap().tags;
382 self.tags(tags_id)
383 }
384
385 pub fn contains_tag<T: 'static>(&self, tags_id: TagId) -> bool {
386 self.tags(tags_id)
387 .and_then(|tags| tags.contains::<T>().then_some(true))
388 .is_some()
389 }
390
391 pub fn node_contains_tag<T: 'static>(&self, def_id: DefId) -> bool {
392 self.node_tags(def_id)
393 .and_then(|tags| tags.contains::<T>().then_some(true))
394 .is_some()
395 }
396
397 pub fn symbol_name(&self, def_id: DefId) -> Symbol {
398 let item = self.item(def_id).unwrap();
399 item.symbol_name()
400 }
401
402 pub fn default_val(&self, f: &Field) -> Option<(FastStr, bool )> {
403 f.default.as_ref().map(|d| {
404 let ty = self.codegen_item_ty(f.ty.kind.clone());
405 match self
406 .lit_as_rvalue(d, &ty)
407 .with_context(|| format!("calc the default value for field {}", f.name))
408 {
409 Ok(v) => v,
410 Err(err) => {
411 panic!("{:?}", err)
412 }
413 }
414 })
415 }
416
417 fn lit_as_rvalue(
418 &self,
419 lit: &Literal,
420 ty: &CodegenTy,
421 ) -> anyhow::Result<(FastStr, bool )> {
422 let mk_map = |m: &Vec<(Literal, Literal)>, k_ty: &Arc<CodegenTy>, v_ty: &Arc<CodegenTy>| {
423 let k_ty = &**k_ty;
424 let v_ty = &**v_ty;
425 let len = m.len();
426 let kvs = m
427 .iter()
428 .map(|(k, v)| {
429 let k = self.lit_into_ty(k, k_ty)?.0;
430 let v = self.lit_into_ty(v, v_ty)?.0;
431 anyhow::Ok(format!("map.insert({k}, {v});"))
432 })
433 .try_collect::<_, Vec<_>, _>()?
434 .join("");
435 anyhow::Ok(
436 format! {r#"{{
437 let mut map = ::std::collections::HashMap::with_capacity({len});
438 {kvs}
439 map
440 }}"#}
441 .into(),
442 )
443 };
444
445 anyhow::Ok(match (lit, ty) {
446 (Literal::Map(m), CodegenTy::LazyStaticRef(map)) => match &**map {
447 CodegenTy::Map(k_ty, v_ty) => (mk_map(m, k_ty, v_ty)?, false),
448 _ => panic!("invalid map type {:?}", map),
449 },
450 (Literal::Map(m), CodegenTy::Map(k_ty, v_ty)) => (mk_map(m, k_ty, v_ty)?, false),
451 _ => self.lit_into_ty(lit, ty)?,
452 })
453 }
454
455 fn ident_into_ty(
456 &self,
457 did: DefId,
458 ident_ty: &CodegenTy,
459 target: &CodegenTy,
460 ) -> (FastStr, bool ) {
461 if ident_ty == target {
462 let stream = self.cur_related_item_path(did);
463 return (stream, true);
464 }
465 match (ident_ty, target) {
466 (CodegenTy::Str, CodegenTy::String) => {
467 let stream = self.cur_related_item_path(did);
468 (
469 format!("::pilota::FastStr::from_static_str({stream})").into(),
470 true,
471 )
472 }
473 (
474 CodegenTy::Adt(AdtDef {
475 did: _,
476 kind: AdtKind::Enum,
477 }),
478 CodegenTy::I64,
479 )
480 | (
481 CodegenTy::Adt(AdtDef {
482 did: _,
483 kind: AdtKind::Enum,
484 }),
485 CodegenTy::I32,
486 )
487 | (
488 CodegenTy::Adt(AdtDef {
489 did: _,
490 kind: AdtKind::Enum,
491 }),
492 CodegenTy::I16,
493 )
494 | (
495 CodegenTy::Adt(AdtDef {
496 did: _,
497 kind: AdtKind::Enum,
498 }),
499 CodegenTy::I8,
500 ) => {
501 let stream = self.cur_related_item_path(did);
502 let target = match target {
503 CodegenTy::I64 => "i64",
504 CodegenTy::I32 => "i32",
505 CodegenTy::I16 => "i16",
506 CodegenTy::I8 => "i8",
507 _ => unreachable!(),
508 };
509 (format!("({stream} as {target})").into(), true)
510 }
511 _ => panic!("invalid convert {:?} to {:?}", ident_ty, target),
512 }
513 }
514
515 fn lit_into_ty(
516 &self,
517 lit: &Literal,
518 ty: &CodegenTy,
519 ) -> anyhow::Result<(FastStr, bool )> {
520 Ok(match (lit, ty) {
521 (Literal::Path(p), ty) => {
522 let ident_ty = self.codegen_ty(p.did);
523
524 self.ident_into_ty(p.did, &ident_ty, ty)
525 }
526 (Literal::String(s), CodegenTy::Str) => (format!("\"{s}\"").into(), true),
527 (Literal::String(s), CodegenTy::String) => {
528 (format! {"\"{s}\".to_string()"}.into(), false)
529 }
530 (Literal::Int(i), CodegenTy::I16) => (format! {"{i}i16"}.into(), true),
531 (Literal::Int(i), CodegenTy::I32) => (format! {"{i}i32"}.into(), true),
532 (Literal::Int(i), CodegenTy::I64) => (format! {"{i}i64"}.into(), true),
533 (Literal::Int(i), CodegenTy::F32) => {
534 let f = (*i) as f32;
535 (format!("{f}f32").into(), true)
536 }
537 (Literal::Int(i), CodegenTy::F64) => {
538 let f = (*i) as f64;
539 (format!("{f}f64").into(), true)
540 }
541 (
542 Literal::Int(i),
543 CodegenTy::Adt(AdtDef {
544 did,
545 kind: AdtKind::Enum,
546 }),
547 ) => {
548 let item = self.item(*did).unwrap();
549 let e = match &*item {
550 Item::Enum(e) => e,
551 _ => panic!("invalid enum"),
552 };
553
554 (
555 e.variants.iter().find(|v| v.discr == Some(*i)).map_or_else(
556 || panic!("invalid enum value"),
557 |v| self.cur_related_item_path(v.did),
558 ),
559 true,
560 )
561 }
562 (Literal::Float(f), CodegenTy::F64) => {
563 let f = f.parse::<f64>().unwrap();
564 (format! {"{f}f64"}.into(), true)
565 }
566 (
567 l,
568 CodegenTy::Adt(AdtDef {
569 kind: AdtKind::NewType(inner_ty),
570 did,
571 }),
572 ) => {
573 let ident = self.cur_related_item_path(*did);
574 let (stream, is_const) = self.lit_into_ty(l, inner_ty)?;
575 (format! {"{ident}({stream})"}.into(), is_const)
576 }
577 (Literal::Map(_), CodegenTy::StaticRef(map)) => match &**map {
578 CodegenTy::Map(_, _) => {
579 let lazy_map =
580 self.def_lit("INNER_MAP", lit, &mut CodegenTy::LazyStaticRef(map.clone()))?;
581 let stream = format! {
582 r#"{{
583 {lazy_map}
584 &*INNER_MAP
585 }}"#
586 }
587 .into();
588 (stream, false)
589 }
590 _ => panic!("invalid map type {:?}", map),
591 },
592 (Literal::List(els), CodegenTy::Array(inner, _)) => {
593 let stream = els
594 .iter()
595 .map(|el| self.lit_into_ty(el, inner))
596 .try_collect::<_, Vec<_>, _>()?;
597 let is_const = stream.iter().all(|(_, is_const)| *is_const);
598 let stream = stream.into_iter().map(|(s, _)| s).join(",");
599
600 (format! {"[{stream}]"}.into(), is_const)
601 }
602 (Literal::List(els), CodegenTy::Vec(inner)) => {
603 let stream = els
604 .iter()
605 .map(|el| self.lit_into_ty(el, inner))
606 .try_collect::<_, Vec<_>, _>()?
607 .into_iter()
608 .map(|(s, _)| s)
609 .join(",");
610
611 (format! {"::std::vec![{stream}]"}.into(), false)
612 }
613 (Literal::Bool(b), CodegenTy::Bool) => (format! {"{b}"}.into(), true),
614 (Literal::String(s), CodegenTy::Bytes) => {
615 let s = &**s;
616 (
617 format! {"::bytes::Bytes::from_static({s}.as_bytes())"}.into(),
618 true,
619 )
620 }
621 (
622 Literal::Map(m),
623 CodegenTy::Adt(AdtDef {
624 did,
625 kind: AdtKind::Struct,
626 }),
627 ) => {
628 let def = self.item(*did).unwrap();
629 let def = match &*def {
630 Item::Message(m) => m,
631 _ => panic!(),
632 };
633
634 let fields: Vec<_> = def
635 .fields
636 .iter()
637 .map(|f| {
638 let v = m.iter().find_map(|(k, v)| {
639 let k = match k {
640 Literal::String(s) => s,
641 _ => panic!(),
642 };
643 if **k == **f.name {
644 Some(v)
645 } else {
646 None
647 }
648 });
649
650 let name = self.rust_name(f.did);
651
652 if let Some(v) = v {
653 let (mut v, is_const) =
654 self.lit_into_ty(v, &self.codegen_item_ty(f.ty.kind.clone()))?;
655
656 if f.is_optional() {
657 v = format!("Some({v})").into()
658 }
659 anyhow::Ok((format!("{name}: {v}"), is_const))
660 } else {
661 anyhow::Ok((format!("{name}: Default::default()"), false))
662 }
663 })
664 .try_collect()?;
665 let is_const = fields.iter().all(|(_, is_const)| *is_const);
666 let fields = fields.into_iter().map(|f| f.0).join(",");
667
668 let name = self.rust_name(*did);
669
670 (
671 format! {
672 r#"{name} {{
673 {fields}
674 }}"#
675 }
676 .into(),
677 is_const,
678 )
679 }
680 _ => panic!("unexpected literal {:?} with ty {:?}", lit, ty),
681 })
682 }
683
684 pub(crate) fn def_lit(
685 &self,
686 name: &str,
687 lit: &Literal,
688 ty: &mut CodegenTy,
689 ) -> anyhow::Result<String> {
690 let should_lazy_static = ty.should_lazy_static();
691 let name = format_ident!("{}", name.to_shouty_snake_case());
692 if let (Literal::List(lit), CodegenTy::Array(_, size)) = (lit, &mut *ty) {
693 *size = lit.len()
694 }
695 Ok(if should_lazy_static {
696 let lit = self.lit_as_rvalue(lit, ty)?.0;
697 format! {r#"
698 ::pilota::lazy_static::lazy_static! {{
699 pub static ref {name}: {ty} = {lit};
700 }}
701 "#}
702 } else {
703 let lit = self.lit_into_ty(lit, ty)?.0;
704 format!(r#"pub const {name}: {ty} = {lit};"#)
705 })
706 }
707
708 pub fn rust_name(&self, def_id: DefId) -> Symbol {
709 let node = self.node(def_id).unwrap();
710
711 if let Some(name) = self
712 .tags(node.tags)
713 .and_then(|tags| tags.get::<crate::tags::PilotaName>().cloned())
714 {
715 return name.0.into();
716 }
717
718 if !self.change_case {
719 return self.node(def_id).unwrap().name().0.into();
720 }
721
722 match self.node(def_id).unwrap().kind {
723 NodeKind::Item(item) => match &*item {
724 crate::rir::Item::Message(m) => (&**m.name).struct_ident(),
725 crate::rir::Item::Enum(e) => (&**e.name).enum_ident(),
726 crate::rir::Item::Service(s) => (&**s.name).trait_ident(),
727 crate::rir::Item::NewType(t) => (&**t.name).newtype_ident(),
728 crate::rir::Item::Const(c) => (&**c.name).const_ident(),
729 crate::rir::Item::Mod(m) => (&**m.name).mod_ident(),
730 },
731 NodeKind::Variant(v) => {
732 let parent = self.node(def_id).unwrap().parent.unwrap();
733
734 if self
735 .node_tags(parent)
736 .unwrap()
737 .get::<EnumMode>()
738 .copied()
739 .unwrap_or(EnumMode::Enum)
740 == EnumMode::NewType
741 {
742 (&**v.name).shouty_snake_case()
743 } else {
744 (&**v.name).variant_ident()
745 }
746 }
747 NodeKind::Field(f) => (&**f.name).field_ident(),
748 NodeKind::Method(m) => (&**m.name).fn_ident(),
749 NodeKind::Arg(a) => (&**a.name).field_ident(),
750 }
751 .into()
752 }
753
754 pub fn mod_path(&self, def_id: DefId) -> Arc<[Symbol]> {
755 self.path_resolver.mod_prefix(self, def_id)
756 }
757
758 pub fn item_path(&self, def_id: DefId) -> Arc<[Symbol]> {
759 self.path_resolver.path_for_def_id(self, def_id)
760 }
761
762 fn related_path(&self, p1: &[Symbol], p2: &[Symbol]) -> FastStr {
763 self.path_resolver.related_path(p1, p2)
764 }
765
766 pub fn cur_related_item_path(&self, did: DefId) -> FastStr {
767 let a = with_cur_item(|def_id| def_id);
768 self.related_item_path(a, did)
769 }
770
771 pub fn related_item_path(&self, a: DefId, b: DefId) -> FastStr {
772 let cur_item_path = self.item_path(a);
773 let mut mod_segs = vec![];
774
775 cur_item_path[..cur_item_path.len() - 1]
776 .iter()
777 .for_each(|p| {
778 mod_segs.push(p.clone());
779 });
780
781 let other_item_path = self.item_path(b);
782 self.related_path(&mod_segs, &other_item_path)
783 }
784
785 #[allow(clippy::single_match)]
786 pub fn exec_plugin<P: Plugin>(&self, mut p: P) {
787 for def_id in self.codegen_items.clone().iter() {
788 let node = self.node(*def_id).unwrap();
789 CUR_ITEM.set(def_id, || match &node.kind {
790 NodeKind::Item(item) => p.on_item(self, *def_id, item.clone()),
791 _ => {}
792 })
793 }
794
795 p.on_emit(self)
796 }
797
798 pub(crate) fn workspace_info(&self) -> &WorkspaceInfo {
799 let Mode::Workspace(info) = &*self.mode else {
800 panic!("can not access workspace info in mode `{:?}`", self.mode)
801 };
802 info
803 }
804
805 pub fn def_id_info(&self, def_id: DefId) -> FastStr {
806 let file_path = self
807 .file(self.node(def_id).unwrap().file_id)
808 .unwrap()
809 .package
810 .clone();
811 file_path
812 .iter()
813 .chain(&[self.node(def_id).unwrap().name()])
814 .join("::")
815 .into()
816 }
817
818 pub(crate) fn crate_name(&self, location: &DefLocation) -> FastStr {
819 match location {
820 DefLocation::Fixed(path) => path.iter().join("_").into(),
821 DefLocation::Dynamic => "common".into(),
822 }
823 }
824}
825
826pub mod tls {
827 use scoped_tls::scoped_thread_local;
828
829 use crate::DefId;
830
831 use super::Context;
832
833 scoped_thread_local!(pub static CONTEXT: Context);
834 scoped_thread_local!(pub static CUR_ITEM: DefId);
835
836 pub fn with_cx<T, F>(f: F) -> T
837 where
838 F: FnOnce(&Context) -> T,
839 {
840 CONTEXT.with(|cx| f(cx))
841 }
842
843 pub fn with_cur_item<T, F>(f: F) -> T
844 where
845 F: FnOnce(DefId) -> T,
846 {
847 CUR_ITEM.with(|def_id| f(*def_id))
848 }
849}