bronzite_query/
lib.rs

1#![feature(rustc_private)]
2
3extern crate rustc_abi;
4extern crate rustc_ast;
5extern crate rustc_driver;
6extern crate rustc_hir;
7extern crate rustc_infer;
8extern crate rustc_interface;
9extern crate rustc_middle;
10extern crate rustc_session;
11extern crate rustc_span;
12extern crate rustc_target;
13extern crate rustc_trait_selection;
14
15use rustc_infer::infer::TyCtxtInferExt;
16use rustc_trait_selection::infer::InferCtxtExt;
17
18use std::collections::HashMap;
19
20use bronzite_types::{
21    AssocConstInfo, AssocTypeInfo, CrateTypeInfo, Delimiter, EnumVariantInfo, FieldInfo,
22    FunctionSignature, GenericParam, GenericParamKind, InherentImplDetails, ItemInfo, ItemKind,
23    LayoutInfo, LiteralKind, MatchArm, MethodDetails, MethodSummary, ModuleInfo, ParamInfo, Query,
24    QueryData, QueryResult, ReceiverInfo, ReexportInfo, SpanInfo, Token, TraitDetails,
25    TraitImplDetails, TraitInfo, TraitMethodInfo, TypeAliasInfo, TypeDetails, TypeKind,
26    TypeSummary, Visibility,
27};
28use clap::Parser;
29use rustc_ast::ast;
30use rustc_hir as hir;
31use rustc_hir::def::DefKind;
32use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
33use rustc_middle::ty::{self, TyCtxt, TypingEnv};
34use rustc_span::symbol::sym;
35use serde::{Deserialize, Serialize};
36
37// Re-export key types for external users
38pub use bronzite_types::CrateTypeInfo as CrateTypeInfoExport;
39
40/// CLI arguments for bronzite-query
41#[derive(Parser, Debug, Clone, Serialize, Deserialize)]
42pub struct Args {
43    /// Query to execute (for single-query mode)
44    #[arg(short, long)]
45    pub query: Option<String>,
46
47    /// Extract all type information to a JSON file (for daemon mode)
48    #[arg(long)]
49    pub extract: bool,
50
51    /// Output file for extraction (defaults to stdout)
52    #[arg(long)]
53    pub output: Option<String>,
54}
55
56/// The Bronzite query plugin
57pub struct BronziteQueryPlugin;
58
59impl rustc_plugin::RustcPlugin for BronziteQueryPlugin {
60    type Args = Args;
61
62    fn version(&self) -> std::borrow::Cow<'static, str> {
63        "0.1.0".into()
64    }
65
66    fn driver_name(&self) -> std::borrow::Cow<'static, str> {
67        "bronzite-query-driver".into()
68    }
69
70    fn args(
71        &self,
72        _target_dir: &rustc_plugin::Utf8Path,
73    ) -> rustc_plugin::RustcPluginArgs<Self::Args> {
74        let args = Args::parse_from(std::env::args().skip(1));
75        rustc_plugin::RustcPluginArgs {
76            args,
77            filter: rustc_plugin::CrateFilter::OnlyWorkspace,
78        }
79    }
80
81    fn run(
82        self,
83        compiler_args: Vec<String>,
84        plugin_args: Self::Args,
85    ) -> rustc_interface::interface::Result<()> {
86        let mut callbacks = BronziteQueryCallbacks { args: plugin_args };
87        rustc_driver::run_compiler(&compiler_args, &mut callbacks);
88        Ok(())
89    }
90}
91
92pub struct BronziteQueryCallbacks {
93    args: Args,
94}
95
96impl rustc_driver::Callbacks for BronziteQueryCallbacks {
97    fn after_analysis(
98        &mut self,
99        _compiler: &rustc_interface::interface::Compiler,
100        tcx: TyCtxt<'_>,
101    ) -> rustc_driver::Compilation {
102        if self.args.extract {
103            let info = extract_crate_info(tcx);
104            output_extracted_info(&info, &self.args.output);
105        } else if let Some(ref query_str) = self.args.query {
106            let query = parse_query(query_str);
107            let result = execute_query(tcx, &query);
108            output_query_result(&result);
109        }
110
111        rustc_driver::Compilation::Stop
112    }
113}
114
115// ============================================================================
116// Source Code Extraction Helpers
117// ============================================================================
118
119/// Get the source code for a span
120fn get_source_for_span(tcx: TyCtxt<'_>, span: rustc_span::Span) -> Option<String> {
121    let source_map = tcx.sess.source_map();
122    source_map.span_to_snippet(span).ok()
123}
124
125/// Get the source code for a definition
126fn get_source_for_def(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
127    if !def_id.is_local() {
128        return None;
129    }
130    let span = tcx.source_span(def_id.expect_local());
131    get_source_for_span(tcx, span)
132}
133
134// ============================================================================
135// Doc Comments Extraction
136// ============================================================================
137
138/// Extract doc comments from attributes
139fn extract_docs(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
140    if !def_id.is_local() {
141        return None;
142    }
143
144    // Use hir_attrs to get attributes
145    let local_def_id = def_id.expect_local();
146    let hir_id = tcx.local_def_id_to_hir_id(local_def_id);
147    let attrs = tcx.hir_attrs(hir_id);
148    let mut docs = Vec::new();
149
150    for attr in attrs {
151        if attr.is_doc_comment() {
152            if let Some(doc) = attr.doc_str() {
153                docs.push(doc.to_string());
154            }
155        } else if attr.has_name(sym::doc) {
156            if let Some(value) = attr.value_str() {
157                docs.push(value.to_string());
158            }
159        }
160    }
161
162    if docs.is_empty() {
163        None
164    } else {
165        Some(docs.join("\n"))
166    }
167}
168
169// ============================================================================
170// Attributes Extraction
171// ============================================================================
172
173/// Extract non-doc attributes as strings
174fn extract_attributes(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<String> {
175    if !def_id.is_local() {
176        return Vec::new();
177    }
178
179    let local_def_id = def_id.expect_local();
180    let hir_id = tcx.local_def_id_to_hir_id(local_def_id);
181    let attrs = tcx.hir_attrs(hir_id);
182
183    attrs
184        .iter()
185        .filter(|attr| !attr.is_doc_comment() && !attr.has_name(sym::doc))
186        .filter_map(|attr| {
187            // Reconstruct attribute from path symbols to avoid panics on parsed attributes
188            // (attr.span() can panic for inline/parsed attributes in newer rustc versions)
189            let path_symbols = attr.path();
190            if path_symbols.is_empty() {
191                None
192            } else {
193                let path_str: Vec<_> = path_symbols.iter().map(|s| s.to_string()).collect();
194                Some(format!("#[{}]", path_str.join("::")))
195            }
196        })
197        .collect()
198}
199
200// ============================================================================
201// Token/AST Extraction
202// ============================================================================
203
204/// Extract body tokens from a function body
205fn extract_body_tokens(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Vec<Token>> {
206    if !def_id.is_local() {
207        return None;
208    }
209
210    let local_def_id = def_id.expect_local();
211
212    // Get the HIR body - hir_maybe_body_owned_by returns Option<&Body> directly
213    let body = tcx.hir_maybe_body_owned_by(local_def_id)?;
214    let expr = &body.value;
215
216    Some(vec![extract_expr_tokens(tcx, expr)])
217}
218
219/// Convert a HIR expression to tokens
220fn extract_expr_tokens(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) -> Token {
221    let source_map = tcx.sess.source_map();
222
223    match &expr.kind {
224        hir::ExprKind::Lit(lit) => {
225            let kind = match lit.node {
226                ast::LitKind::Str(_, _) => LiteralKind::String,
227                ast::LitKind::ByteStr(_, _) => LiteralKind::ByteString,
228                ast::LitKind::CStr(_, _) => LiteralKind::String,
229                ast::LitKind::Byte(_) => LiteralKind::Byte,
230                ast::LitKind::Char(_) => LiteralKind::Char,
231                ast::LitKind::Int(_, _) => LiteralKind::Int,
232                ast::LitKind::Float(_, _) => LiteralKind::Float,
233                ast::LitKind::Bool(_) => LiteralKind::Bool,
234                ast::LitKind::Err(_) => LiteralKind::String,
235            };
236            let value = source_map.span_to_snippet(expr.span).unwrap_or_default();
237            Token::Literal { kind, value }
238        }
239
240        hir::ExprKind::Path(qpath) => {
241            let segments: Vec<String> = match qpath {
242                hir::QPath::Resolved(_, path) => {
243                    path.segments.iter().map(|s| s.ident.to_string()).collect()
244                }
245                hir::QPath::TypeRelative(_, segment) => vec![segment.ident.to_string()],
246                hir::QPath::LangItem(item, _) => vec![format!("{:?}", item)],
247            };
248            if segments.len() == 1 {
249                Token::Ident {
250                    name: segments[0].clone(),
251                }
252            } else {
253                Token::Path { segments }
254            }
255        }
256
257        hir::ExprKind::Call(func, args) => {
258            let func_token = extract_expr_tokens(tcx, func);
259            let path = match &func_token {
260                Token::Path { segments } => segments.clone(),
261                Token::Ident { name } => vec![name.clone()],
262                _ => vec!["<expr>".to_string()],
263            };
264            let arg_tokens: Vec<Token> = args.iter().map(|a| extract_expr_tokens(tcx, a)).collect();
265            Token::FnCall {
266                path,
267                args: arg_tokens,
268            }
269        }
270
271        hir::ExprKind::MethodCall(segment, receiver, args, _) => {
272            let receiver_token = Box::new(extract_expr_tokens(tcx, receiver));
273            let arg_tokens: Vec<Token> = args.iter().map(|a| extract_expr_tokens(tcx, a)).collect();
274            Token::MethodCall {
275                receiver: receiver_token,
276                method: segment.ident.to_string(),
277                args: arg_tokens,
278            }
279        }
280
281        hir::ExprKind::Field(base, field) => Token::FieldAccess {
282            base: Box::new(extract_expr_tokens(tcx, base)),
283            field: field.to_string(),
284        },
285
286        hir::ExprKind::Binary(op, lhs, rhs) => Token::BinOp {
287            lhs: Box::new(extract_expr_tokens(tcx, lhs)),
288            op: format!("{:?}", op.node),
289            rhs: Box::new(extract_expr_tokens(tcx, rhs)),
290        },
291
292        hir::ExprKind::Unary(op, expr) => Token::UnaryOp {
293            op: format!("{:?}", op),
294            expr: Box::new(extract_expr_tokens(tcx, expr)),
295        },
296
297        hir::ExprKind::If(cond, then_branch, else_branch) => Token::If {
298            cond: Box::new(extract_expr_tokens(tcx, cond)),
299            then_branch: vec![extract_expr_tokens(tcx, then_branch)],
300            else_branch: else_branch.map(|e| vec![extract_expr_tokens(tcx, e)]),
301        },
302
303        hir::ExprKind::Match(expr, arms, _) => {
304            let match_arms: Vec<MatchArm> = arms
305                .iter()
306                .map(|arm| {
307                    let pattern = source_map
308                        .span_to_snippet(arm.pat.span)
309                        .unwrap_or_else(|_| "<pattern>".to_string());
310                    let guard = arm.guard.map(|g| {
311                        source_map
312                            .span_to_snippet(g.span)
313                            .unwrap_or_else(|_| "<guard>".to_string())
314                    });
315                    let body = vec![extract_expr_tokens(tcx, arm.body)];
316                    MatchArm {
317                        pattern,
318                        guard,
319                        body,
320                    }
321                })
322                .collect();
323            Token::Match {
324                expr: Box::new(extract_expr_tokens(tcx, expr)),
325                arms: match_arms,
326            }
327        }
328
329        hir::ExprKind::Block(block, _) => {
330            let stmts: Vec<Token> = block
331                .stmts
332                .iter()
333                .map(|stmt| extract_stmt_tokens(tcx, stmt))
334                .collect();
335            let mut all_tokens = stmts;
336            if let Some(expr) = block.expr {
337                all_tokens.push(extract_expr_tokens(tcx, expr));
338            }
339            Token::Block { stmts: all_tokens }
340        }
341
342        hir::ExprKind::Ret(expr) => Token::Return {
343            expr: expr.map(|e| Box::new(extract_expr_tokens(tcx, e))),
344        },
345
346        hir::ExprKind::Closure(closure) => {
347            let body_id = closure.body;
348            let body = tcx.hir_body(body_id);
349            let params: Vec<String> = body
350                .params
351                .iter()
352                .map(|p| source_map.span_to_snippet(p.pat.span).unwrap_or_default())
353                .collect();
354            Token::Closure {
355                params,
356                body: Box::new(extract_expr_tokens(tcx, &body.value)),
357            }
358        }
359
360        hir::ExprKind::Tup(exprs) => Token::Group {
361            delimiter: Delimiter::Paren,
362            tokens: exprs.iter().map(|e| extract_expr_tokens(tcx, e)).collect(),
363        },
364
365        hir::ExprKind::Array(exprs) => Token::Group {
366            delimiter: Delimiter::Bracket,
367            tokens: exprs.iter().map(|e| extract_expr_tokens(tcx, e)).collect(),
368        },
369
370        hir::ExprKind::Struct(_, fields, base) => {
371            let mut tokens: Vec<Token> = fields
372                .iter()
373                .map(|f| {
374                    let field_name = f.ident.to_string();
375                    let field_value = extract_expr_tokens(tcx, f.expr);
376                    Token::BinOp {
377                        lhs: Box::new(Token::Ident { name: field_name }),
378                        op: ":".to_string(),
379                        rhs: Box::new(field_value),
380                    }
381                })
382                .collect();
383            if let hir::StructTailExpr::Base(base_expr) = base {
384                tokens.push(Token::UnaryOp {
385                    op: "..".to_string(),
386                    expr: Box::new(extract_expr_tokens(tcx, base_expr)),
387                });
388            }
389            Token::Group {
390                delimiter: Delimiter::Brace,
391                tokens,
392            }
393        }
394
395        hir::ExprKind::Loop(block, label, _, _) => {
396            let label_str = label.map(|l| l.ident.to_string());
397            let body = extract_expr_tokens(
398                tcx,
399                &hir::Expr {
400                    hir_id: block.hir_id,
401                    kind: hir::ExprKind::Block(block, None),
402                    span: block.span,
403                },
404            );
405            Token::Block {
406                stmts: vec![
407                    Token::Keyword {
408                        name: if let Some(l) = label_str {
409                            format!("'{}: loop", l)
410                        } else {
411                            "loop".to_string()
412                        },
413                    },
414                    body,
415                ],
416            }
417        }
418
419        // For complex expressions, fall back to raw source
420        _ => {
421            let source = source_map
422                .span_to_snippet(expr.span)
423                .unwrap_or_else(|_| "<expr>".to_string());
424            Token::Raw { source }
425        }
426    }
427}
428
429/// Convert a HIR statement to tokens
430fn extract_stmt_tokens(tcx: TyCtxt<'_>, stmt: &hir::Stmt<'_>) -> Token {
431    let source_map = tcx.sess.source_map();
432
433    match &stmt.kind {
434        hir::StmtKind::Let(local) => {
435            let pattern = source_map
436                .span_to_snippet(local.pat.span)
437                .unwrap_or_else(|_| "<pattern>".to_string());
438            let ty = local.ty.map(|t| {
439                source_map
440                    .span_to_snippet(t.span)
441                    .unwrap_or_else(|_| "<type>".to_string())
442            });
443            let init = local.init.map(|e| Box::new(extract_expr_tokens(tcx, e)));
444            Token::Let { pattern, ty, init }
445        }
446        hir::StmtKind::Item(_) => Token::Raw {
447            source: source_map
448                .span_to_snippet(stmt.span)
449                .unwrap_or_else(|_| "<item>".to_string()),
450        },
451        hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => extract_expr_tokens(tcx, expr),
452    }
453}
454
455// ============================================================================
456// Type Resolution
457// ============================================================================
458
459/// Resolve a type alias chain to its final type
460fn resolve_type_alias_chain(tcx: TyCtxt<'_>, def_id: DefId) -> (String, Vec<String>) {
461    let mut chain = Vec::new();
462    let mut current_def_id = def_id;
463    let mut seen = std::collections::HashSet::new();
464
465    loop {
466        if seen.contains(&current_def_id) {
467            break; // Avoid infinite loops
468        }
469        seen.insert(current_def_id);
470
471        let ty = tcx.type_of(current_def_id).skip_binder();
472        let ty_str = format!("{:?}", ty);
473        chain.push(ty_str.clone());
474
475        // Check if this type is itself an alias
476        if let ty::TyKind::Alias(ty::Projection | ty::Opaque, alias_ty) = ty.kind() {
477            current_def_id = alias_ty.def_id;
478        } else if let Some(adt) = ty.ty_adt_def() {
479            // If it's an ADT, we've reached the end
480            chain.push(tcx.def_path_str(adt.did()));
481            break;
482        } else {
483            break;
484        }
485    }
486
487    let resolved = chain.last().cloned().unwrap_or_default();
488    (resolved, chain)
489}
490
491/// Get fully resolved type string
492fn get_resolved_type<'tcx>(tcx: TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> String {
493    // Attempt to normalize the type
494    let typing_env = TypingEnv::fully_monomorphized();
495
496    match tcx.try_normalize_erasing_regions(typing_env, ty) {
497        Ok(normalized) => format!("{:?}", normalized),
498        Err(_) => format!("{:?}", ty),
499    }
500}
501
502// ============================================================================
503// Main Extraction Logic
504// ============================================================================
505
506/// Extract all type information from the crate
507pub fn extract_crate_info(tcx: TyCtxt<'_>) -> CrateTypeInfo {
508    let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
509
510    let mut info = CrateTypeInfo {
511        crate_name,
512        crate_version: None,
513        items: Vec::new(),
514        types: HashMap::new(),
515        traits: HashMap::new(),
516        trait_impls: HashMap::new(),
517        inherent_impls: HashMap::new(),
518        type_aliases: HashMap::new(),
519        layouts: HashMap::new(),
520        modules: HashMap::new(),
521    };
522
523    let crate_items = tcx.hir_crate_items(());
524
525    // First pass: collect all items
526    for item_id in crate_items.free_items() {
527        let def_id = item_id.owner_id.to_def_id();
528        let local_def_id = item_id.owner_id.def_id;
529        let def_kind = tcx.def_kind(def_id);
530        let path = tcx.def_path_str(def_id);
531
532        // Extract basic item info
533        if let Some(item_info) = extract_item_info(tcx, def_id) {
534            info.items.push(item_info);
535        }
536
537        // Extract detailed information based on kind
538        match def_kind {
539            DefKind::Struct | DefKind::Enum | DefKind::Union => {
540                if let Some(type_details) = extract_type_details(tcx, local_def_id) {
541                    if let Some(layout) = extract_layout_info(tcx, local_def_id) {
542                        info.layouts.insert(path.clone(), layout);
543                    }
544                    info.types.insert(path.clone(), type_details);
545                }
546            }
547            DefKind::Trait => {
548                if let Some(trait_details) = extract_trait_details(tcx, def_id) {
549                    info.traits.insert(path.clone(), trait_details);
550                }
551            }
552            DefKind::TyAlias => {
553                if let Some(alias_info) = extract_type_alias(tcx, def_id) {
554                    info.type_aliases.insert(path.clone(), alias_info);
555                }
556            }
557            DefKind::Mod => {
558                if let Some(module_info) = extract_module_info(tcx, local_def_id) {
559                    info.modules.insert(path.clone(), module_info);
560                }
561            }
562            DefKind::Impl { .. } => {
563                if let Some(trait_ref) = tcx.impl_trait_ref(def_id) {
564                    // This is a trait impl
565                    if let Some(impl_details) = extract_trait_impl_details(tcx, def_id) {
566                        let self_ty = trait_ref.skip_binder().self_ty();
567                        let self_ty_str = get_type_path_string(tcx, self_ty);
568                        info.trait_impls
569                            .entry(self_ty_str)
570                            .or_default()
571                            .push(impl_details);
572                    }
573                } else {
574                    // This is an inherent impl
575                    if let Some(impl_details) = extract_inherent_impl_details(tcx, def_id) {
576                        let self_ty = tcx.type_of(def_id).skip_binder();
577                        let self_ty_str = get_type_path_string(tcx, self_ty);
578                        info.inherent_impls
579                            .entry(self_ty_str)
580                            .or_default()
581                            .push(impl_details);
582                    }
583                }
584            }
585            _ => {}
586        }
587    }
588
589    info
590}
591
592/// Get a clean path string for a type
593fn get_type_path_string(tcx: TyCtxt<'_>, ty: ty::Ty<'_>) -> String {
594    if let Some(adt) = ty.ty_adt_def() {
595        tcx.def_path_str(adt.did())
596    } else {
597        format!("{:?}", ty)
598    }
599}
600
601fn extract_item_info(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ItemInfo> {
602    let def_kind = tcx.def_kind(def_id);
603    let path = tcx.def_path_str(def_id);
604
605    let name = match def_kind {
606        DefKind::Use | DefKind::Impl { .. } | DefKind::GlobalAsm | DefKind::OpaqueTy => {
607            path.split("::").last().unwrap_or("").to_string()
608        }
609        _ => match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
610            tcx.item_name(def_id).to_string()
611        })) {
612            Ok(name) => name,
613            Err(_) => path.split("::").last().unwrap_or("").to_string(),
614        },
615    };
616
617    let kind = match def_kind {
618        DefKind::Struct => ItemKind::Struct,
619        DefKind::Enum => ItemKind::Enum,
620        DefKind::Union => ItemKind::Union,
621        DefKind::Trait => ItemKind::Trait,
622        DefKind::Fn => ItemKind::Function,
623        DefKind::Const => ItemKind::Const,
624        DefKind::Static { .. } => ItemKind::Static,
625        DefKind::TyAlias => ItemKind::TypeAlias,
626        DefKind::Impl { .. } => ItemKind::Impl,
627        DefKind::Mod => ItemKind::Mod,
628        DefKind::Use => ItemKind::Use,
629        DefKind::ExternCrate => ItemKind::ExternCrate,
630        DefKind::Macro(_) => ItemKind::Macro,
631        DefKind::TraitAlias => ItemKind::TraitAlias,
632        _ => ItemKind::Other(format!("{:?}", def_kind)),
633    };
634
635    let visibility = extract_visibility(tcx, def_id);
636    let span = extract_span_info(tcx, def_id);
637
638    Some(ItemInfo {
639        name,
640        path,
641        kind,
642        visibility,
643        span,
644    })
645}
646
647fn extract_visibility(tcx: TyCtxt<'_>, def_id: DefId) -> Visibility {
648    let vis = tcx.visibility(def_id);
649    if vis.is_public() {
650        Visibility::Public
651    } else {
652        Visibility::Private
653    }
654}
655
656fn extract_span_info(tcx: TyCtxt<'_>, def_id: DefId) -> Option<SpanInfo> {
657    if !def_id.is_local() {
658        return None;
659    }
660
661    let span = tcx.def_span(def_id);
662    let source_map = tcx.sess.source_map();
663
664    let lo = source_map.lookup_char_pos(span.lo());
665    let hi = source_map.lookup_char_pos(span.hi());
666
667    Some(SpanInfo {
668        file: lo.file.name.prefer_remapped_unconditionally().to_string(),
669        start_line: lo.line as u32,
670        start_col: lo.col.0 as u32,
671        end_line: hi.line as u32,
672        end_col: hi.col.0 as u32,
673    })
674}
675
676fn extract_type_details(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> Option<TypeDetails> {
677    let def_id = local_def_id.to_def_id();
678    let def_kind = tcx.def_kind(def_id);
679
680    let name = tcx.item_name(def_id).to_string();
681    let path = tcx.def_path_str(def_id);
682    let visibility = extract_visibility(tcx, def_id);
683    let generics = extract_generics(tcx, def_id);
684    let where_clause = extract_where_clause(tcx, def_id);
685    let span = extract_span_info(tcx, def_id);
686    let docs = extract_docs(tcx, def_id);
687    let attributes = extract_attributes(tcx, def_id);
688    let source = get_source_for_def(tcx, def_id);
689
690    let kind = match def_kind {
691        DefKind::Struct => TypeKind::Struct,
692        DefKind::Enum => TypeKind::Enum,
693        DefKind::Union => TypeKind::Union,
694        _ => return None,
695    };
696
697    // Extract fields for structs/unions
698    let fields = if matches!(def_kind, DefKind::Struct | DefKind::Union) {
699        Some(extract_struct_fields(tcx, local_def_id))
700    } else {
701        None
702    };
703
704    // Extract variants for enums
705    let variants = if def_kind == DefKind::Enum {
706        Some(extract_enum_variants(tcx, local_def_id))
707    } else {
708        None
709    };
710
711    // Get trait impls for this type
712    let trait_impls = get_trait_impl_paths(tcx, def_id);
713
714    // Get inherent methods
715    let inherent_methods = extract_inherent_method_summaries(tcx, def_id);
716
717    // Try to get layout
718    let layout = extract_layout_info(tcx, local_def_id);
719
720    Some(TypeDetails {
721        name,
722        path,
723        kind,
724        visibility,
725        generics,
726        where_clause,
727        docs,
728        attributes,
729        fields,
730        variants,
731        trait_impls,
732        inherent_methods,
733        layout,
734        source,
735        span,
736    })
737}
738
739fn extract_where_clause(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
740    if !def_id.is_local() {
741        return None;
742    }
743
744    let predicates = tcx.predicates_of(def_id);
745    if predicates.predicates.is_empty() {
746        return None;
747    }
748
749    let preds: Vec<String> = predicates
750        .predicates
751        .iter()
752        .map(|(pred, _)| format!("{:?}", pred))
753        .collect();
754
755    if preds.is_empty() {
756        None
757    } else {
758        Some(format!("where {}", preds.join(", ")))
759    }
760}
761
762fn extract_generics(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<GenericParam> {
763    let generics = tcx.generics_of(def_id);
764
765    generics
766        .own_params
767        .iter()
768        .filter_map(|param| {
769            let name = param.name.to_string();
770            if name == "Self" {
771                return None;
772            }
773
774            let kind = match param.kind {
775                ty::GenericParamDefKind::Lifetime => GenericParamKind::Lifetime,
776                ty::GenericParamDefKind::Type { .. } => GenericParamKind::Type,
777                ty::GenericParamDefKind::Const { .. } => {
778                    GenericParamKind::Const { ty: String::new() }
779                }
780            };
781
782            // Get bounds from predicates
783            let bounds = extract_param_bounds(tcx, def_id, param.index);
784
785            Some(GenericParam {
786                name,
787                kind,
788                bounds,
789                default: None,
790            })
791        })
792        .collect()
793}
794
795fn extract_param_bounds(tcx: TyCtxt<'_>, def_id: DefId, param_index: u32) -> Vec<String> {
796    let predicates = tcx.predicates_of(def_id);
797    let mut bounds = Vec::new();
798
799    for (pred, _) in predicates.predicates {
800        if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() {
801            if let ty::TyKind::Param(param_ty) = trait_pred.self_ty().kind() {
802                if param_ty.index == param_index {
803                    bounds.push(tcx.def_path_str(trait_pred.def_id()));
804                }
805            }
806        }
807    }
808
809    bounds
810}
811
812fn extract_struct_fields(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> Vec<FieldInfo> {
813    let def_id = local_def_id.to_def_id();
814    let adt_def = tcx.adt_def(def_id);
815    let variant = adt_def.non_enum_variant();
816
817    variant
818        .fields
819        .iter()
820        .enumerate()
821        .map(|(index, field)| {
822            let ty = tcx.type_of(field.did).skip_binder();
823            let visibility = extract_visibility(tcx, field.did);
824            let docs = extract_docs(tcx, field.did);
825            let attributes = extract_attributes(tcx, field.did);
826
827            FieldInfo {
828                name: Some(field.name.to_string()),
829                index,
830                ty: format!("{:?}", ty),
831                resolved_ty: Some(get_resolved_type(tcx, ty)),
832                visibility,
833                docs,
834                attributes,
835                offset: None, // Filled in by layout
836                size: None,
837                span: extract_span_info(tcx, field.did),
838            }
839        })
840        .collect()
841}
842
843fn extract_enum_variants(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> Vec<EnumVariantInfo> {
844    let def_id = local_def_id.to_def_id();
845    let adt_def = tcx.adt_def(def_id);
846
847    adt_def
848        .variants()
849        .iter()
850        .enumerate()
851        .map(|(index, variant)| {
852            let docs = extract_docs(tcx, variant.def_id);
853            let attributes = extract_attributes(tcx, variant.def_id);
854
855            let fields: Vec<FieldInfo> = variant
856                .fields
857                .iter()
858                .enumerate()
859                .map(|(field_index, field)| {
860                    let ty = tcx.type_of(field.did).skip_binder();
861                    let visibility = extract_visibility(tcx, field.did);
862                    let name = field.name.to_string();
863                    let name = if name.parse::<usize>().is_ok() {
864                        None
865                    } else {
866                        Some(name)
867                    };
868
869                    FieldInfo {
870                        name,
871                        index: field_index,
872                        ty: format!("{:?}", ty),
873                        resolved_ty: Some(get_resolved_type(tcx, ty)),
874                        visibility,
875                        docs: extract_docs(tcx, field.did),
876                        attributes: extract_attributes(tcx, field.did),
877                        offset: None,
878                        size: None,
879                        span: extract_span_info(tcx, field.did),
880                    }
881                })
882                .collect();
883
884            // Get discriminant value if explicit
885            let discriminant = match variant.discr {
886                ty::VariantDiscr::Explicit(def_id) => {
887                    // Try to get the evaluated value
888                    Some(format!("{:?}", def_id))
889                }
890                ty::VariantDiscr::Relative(offset) => Some(format!("{}", offset)),
891            };
892
893            EnumVariantInfo {
894                name: variant.name.to_string(),
895                index,
896                fields,
897                discriminant,
898                docs,
899                attributes,
900                span: extract_span_info(tcx, variant.def_id),
901            }
902        })
903        .collect()
904}
905
906fn extract_layout_info(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> Option<LayoutInfo> {
907    let def_id = local_def_id.to_def_id();
908    let ty = tcx.type_of(def_id).skip_binder();
909
910    let typing_env = TypingEnv::fully_monomorphized();
911    let layout = tcx.layout_of(typing_env.as_query_input(ty)).ok()?;
912
913    let size = layout.size.bytes() as usize;
914    let align = layout.align.abi.bytes() as usize;
915
916    let is_copy = tcx.type_is_copy_modulo_regions(typing_env, ty);
917    let is_sized = ty.is_sized(tcx, typing_env);
918
919    // Check for Send and Sync
920    let is_send = check_trait_impl(tcx, ty, "core::marker::Send");
921    let is_sync = check_trait_impl(tcx, ty, "core::marker::Sync");
922
923    Some(LayoutInfo {
924        size,
925        align,
926        field_offsets: None,
927        variants: None,
928        is_sized,
929        is_copy,
930        is_send,
931        is_sync,
932    })
933}
934
935fn check_trait_impl<'tcx>(tcx: TyCtxt<'tcx>, ty: ty::Ty<'tcx>, trait_path: &str) -> bool {
936    // Look for the trait in known lang items
937    let trait_def_id = match trait_path {
938        "core::marker::Send" => tcx.get_diagnostic_item(rustc_span::sym::Send),
939        "core::marker::Sync" => tcx.get_diagnostic_item(rustc_span::sym::Sync),
940        _ => None,
941    };
942
943    if let Some(def_id) = trait_def_id {
944        let infcx = tcx.infer_ctxt().build(ty::TypingMode::non_body_analysis());
945        let param_env = ty::ParamEnv::empty();
946        return infcx
947            .type_implements_trait(def_id, [ty], param_env)
948            .must_apply_modulo_regions();
949    }
950
951    false
952}
953
954fn get_trait_impl_paths(tcx: TyCtxt<'_>, type_def_id: DefId) -> Vec<String> {
955    let mut trait_paths = Vec::new();
956    let crate_items = tcx.hir_crate_items(());
957
958    for item_id in crate_items.free_items() {
959        let impl_def_id = item_id.owner_id.to_def_id();
960        if !matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. }) {
961            continue;
962        }
963
964        if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) {
965            let impl_self_ty = trait_ref.skip_binder().self_ty();
966            let matches = match impl_self_ty.ty_adt_def() {
967                Some(adt) => adt.did() == type_def_id,
968                None => false,
969            };
970
971            if matches {
972                let trait_path = tcx.def_path_str(trait_ref.skip_binder().def_id);
973                trait_paths.push(trait_path);
974            }
975        }
976    }
977
978    trait_paths
979}
980
981fn extract_inherent_method_summaries(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<MethodSummary> {
982    let mut methods = Vec::new();
983
984    for impl_def_id in tcx.inherent_impls(def_id).iter() {
985        for &item_def_id in tcx.associated_item_def_ids(impl_def_id) {
986            let item = tcx.associated_item(item_def_id);
987            if matches!(item.kind, ty::AssocKind::Fn { .. }) {
988                let sig = tcx.fn_sig(item_def_id).skip_binder();
989                methods.push(MethodSummary {
990                    name: item.name().to_string(),
991                    path: tcx.def_path_str(item_def_id),
992                    signature: format!("{:?}", sig),
993                    is_unsafe: sig.safety().is_unsafe(),
994                    is_const: tcx.is_const_fn(item_def_id),
995                    is_async: tcx.asyncness(item_def_id).is_async(),
996                });
997            }
998        }
999    }
1000
1001    methods
1002}
1003
1004fn extract_trait_details(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Option<TraitDetails> {
1005    let name = tcx.item_name(trait_def_id).to_string();
1006    let path = tcx.def_path_str(trait_def_id);
1007    let visibility = extract_visibility(tcx, trait_def_id);
1008    let generics = extract_generics(tcx, trait_def_id);
1009    let where_clause = extract_where_clause(tcx, trait_def_id);
1010    let span = extract_span_info(tcx, trait_def_id);
1011    let docs = extract_docs(tcx, trait_def_id);
1012    let attributes = extract_attributes(tcx, trait_def_id);
1013    let source = get_source_for_def(tcx, trait_def_id);
1014
1015    let trait_def = tcx.trait_def(trait_def_id);
1016    let is_auto = trait_def.has_auto_impl;
1017    let is_unsafe = trait_def.safety.is_unsafe();
1018
1019    // Get supertraits
1020    let supertraits: Vec<String> = tcx
1021        .explicit_super_predicates_of(trait_def_id)
1022        .skip_binder()
1023        .iter()
1024        .filter_map(|(pred, _)| {
1025            if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() {
1026                if trait_pred.def_id() != trait_def_id {
1027                    Some(tcx.def_path_str(trait_pred.def_id()))
1028                } else {
1029                    None
1030                }
1031            } else {
1032                None
1033            }
1034        })
1035        .collect();
1036
1037    let methods = extract_trait_methods(tcx, trait_def_id);
1038    let assoc_types = extract_trait_assoc_types(tcx, trait_def_id);
1039    let assoc_consts = extract_trait_assoc_consts(tcx, trait_def_id);
1040    let implementors = get_trait_implementors(tcx, trait_def_id);
1041
1042    Some(TraitDetails {
1043        name,
1044        path,
1045        visibility,
1046        generics,
1047        where_clause,
1048        is_auto,
1049        is_unsafe,
1050        supertraits,
1051        methods,
1052        assoc_types,
1053        assoc_consts,
1054        docs,
1055        attributes,
1056        source,
1057        implementors,
1058        span,
1059    })
1060}
1061
1062fn extract_trait_methods(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Vec<TraitMethodInfo> {
1063    tcx.associated_item_def_ids(trait_def_id)
1064        .iter()
1065        .filter_map(|&item_def_id| {
1066            let item = tcx.associated_item(item_def_id);
1067            if !matches!(item.kind, ty::AssocKind::Fn { .. }) {
1068                return None;
1069            }
1070
1071            let sig = tcx.fn_sig(item_def_id).skip_binder();
1072            let has_default = item.defaultness(tcx).has_value();
1073            let docs = extract_docs(tcx, item_def_id);
1074            let attributes = extract_attributes(tcx, item_def_id);
1075            let default_body = if has_default {
1076                get_source_for_def(tcx, item_def_id)
1077            } else {
1078                None
1079            };
1080
1081            Some(TraitMethodInfo {
1082                name: item.name().to_string(),
1083                signature: format!("{:?}", sig),
1084                parsed_signature: parse_fn_signature(tcx, item_def_id),
1085                has_default,
1086                default_body,
1087                is_unsafe: sig.safety().is_unsafe(),
1088                docs,
1089                attributes,
1090                span: extract_span_info(tcx, item_def_id),
1091            })
1092        })
1093        .collect()
1094}
1095
1096fn extract_trait_assoc_types(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Vec<AssocTypeInfo> {
1097    tcx.associated_item_def_ids(trait_def_id)
1098        .iter()
1099        .filter_map(|&item_def_id| {
1100            let item = tcx.associated_item(item_def_id);
1101            if !matches!(item.kind, ty::AssocKind::Type { .. }) {
1102                return None;
1103            }
1104
1105            let docs = extract_docs(tcx, item_def_id);
1106            let bounds = extract_assoc_type_bounds(tcx, item_def_id);
1107
1108            Some(AssocTypeInfo {
1109                name: item.name().to_string(),
1110                ty: None,
1111                bounds,
1112                default: None, // TODO: check for default
1113                docs,
1114                span: extract_span_info(tcx, item_def_id),
1115            })
1116        })
1117        .collect()
1118}
1119
1120fn extract_assoc_type_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<String> {
1121    let predicates = tcx.item_bounds(def_id);
1122    predicates
1123        .skip_binder()
1124        .iter()
1125        .map(|pred| format!("{:?}", pred))
1126        .collect()
1127}
1128
1129fn extract_trait_assoc_consts(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Vec<AssocConstInfo> {
1130    tcx.associated_item_def_ids(trait_def_id)
1131        .iter()
1132        .filter_map(|&item_def_id| {
1133            let item = tcx.associated_item(item_def_id);
1134            if !matches!(item.kind, ty::AssocKind::Const { .. }) {
1135                return None;
1136            }
1137
1138            let ty = tcx.type_of(item_def_id).skip_binder();
1139            let docs = extract_docs(tcx, item_def_id);
1140
1141            Some(AssocConstInfo {
1142                name: item.name().to_string(),
1143                ty: format!("{:?}", ty),
1144                value: None,
1145                docs,
1146                span: extract_span_info(tcx, item_def_id),
1147            })
1148        })
1149        .collect()
1150}
1151
1152fn get_trait_implementors(tcx: TyCtxt<'_>, trait_def_id: DefId) -> Vec<String> {
1153    let mut implementors = Vec::new();
1154    let crate_items = tcx.hir_crate_items(());
1155
1156    for item_id in crate_items.free_items() {
1157        let impl_def_id = item_id.owner_id.to_def_id();
1158        if !matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. }) {
1159            continue;
1160        }
1161
1162        if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) {
1163            if trait_ref.skip_binder().def_id == trait_def_id {
1164                let self_ty = trait_ref.skip_binder().self_ty();
1165                implementors.push(get_type_path_string(tcx, self_ty));
1166            }
1167        }
1168    }
1169
1170    implementors
1171}
1172
1173fn extract_trait_impl_details(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<TraitImplDetails> {
1174    let trait_ref = tcx.impl_trait_ref(impl_def_id)?;
1175    let trait_ref = trait_ref.skip_binder();
1176
1177    let self_ty = get_type_path_string(tcx, trait_ref.self_ty());
1178    let trait_path = tcx.def_path_str(trait_ref.def_id);
1179    let generics = extract_generics(tcx, impl_def_id);
1180    let where_clause = extract_where_clause(tcx, impl_def_id);
1181    let span = extract_span_info(tcx, impl_def_id);
1182    let source = get_source_for_def(tcx, impl_def_id);
1183
1184    let polarity = tcx.impl_polarity(impl_def_id);
1185    let is_negative = matches!(polarity, ty::ImplPolarity::Negative);
1186
1187    let methods = extract_impl_methods(tcx, impl_def_id);
1188    let assoc_types = extract_impl_assoc_types(tcx, impl_def_id);
1189    let assoc_consts = extract_impl_assoc_consts(tcx, impl_def_id);
1190
1191    Some(TraitImplDetails {
1192        self_ty,
1193        trait_path,
1194        generics,
1195        where_clause,
1196        is_negative,
1197        is_unsafe: false,
1198        methods,
1199        assoc_types,
1200        assoc_consts,
1201        source,
1202        span,
1203    })
1204}
1205
1206fn extract_inherent_impl_details(
1207    tcx: TyCtxt<'_>,
1208    impl_def_id: DefId,
1209) -> Option<InherentImplDetails> {
1210    let self_ty = tcx.type_of(impl_def_id).skip_binder();
1211    let generics = extract_generics(tcx, impl_def_id);
1212    let where_clause = extract_where_clause(tcx, impl_def_id);
1213    let span = extract_span_info(tcx, impl_def_id);
1214    let source = get_source_for_def(tcx, impl_def_id);
1215
1216    let methods = extract_impl_methods(tcx, impl_def_id);
1217    let assoc_types = extract_impl_assoc_types(tcx, impl_def_id);
1218    let assoc_consts = extract_impl_assoc_consts(tcx, impl_def_id);
1219
1220    Some(InherentImplDetails {
1221        self_ty: get_type_path_string(tcx, self_ty),
1222        generics,
1223        where_clause,
1224        is_unsafe: false,
1225        methods,
1226        assoc_consts,
1227        assoc_types,
1228        source,
1229        span,
1230    })
1231}
1232
1233fn extract_impl_methods(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Vec<MethodDetails> {
1234    tcx.associated_item_def_ids(impl_def_id)
1235        .iter()
1236        .filter_map(|&item_def_id| {
1237            let item = tcx.associated_item(item_def_id);
1238            if !matches!(item.kind, ty::AssocKind::Fn { .. }) {
1239                return None;
1240            }
1241
1242            let sig = tcx.fn_sig(item_def_id).skip_binder();
1243            let docs = extract_docs(tcx, item_def_id);
1244            let attributes = extract_attributes(tcx, item_def_id);
1245            let body_source = get_source_for_def(tcx, item_def_id);
1246            let body_tokens = extract_body_tokens(tcx, item_def_id);
1247
1248            Some(MethodDetails {
1249                name: item.name().to_string(),
1250                path: tcx.def_path_str(item_def_id),
1251                signature: format!("{:?}", sig),
1252                parsed_signature: parse_fn_signature(tcx, item_def_id),
1253                has_body: true,
1254                body_source,
1255                body_tokens,
1256                is_unsafe: sig.safety().is_unsafe(),
1257                is_const: tcx.is_const_fn(item_def_id),
1258                is_async: tcx.asyncness(item_def_id).is_async(),
1259                docs,
1260                attributes,
1261                span: extract_span_info(tcx, item_def_id),
1262            })
1263        })
1264        .collect()
1265}
1266
1267fn extract_impl_assoc_types(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Vec<AssocTypeInfo> {
1268    tcx.associated_item_def_ids(impl_def_id)
1269        .iter()
1270        .filter_map(|&item_def_id| {
1271            let item = tcx.associated_item(item_def_id);
1272            if !matches!(item.kind, ty::AssocKind::Type { .. }) {
1273                return None;
1274            }
1275
1276            let ty = tcx.type_of(item_def_id).skip_binder();
1277            let docs = extract_docs(tcx, item_def_id);
1278
1279            Some(AssocTypeInfo {
1280                name: item.name().to_string(),
1281                ty: Some(format!("{:?}", ty)),
1282                bounds: Vec::new(),
1283                default: None,
1284                docs,
1285                span: extract_span_info(tcx, item_def_id),
1286            })
1287        })
1288        .collect()
1289}
1290
1291fn extract_impl_assoc_consts(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Vec<AssocConstInfo> {
1292    tcx.associated_item_def_ids(impl_def_id)
1293        .iter()
1294        .filter_map(|&item_def_id| {
1295            let item = tcx.associated_item(item_def_id);
1296            if !matches!(item.kind, ty::AssocKind::Const { .. }) {
1297                return None;
1298            }
1299
1300            let ty = tcx.type_of(item_def_id).skip_binder();
1301            let docs = extract_docs(tcx, item_def_id);
1302
1303            Some(AssocConstInfo {
1304                name: item.name().to_string(),
1305                ty: format!("{:?}", ty),
1306                value: None,
1307                docs,
1308                span: extract_span_info(tcx, item_def_id),
1309            })
1310        })
1311        .collect()
1312}
1313
1314fn parse_fn_signature(tcx: TyCtxt<'_>, fn_def_id: DefId) -> FunctionSignature {
1315    let sig = tcx.fn_sig(fn_def_id).skip_binder();
1316    let inputs = sig.inputs().skip_binder();
1317
1318    let mut receiver = None;
1319    let mut params = Vec::new();
1320
1321    for (i, ty) in inputs.iter().enumerate() {
1322        if i == 0 {
1323            let ty_str = format!("{:?}", ty);
1324            if ty_str.contains("Self") || ty_str.contains("self") {
1325                receiver = Some(ReceiverInfo {
1326                    kind: ty_str.clone(),
1327                    is_mut: ty_str.contains("mut"),
1328                    is_ref: ty_str.contains("&"),
1329                    lifetime: None,
1330                });
1331                continue;
1332            }
1333        }
1334
1335        params.push(ParamInfo {
1336            name: format!("arg{}", i),
1337            ty: format!("{:?}", ty),
1338            is_mut: false,
1339        });
1340    }
1341
1342    let return_ty = {
1343        let output = sig.output().skip_binder();
1344        if output.is_unit() {
1345            None
1346        } else {
1347            Some(format!("{:?}", output))
1348        }
1349    };
1350
1351    FunctionSignature {
1352        receiver,
1353        params,
1354        return_ty,
1355        generics: extract_generics(tcx, fn_def_id),
1356        where_clause: extract_where_clause(tcx, fn_def_id),
1357    }
1358}
1359
1360fn extract_type_alias(tcx: TyCtxt<'_>, def_id: DefId) -> Option<TypeAliasInfo> {
1361    let name = tcx.item_name(def_id).to_string();
1362    let path = tcx.def_path_str(def_id);
1363    let visibility = extract_visibility(tcx, def_id);
1364    let generics = extract_generics(tcx, def_id);
1365    let span = extract_span_info(tcx, def_id);
1366    let docs = extract_docs(tcx, def_id);
1367
1368    let ty = tcx.type_of(def_id).skip_binder();
1369    let ty_str = format!("{:?}", ty);
1370
1371    let (resolved_ty, _chain) = resolve_type_alias_chain(tcx, def_id);
1372
1373    Some(TypeAliasInfo {
1374        name,
1375        path,
1376        generics,
1377        ty: ty_str,
1378        resolved_ty,
1379        visibility,
1380        docs,
1381        span,
1382    })
1383}
1384
1385fn extract_module_info(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> Option<ModuleInfo> {
1386    let def_id = local_def_id.to_def_id();
1387
1388    let name = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1389        tcx.item_name(def_id).to_string()
1390    })) {
1391        Ok(n) if !n.is_empty() => n,
1392        _ => return None,
1393    };
1394
1395    let path = tcx.def_path_str(def_id);
1396    let visibility = extract_visibility(tcx, def_id);
1397
1398    // Get child items
1399    let mut items = Vec::new();
1400    let mut reexports = Vec::new();
1401
1402    for child in tcx.module_children(def_id) {
1403        let child_name = child.ident.to_string();
1404
1405        if child.reexport_chain.is_empty() {
1406            items.push(child_name);
1407        } else {
1408            // This is a re-export
1409            if let Some(child_def_id) = child.res.opt_def_id() {
1410                let original_path = tcx.def_path_str(child_def_id);
1411                reexports.push(ReexportInfo {
1412                    name: child_name,
1413                    original_path,
1414                    visibility: Visibility::Public, // Re-exports are typically public
1415                });
1416            }
1417        }
1418    }
1419
1420    Some(ModuleInfo {
1421        name,
1422        path,
1423        visibility,
1424        items,
1425        reexports,
1426    })
1427}
1428
1429// ============================================================================
1430// Output Functions
1431// ============================================================================
1432
1433fn output_extracted_info(info: &CrateTypeInfo, output: &Option<String>) {
1434    let json = serde_json::to_string_pretty(info).expect("Failed to serialize");
1435
1436    match output {
1437        Some(path) => {
1438            std::fs::write(path, json).expect("Failed to write output file");
1439        }
1440        None => {
1441            println!("{}", json);
1442        }
1443    }
1444}
1445
1446fn output_query_result(result: &QueryResult) {
1447    let json = serde_json::to_string(result).expect("Failed to serialize");
1448    println!("{}", json);
1449}
1450
1451// ============================================================================
1452// Query Execution
1453// ============================================================================
1454
1455fn parse_query(query_str: &str) -> Query {
1456    let parts: Vec<&str> = query_str.split(':').collect();
1457
1458    match parts[0] {
1459        "list_items" => Query::ListItems,
1460        "get_type" if parts.len() >= 2 => Query::GetType {
1461            path: parts[1].to_string(),
1462        },
1463        "get_trait_impls" if parts.len() >= 2 => Query::GetTraitImpls {
1464            type_path: parts[1].to_string(),
1465        },
1466        "get_inherent_impls" if parts.len() >= 2 => Query::GetInherentImpls {
1467            type_path: parts[1].to_string(),
1468        },
1469        "get_fields" if parts.len() >= 2 => Query::GetFields {
1470            type_path: parts[1].to_string(),
1471        },
1472        "get_layout" if parts.len() >= 2 => Query::GetLayout {
1473            type_path: parts[1].to_string(),
1474        },
1475        "get_traits" => Query::GetTraits,
1476        "get_trait" if parts.len() >= 2 => Query::GetTrait {
1477            path: parts[1].to_string(),
1478        },
1479        "find_types" if parts.len() >= 2 => Query::FindTypes {
1480            pattern: parts[1].to_string(),
1481        },
1482        "resolve_alias" if parts.len() >= 2 => Query::ResolveAlias {
1483            path: parts[1].to_string(),
1484        },
1485        "check_impl" if parts.len() >= 3 => Query::CheckImpl {
1486            type_path: parts[1].to_string(),
1487            trait_path: parts[2].to_string(),
1488        },
1489        "get_implementors" if parts.len() >= 2 => Query::GetImplementors {
1490            trait_path: parts[1].to_string(),
1491        },
1492        _ => {
1493            eprintln!("Unknown query: {}", query_str);
1494            eprintln!("Available queries:");
1495            eprintln!("  list_items");
1496            eprintln!("  get_type:<path>");
1497            eprintln!("  get_trait_impls:<type_path>");
1498            eprintln!("  get_inherent_impls:<type_path>");
1499            eprintln!("  get_fields:<type_path>");
1500            eprintln!("  get_layout:<type_path>");
1501            eprintln!("  get_traits");
1502            eprintln!("  get_trait:<path>");
1503            eprintln!("  find_types:<pattern>");
1504            eprintln!("  resolve_alias:<path>");
1505            eprintln!("  check_impl:<type_path>:<trait_path>");
1506            eprintln!("  get_implementors:<trait_path>");
1507            std::process::exit(1);
1508        }
1509    }
1510}
1511
1512fn execute_query(tcx: TyCtxt<'_>, query: &Query) -> QueryResult {
1513    let info = extract_crate_info(tcx);
1514
1515    match query {
1516        Query::ListItems => QueryResult::Success {
1517            data: QueryData::Items { items: info.items },
1518        },
1519
1520        Query::GetType { path } => match info.types.get(path) {
1521            Some(type_details) => QueryResult::Success {
1522                data: QueryData::TypeInfo(type_details.clone()),
1523            },
1524            None => QueryResult::Error {
1525                message: format!("Type not found: {}", path),
1526            },
1527        },
1528
1529        Query::GetTraitImpls { type_path } => {
1530            let impls = info.trait_impls.get(type_path).cloned().unwrap_or_default();
1531            QueryResult::Success {
1532                data: QueryData::TraitImpls { impls },
1533            }
1534        }
1535
1536        Query::GetInherentImpls { type_path } => {
1537            let impls = info
1538                .inherent_impls
1539                .get(type_path)
1540                .cloned()
1541                .unwrap_or_default();
1542            QueryResult::Success {
1543                data: QueryData::InherentImpls { impls },
1544            }
1545        }
1546
1547        Query::GetFields { type_path } => {
1548            let fields = info
1549                .types
1550                .get(type_path)
1551                .and_then(|t| t.fields.clone())
1552                .unwrap_or_default();
1553            QueryResult::Success {
1554                data: QueryData::Fields { fields },
1555            }
1556        }
1557
1558        Query::GetLayout { type_path } => match info.layouts.get(type_path) {
1559            Some(layout) => QueryResult::Success {
1560                data: QueryData::Layout(layout.clone()),
1561            },
1562            None => QueryResult::Error {
1563                message: format!("Layout not available for: {}", type_path),
1564            },
1565        },
1566
1567        Query::GetTraits => {
1568            let traits: Vec<TraitInfo> = info
1569                .traits
1570                .values()
1571                .map(|t| TraitInfo {
1572                    name: t.name.clone(),
1573                    path: t.path.clone(),
1574                    generics: t.generics.clone(),
1575                    required_methods: t.methods.iter().filter(|m| !m.has_default).count(),
1576                    provided_methods: t.methods.iter().filter(|m| m.has_default).count(),
1577                    supertraits: t.supertraits.clone(),
1578                })
1579                .collect();
1580            QueryResult::Success {
1581                data: QueryData::Traits { traits },
1582            }
1583        }
1584
1585        Query::GetTrait { path } => match info.traits.get(path) {
1586            Some(trait_details) => QueryResult::Success {
1587                data: QueryData::TraitDetails(trait_details.clone()),
1588            },
1589            None => QueryResult::Error {
1590                message: format!("Trait not found: {}", path),
1591            },
1592        },
1593
1594        Query::FindTypes { pattern } => {
1595            let types: Vec<TypeSummary> = info
1596                .types
1597                .values()
1598                .filter(|t| bronzite_types::path_matches_pattern(&t.path, pattern))
1599                .map(|t| TypeSummary {
1600                    name: t.name.clone(),
1601                    path: t.path.clone(),
1602                    kind: t.kind.clone(),
1603                    generics: t.generics.clone(),
1604                })
1605                .collect();
1606            QueryResult::Success {
1607                data: QueryData::Types { types },
1608            }
1609        }
1610
1611        Query::ResolveAlias { path } => match info.type_aliases.get(path) {
1612            Some(alias) => QueryResult::Success {
1613                data: QueryData::ResolvedType {
1614                    original: alias.ty.clone(),
1615                    resolved: alias.resolved_ty.clone(),
1616                    chain: vec![alias.path.clone(), alias.resolved_ty.clone()],
1617                },
1618            },
1619            None => QueryResult::Error {
1620                message: format!("Type alias not found: {}", path),
1621            },
1622        },
1623
1624        Query::CheckImpl {
1625            type_path,
1626            trait_path,
1627        } => {
1628            let impls = info.trait_impls.get(type_path).cloned().unwrap_or_default();
1629            let impl_info = impls.into_iter().find(|i| i.trait_path == *trait_path);
1630            QueryResult::Success {
1631                data: QueryData::ImplCheck {
1632                    implements: impl_info.is_some(),
1633                    impl_info,
1634                },
1635            }
1636        }
1637
1638        Query::GetImplementors { trait_path } => match info.traits.get(trait_path) {
1639            Some(trait_details) => {
1640                let types: Vec<TypeSummary> = trait_details
1641                    .implementors
1642                    .iter()
1643                    .filter_map(|ty_str| {
1644                        info.types
1645                            .values()
1646                            .find(|t| t.path == *ty_str)
1647                            .map(|t| TypeSummary {
1648                                name: t.name.clone(),
1649                                path: t.path.clone(),
1650                                kind: t.kind.clone(),
1651                                generics: t.generics.clone(),
1652                            })
1653                    })
1654                    .collect();
1655                QueryResult::Success {
1656                    data: QueryData::Implementors { types },
1657                }
1658            }
1659            None => QueryResult::Error {
1660                message: format!("Trait not found: {}", trait_path),
1661            },
1662        },
1663
1664        Query::Ping => QueryResult::Success {
1665            data: QueryData::Pong,
1666        },
1667
1668        Query::Shutdown => QueryResult::Success {
1669            data: QueryData::ShuttingDown,
1670        },
1671    }
1672}