1use rustc_hir::def_id::DefId;
2use rustc_middle::ty::print::with_no_trimmed_paths;
3use rustc_middle::ty::{GenericArgKind, Ty, TyCtxt, TyKind};
4use serde_json::Value;
5use std::sync::OnceLock;
6use syn::Expr;
7
8pub fn get_cleaned_def_path_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
12 let def_id_str = format!("{:?}", def_id);
13 let mut parts: Vec<&str> = def_id_str.split("::").collect();
14
15 let crate_name = def_id_str
19 .split("~ ")
20 .nth(1)
21 .and_then(|p| p.split('[').next())
22 .unwrap_or("");
23
24 let mut remove_first = false;
25 if let Some(first_part) = parts.get_mut(0) {
26 match crate_name {
27 "core" => *first_part = "core",
28 "std" => *first_part = "std",
29 "alloc" => *first_part = "alloc",
30 _ => remove_first = true,
31 }
32 }
33 if remove_first && !parts.is_empty() {
34 parts.remove(0);
35 }
36
37 let new_parts: Vec<String> = parts
38 .into_iter()
39 .filter_map(|s| {
40 if s.contains("{") {
41 if remove_first {
42 get_struct_name(tcx, def_id)
43 } else {
44 None
45 }
46 } else {
47 Some(s.to_string())
48 }
49 })
50 .collect();
51
52 let mut cleaned_path = new_parts.join("::");
53 cleaned_path = cleaned_path.trim_end_matches(')').to_string();
54 cleaned_path
55}
56
57fn get_struct_name(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
60 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
61 if let Some(impl_id) = assoc_item.impl_container(tcx) {
62 let ty = tcx.type_of(impl_id).skip_binder();
63 let type_name = ty.to_string();
64 let struct_name = type_name
65 .split('<')
66 .next()
67 .unwrap_or("")
68 .split("::")
69 .last()
70 .unwrap_or("")
71 .to_string();
72
73 return Some(struct_name);
74 }
75 }
76 None
77}
78
79fn public_ty_path<'tcx>(ty: Ty<'tcx>) -> String {
82 let full = with_no_trimmed_paths!(ty.to_string());
84 let base = full.split('<').next().unwrap_or(&full);
85 base.split("::").skip(1).collect::<Vec<_>>().join("::")
86}
87
88pub fn public_def_path<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String {
93 let defining_crate = tcx.crate_name(def_id.krate).to_string();
94 match tcx.def_kind(def_id) {
95 rustc_hir::def::DefKind::Struct
96 | rustc_hir::def::DefKind::Enum
97 | rustc_hir::def::DefKind::Union
98 | rustc_hir::def::DefKind::TyAlias => {
99 let path = public_ty_path(tcx.type_of(def_id).skip_binder());
100 if path.is_empty() {
101 defining_crate
102 } else {
103 format!("{defining_crate}::{path}")
104 }
105 }
106 rustc_hir::def::DefKind::AssocFn => {
107 let assoc_item = tcx.associated_item(def_id);
108 let name = assoc_item.name();
109 if let Some(trait_item) = assoc_item.trait_item_def_id() {
111 let trait_path = get_cleaned_def_path_name(tcx, tcx.parent(trait_item));
112 format!("{trait_path}::{name}")
113 } else if let Some(impl_id) = assoc_item.impl_container(tcx) {
114 let self_ty = tcx.type_of(impl_id).skip_binder();
115 let def_path = get_cleaned_def_path_name(tcx, def_id);
116 if matches!(self_ty.kind(), TyKind::Adt(..)) && def_path.contains("::rcs::") {
118 let type_path = public_ty_path(self_ty);
119 let module = type_path.rsplit_once("::").map(|(m, _)| m).unwrap_or("");
120 if module.is_empty() {
121 def_path
122 } else {
123 format!("{defining_crate}::{module}::{name}")
124 }
125 } else {
126 def_path
127 }
128 } else {
129 get_cleaned_def_path_name(tcx, def_id)
130 }
131 }
132 _ => get_cleaned_def_path_name(tcx, def_id),
133 }
134}
135
136pub fn get_struct_self_ty<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Ty<'tcx>> {
140 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
141 let impl_id = assoc_item.impl_container(tcx)?;
142 let self_ty = tcx.type_of(impl_id).skip_binder();
143 if matches!(self_ty.kind(), TyKind::Adt(_, _)) {
144 return Some(self_ty);
145 }
146 return None;
147 }
148 if matches!(
149 tcx.def_kind(def_id),
150 rustc_hir::def::DefKind::Struct | rustc_hir::def::DefKind::Enum
151 ) {
152 let self_ty = tcx.type_of(def_id).skip_binder();
153 if matches!(self_ty.kind(), TyKind::Adt(_, _)) {
154 return Some(self_ty);
155 }
156 }
157 None
158}
159
160fn get_std_api_signature_json() -> &'static Value {
163 static JSON: OnceLock<Value> = OnceLock::new();
164 JSON.get_or_init(|| {
165 serde_json::from_str(include_str!("data/std_sig.json")).expect("Unable to parse JSON")
166 })
167}
168
169fn get_known_std_names<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Vec<String>> {
175 let std_func_name = public_def_path(tcx, def_id);
176 let json_data = get_std_api_signature_json();
177
178 if let Some(arg_info) = json_data.get(&std_func_name) {
179 if let Some(args_name) = arg_info.as_array() {
180 if args_name.is_empty() {
181 return Some(vec!["0".to_string()]);
182 }
183 let mut result = Vec::new();
184 for arg in args_name {
185 if let Some(sp_name) = arg.as_str() {
186 result.push(sp_name.to_string());
187 }
188 }
189 return Some(result);
190 }
191 }
192 None
193}
194
195fn extract_pat_ident(pat: &rustc_hir::Pat<'_>) -> Option<rustc_span::symbol::Ident> {
199 match &pat.kind {
200 rustc_hir::PatKind::Binding(_, _, ident, _) => Some(*ident),
201 rustc_hir::PatKind::Ref(inner, ..) => extract_pat_ident(inner),
202 _ => None,
203 }
204}
205
206fn parse_local_signature<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> (Vec<String>, Vec<Ty<'tcx>>) {
207 let Some(local_def_id) = def_id.as_local() else {
208 return (vec!["0".to_string()], Vec::new());
209 };
210 let hir_body = tcx.hir_body_owned_by(local_def_id);
211 if hir_body.params.is_empty() {
212 return (vec!["0".to_string()], Vec::new());
213 }
214
215 let params = hir_body.params;
216 let typeck_results = tcx.typeck_body(hir_body.id());
217 let mut param_names = Vec::new();
218 let mut param_tys = Vec::new();
219 for param in params {
220 let ident = extract_pat_ident(param.pat);
221 match ident {
222 Some(name) => {
223 param_names.push(name.name.to_string());
224 }
225 None => {
226 param_names.push(String::new());
227 }
228 }
229 param_tys.push(typeck_results.pat_ty(param.pat));
230 }
231 (param_names, param_tys)
232}
233
234fn parse_outside_signature<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> (Vec<String>, Vec<Ty<'tcx>>) {
239 let sig = tcx.fn_sig(def_id).skip_binder();
240 let param_tys: Vec<Ty<'tcx>> = sig.inputs().skip_binder().iter().copied().collect();
241
242 if let Some(args_name) = get_known_std_names(tcx, def_id) {
243 return (args_name, param_tys);
244 }
245
246 let args_name = (0..param_tys.len()).map(|i| format!("{}", i)).collect();
247 (args_name, param_tys)
248}
249
250fn parse_trait_fn_sig<'tcx>(
254 tcx: TyCtxt<'tcx>,
255 def_id: DefId,
256) -> Option<(Vec<String>, Vec<Ty<'tcx>>)> {
257 let local_def_id = def_id.as_local()?;
258 if !matches!(tcx.def_kind(def_id), rustc_hir::def::DefKind::AssocFn) {
259 return None;
260 }
261 let trait_item_id = rustc_hir::TraitItemId {
262 owner_id: rustc_hir::OwnerId {
263 def_id: local_def_id,
264 },
265 };
266 let item = tcx.hir_trait_item(trait_item_id);
267 let (_sig, trait_fn) = match &item.kind {
268 rustc_hir::TraitItemKind::Fn(sig, tf) => (sig, tf),
269 _ => return None,
270 };
271 let names: Vec<String> = match trait_fn {
272 rustc_hir::TraitFn::Required(param_names) => param_names
273 .iter()
274 .filter_map(|opt| opt.map(|ident| ident.name.to_string()))
275 .collect(),
276 rustc_hir::TraitFn::Provided(body_id) => {
277 let body = tcx.hir_body(*body_id);
278 body.params
279 .iter()
280 .filter_map(|param| extract_pat_ident(param.pat).map(|i| i.name.to_string()))
281 .collect()
282 }
283 };
284 let sig = tcx.fn_sig(def_id).skip_binder();
285 let param_tys: Vec<Ty<'tcx>> = sig.inputs().skip_binder().iter().copied().collect();
286 if names.len() == param_tys.len() {
287 Some((names, param_tys))
288 } else {
289 None
290 }
291}
292
293pub fn parse_signature<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> (Vec<String>, Vec<Ty<'tcx>>) {
296 if def_id.as_local().is_some() && tcx.is_mir_available(def_id) {
297 parse_local_signature(tcx, def_id)
298 } else if def_id.is_local() {
299 if let Some((names, tys)) = parse_trait_fn_sig(tcx, def_id) {
300 return (names, tys);
301 }
302 if matches!(
303 tcx.def_kind(def_id),
304 rustc_hir::def::DefKind::Fn | rustc_hir::def::DefKind::AssocFn
305 ) {
306 parse_outside_signature(tcx, def_id)
307 } else {
308 (vec!["0".to_string()], Vec::new())
309 }
310 } else {
311 parse_outside_signature(tcx, def_id)
312 }
313}
314
315pub fn access_ident_recursive(expr: &Expr) -> Option<(String, Vec<String>)> {
323 match expr {
324 Expr::Path(syn::ExprPath { path, .. }) => {
325 if path.segments.len() == 1 {
326 rap_debug!("expr2 {:?}", expr);
327 let ident = path.segments[0].ident.to_string();
328 Some((ident, Vec::new()))
329 } else {
330 None
331 }
332 }
333 Expr::Field(syn::ExprField { base, member, .. }) => {
334 let (base_ident, mut fields) =
335 if let Some((base_ident, fields)) = access_ident_recursive(base) {
336 (base_ident, fields)
337 } else {
338 return None;
339 };
340 let field_name = match member {
341 syn::Member::Named(ident) => ident.to_string(),
342 syn::Member::Unnamed(index) => index.index.to_string(),
343 };
344 fields.push(field_name);
345 Some((base_ident, fields))
346 }
347 _ => None,
348 }
349}
350
351pub fn match_ty_with_ident<'tcx>(
357 tcx: TyCtxt<'tcx>,
358 def_id: DefId,
359 type_ident: String,
360) -> Option<Ty<'tcx>> {
361 if let Some(primitive_ty) = match_primitive_type(tcx, &type_ident) {
362 return Some(primitive_ty);
363 }
364 if let Some(std_ty) = match_std_type(tcx, &type_ident) {
365 return Some(std_ty);
366 }
367 if let Some(param_ty) = find_declared_generic_param(tcx, def_id, &type_ident) {
368 return Some(param_ty);
369 }
370 find_generic_param(tcx, def_id, &type_ident)
371}
372
373fn match_std_type<'tcx>(tcx: TyCtxt<'tcx>, type_ident: &str) -> Option<Ty<'tcx>> {
377 match type_ident {
378 "Char" | "AsciiChar" => {
379 let did = *crate::def_id::ascii_char_types().first()?;
380 let adt = tcx.adt_def(did);
381 Some(Ty::new_adt(
382 tcx,
383 adt,
384 rustc_middle::ty::GenericArgs::empty(),
385 ))
386 }
387 _ => None,
388 }
389}
390
391fn find_declared_generic_param<'tcx>(
392 tcx: TyCtxt<'tcx>,
393 def_id: DefId,
394 type_ident: &str,
395) -> Option<Ty<'tcx>> {
396 tcx.generics_of(def_id)
397 .own_params
398 .iter()
399 .find(|param| param.name.as_str() == type_ident)
400 .map(|param| {
401 tcx.mk_ty_from_kind(TyKind::Param(rustc_middle::ty::ParamTy {
402 index: param.index,
403 name: param.name,
404 }))
405 })
406}
407
408fn match_primitive_type<'tcx>(tcx: TyCtxt<'tcx>, type_ident: &str) -> Option<Ty<'tcx>> {
411 match type_ident {
412 "i8" => Some(tcx.types.i8),
413 "i16" => Some(tcx.types.i16),
414 "i32" => Some(tcx.types.i32),
415 "i64" => Some(tcx.types.i64),
416 "i128" => Some(tcx.types.i128),
417 "isize" => Some(tcx.types.isize),
418 "u8" => Some(tcx.types.u8),
419 "u16" => Some(tcx.types.u16),
420 "u32" => Some(tcx.types.u32),
421 "u64" => Some(tcx.types.u64),
422 "u128" => Some(tcx.types.u128),
423 "usize" => Some(tcx.types.usize),
424 "f16" => Some(tcx.types.f16),
425 "f32" => Some(tcx.types.f32),
426 "f64" => Some(tcx.types.f64),
427 "f128" => Some(tcx.types.f128),
428 "bool" => Some(tcx.types.bool),
429 "char" => Some(tcx.types.char),
430 "str" => Some(tcx.types.str_),
431 _ => None,
432 }
433}
434
435fn find_generic_param<'tcx>(
438 tcx: TyCtxt<'tcx>,
439 def_id: DefId,
440 type_ident: &str,
441) -> Option<Ty<'tcx>> {
442 rap_debug!(
443 "Searching for generic param: {} in {:?}",
444 type_ident,
445 def_id
446 );
447 let (_, param_tys) = parse_signature(tcx, def_id);
448 rap_debug!("Function parameter types: {:?} of {:?}", param_tys, def_id);
449 for &ty in ¶m_tys {
450 if let Some(found) = find_generic_in_ty(tcx, ty, type_ident) {
451 return Some(found);
452 }
453 }
454
455 if let Some(struct_ty) = get_struct_self_ty(tcx, def_id) {
456 if let Some(found) = find_generic_in_ty(tcx, struct_ty, type_ident) {
457 return Some(found);
458 }
459 }
460
461 let ret_ty = tcx.fn_sig(def_id).skip_binder().output().skip_binder();
465 if let Some(found) = find_generic_in_ty(tcx, ret_ty, type_ident) {
466 return Some(found);
467 }
468
469 None
470}
471
472fn find_generic_in_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, type_ident: &str) -> Option<Ty<'tcx>> {
478 match ty.kind() {
479 TyKind::Param(param_ty) => {
480 if param_ty.name.as_str() == type_ident {
481 return Some(ty);
482 }
483 }
484 TyKind::RawPtr(ty, _)
485 | TyKind::Ref(_, ty, _)
486 | TyKind::Slice(ty)
487 | TyKind::Array(ty, _) => {
488 if let Some(found) = find_generic_in_ty(tcx, *ty, type_ident) {
489 return Some(found);
490 }
491 }
492 TyKind::Tuple(tys) => {
493 for tuple_ty in tys.iter() {
494 if let Some(found) = find_generic_in_ty(tcx, tuple_ty, type_ident) {
495 return Some(found);
496 }
497 }
498 }
499 TyKind::Adt(adt_def, substs) => {
500 let name = tcx.item_name(adt_def.did()).to_string();
501 if name == type_ident {
502 return Some(ty);
503 }
504 for field in adt_def.all_fields() {
505 let field_ty = crate::helpers::mir_utils::field_ty(tcx, field, substs);
506 if let Some(found) = find_generic_in_ty(tcx, field_ty, type_ident) {
507 return Some(found);
508 }
509 }
510 for subst in substs.iter() {
511 if let GenericArgKind::Type(subst_ty) = subst.kind() {
512 if let Some(found) = find_generic_in_ty(tcx, subst_ty, type_ident) {
513 return Some(found);
514 }
515 }
516 }
517 }
518 _ => {}
519 }
520 None
521}
522
523pub fn short_fn_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
524 let path = tcx.def_path_str(def_id);
525 path.rsplit("::").next().unwrap_or(&path).to_string()
526}
527
528pub fn resolve_field_name(tcx: TyCtxt<'_>, index: &usize, struct_def_id: Option<DefId>) -> String {
529 if let Some(struct_def_id) = struct_def_id
530 && let TyKind::Adt(adt_def, _) = tcx.type_of(struct_def_id).skip_binder().kind()
531 {
532 let variant = adt_def.non_enum_variant();
533 let field_idx = rustc_abi::FieldIdx::from_usize(*index);
534 if field_idx.as_usize() < variant.fields.len() {
535 return variant.fields[field_idx].name.to_string();
536 }
537 }
538 index.to_string()
539}