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
37pub use bronzite_types::CrateTypeInfo as CrateTypeInfoExport;
39
40#[derive(Parser, Debug, Clone, Serialize, Deserialize)]
42pub struct Args {
43 #[arg(short, long)]
45 pub query: Option<String>,
46
47 #[arg(long)]
49 pub extract: bool,
50
51 #[arg(long)]
53 pub output: Option<String>,
54}
55
56pub 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
115fn 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
125fn 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
134fn extract_docs(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
140 if !def_id.is_local() {
141 return None;
142 }
143
144 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
169fn 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 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
200fn 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 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
219fn 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 _ => {
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
429fn 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
455fn 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(¤t_def_id) {
467 break; }
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 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 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
491fn get_resolved_type<'tcx>(tcx: TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> String {
493 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
502pub 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 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 if let Some(item_info) = extract_item_info(tcx, def_id) {
534 info.items.push(item_info);
535 }
536
537 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 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 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
592fn 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 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 let variants = if def_kind == DefKind::Enum {
706 Some(extract_enum_variants(tcx, local_def_id))
707 } else {
708 None
709 };
710
711 let trait_impls = get_trait_impl_paths(tcx, def_id);
713
714 let inherent_methods = extract_inherent_method_summaries(tcx, def_id);
716
717 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 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, 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 let discriminant = match variant.discr {
886 ty::VariantDiscr::Explicit(def_id) => {
887 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 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 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 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, 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 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 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, });
1416 }
1417 }
1418 }
1419
1420 Some(ModuleInfo {
1421 name,
1422 path,
1423 visibility,
1424 items,
1425 reexports,
1426 })
1427}
1428
1429fn 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
1451fn 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}