pilota_build2/middle/
context.rs

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 /* const? */)> {
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 /* const? */)> {
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 /* const? */) {
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 /* const? */)> {
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}