1use rustc_hir::def_id::DefId;
2use rustc_middle::ty::{GenericArgKind, Ty, TyCtxt, TyKind};
3use serde_json::Value;
4use syn::Expr;
5
6pub fn get_cleaned_def_path_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
13 let def_id_str = format!("{:?}", def_id);
14 let mut parts: Vec<&str> = def_id_str.split("::").collect();
15
16 let mut remove_first = false;
17 if let Some(first_part) = parts.get_mut(0) {
18 if first_part.contains("core") {
19 *first_part = "core";
20 } else if first_part.contains("std") {
21 *first_part = "std";
22 } else if first_part.contains("alloc") {
23 *first_part = "alloc";
24 } else {
25 remove_first = true;
26 }
27 }
28 if remove_first && !parts.is_empty() {
29 parts.remove(0);
30 }
31
32 let new_parts: Vec<String> = parts
33 .into_iter()
34 .filter_map(|s| {
35 if s.contains("{") {
36 if remove_first {
37 get_struct_name(tcx, def_id)
38 } else {
39 None
40 }
41 } else {
42 Some(s.to_string())
43 }
44 })
45 .collect();
46
47 let mut cleaned_path = new_parts.join("::");
48 cleaned_path = cleaned_path.trim_end_matches(')').to_string();
49 cleaned_path
50}
51
52fn get_struct_name(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
55 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
56 if let Some(impl_id) = assoc_item.impl_container(tcx) {
57 let ty = tcx.type_of(impl_id).skip_binder();
58 let type_name = ty.to_string();
59 let struct_name = type_name
60 .split('<')
61 .next()
62 .unwrap_or("")
63 .split("::")
64 .last()
65 .unwrap_or("")
66 .to_string();
67
68 return Some(struct_name);
69 }
70 }
71 None
72}
73
74pub fn get_struct_self_ty<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Ty<'tcx>> {
78 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
79 let impl_id = assoc_item.impl_container(tcx)?;
80 let self_ty = tcx.type_of(impl_id).skip_binder();
81 match self_ty.kind() {
82 TyKind::Adt(_, _) => return Some(self_ty),
83 _ => return None,
84 }
85 }
86 let def_kind = tcx.def_kind(def_id);
87 if matches!(
88 def_kind,
89 rustc_hir::def::DefKind::Struct | rustc_hir::def::DefKind::Enum
90 ) {
91 let self_ty = tcx.type_of(def_id).skip_binder();
92 match self_ty.kind() {
93 TyKind::Adt(_, _) => return Some(self_ty),
94 _ => {}
95 }
96 }
97 None
98}
99
100fn get_std_api_signature_json() -> Value {
103 serde_json::from_str(include_str!("data/std_sig.json")).expect("Unable to parse JSON")
104}
105
106fn get_known_std_names<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Vec<String>> {
112 let std_func_name = get_cleaned_def_path_name(tcx, def_id);
113 let json_data = get_std_api_signature_json();
114
115 if let Some(arg_info) = json_data.get(&std_func_name) {
116 if let Some(args_name) = arg_info.as_array() {
117 if args_name.is_empty() {
118 return Some(vec!["0".to_string()]);
119 }
120 let mut result = Vec::new();
121 for arg in args_name {
122 if let Some(sp_name) = arg.as_str() {
123 result.push(sp_name.to_string());
124 }
125 }
126 return Some(result);
127 }
128 }
129 None
130}
131
132fn extract_pat_ident(pat: &rustc_hir::Pat<'_>) -> Option<rustc_span::symbol::Ident> {
136 match &pat.kind {
137 rustc_hir::PatKind::Binding(_, _, ident, _) => Some(*ident),
138 rustc_hir::PatKind::Ref(inner, ..) => extract_pat_ident(inner),
139 _ => None,
140 }
141}
142
143fn parse_local_signature<'tcx>(
144 tcx: TyCtxt<'tcx>,
145 def_id: DefId,
146) -> (Vec<String>, Vec<Ty<'tcx>>) {
147 let local_def_id = def_id.as_local().unwrap();
148 let hir_body = tcx.hir_body_owned_by(local_def_id);
149 if hir_body.params.is_empty() {
150 return (vec!["0".to_string()], Vec::new());
151 }
152
153 let params = hir_body.params;
154 let typeck_results = tcx.typeck_body(hir_body.id());
155 let mut param_names = Vec::new();
156 let mut param_tys = Vec::new();
157 for param in params {
158 let ident = extract_pat_ident(¶m.pat);
159 match ident {
160 Some(name) => {
161 param_names.push(name.name.to_string());
162 }
163 None => {
164 param_names.push(String::new());
165 }
166 }
167 param_tys.push(typeck_results.pat_ty(param.pat));
168 }
169 (param_names, param_tys)
170}
171
172fn parse_outside_signature<'tcx>(
177 tcx: TyCtxt<'tcx>,
178 def_id: DefId,
179) -> (Vec<String>, Vec<Ty<'tcx>>) {
180 let sig = tcx.fn_sig(def_id).skip_binder();
181 let param_tys: Vec<Ty<'tcx>> = sig.inputs().skip_binder().iter().copied().collect();
182
183 if let Some(args_name) = get_known_std_names(tcx, def_id) {
184 return (args_name, param_tys);
185 }
186
187 let args_name = (0..param_tys.len()).map(|i| format!("{}", i)).collect();
188 (args_name, param_tys)
189}
190
191fn parse_trait_fn_sig<'tcx>(
195 tcx: TyCtxt<'tcx>,
196 def_id: DefId,
197) -> Option<(Vec<String>, Vec<Ty<'tcx>>)> {
198 let local_def_id = def_id.as_local()?;
199 if !matches!(tcx.def_kind(def_id), rustc_hir::def::DefKind::AssocFn) {
200 return None;
201 }
202 let trait_item_id = rustc_hir::TraitItemId {
203 owner_id: rustc_hir::OwnerId {
204 def_id: local_def_id,
205 },
206 };
207 let item = tcx.hir_trait_item(trait_item_id);
208 let (_sig, trait_fn) = match &item.kind {
209 rustc_hir::TraitItemKind::Fn(sig, tf) => (sig, tf),
210 _ => return None,
211 };
212 let names: Vec<String> = match trait_fn {
213 rustc_hir::TraitFn::Required(param_names) => param_names
214 .iter()
215 .filter_map(|opt| opt.map(|ident| ident.name.to_string()))
216 .collect(),
217 rustc_hir::TraitFn::Provided(body_id) => {
218 let body = tcx.hir_body(*body_id);
219 body.params
220 .iter()
221 .filter_map(|param| extract_pat_ident(¶m.pat).map(|i| i.name.to_string()))
222 .collect()
223 }
224 };
225 let sig = tcx.fn_sig(def_id).skip_binder();
226 let param_tys: Vec<Ty<'tcx>> = sig.inputs().skip_binder().iter().copied().collect();
227 if names.len() == param_tys.len() {
228 Some((names, param_tys))
229 } else {
230 None
231 }
232}
233
234pub fn parse_signature<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> (Vec<String>, Vec<Ty<'tcx>>) {
237 if def_id.as_local().is_some() && tcx.is_mir_available(def_id) {
238 parse_local_signature(tcx, def_id)
239 } else if def_id.is_local() {
240 if let Some((names, tys)) = parse_trait_fn_sig(tcx, def_id) {
241 return (names, tys);
242 }
243 if matches!(
244 tcx.def_kind(def_id),
245 rustc_hir::def::DefKind::Fn | rustc_hir::def::DefKind::AssocFn
246 ) {
247 parse_outside_signature(tcx, def_id)
248 } else {
249 (vec!["0".to_string()], Vec::new())
250 }
251 } else {
252 parse_outside_signature(tcx, def_id)
253 }
254}
255
256pub fn access_ident_recursive(expr: &Expr) -> Option<(String, Vec<String>)> {
264 match expr {
265 Expr::Path(syn::ExprPath { path, .. }) => {
266 if path.segments.len() == 1 {
267 rap_debug!("expr2 {:?}", expr);
268 let ident = path.segments[0].ident.to_string();
269 Some((ident, Vec::new()))
270 } else {
271 None
272 }
273 }
274 Expr::Field(syn::ExprField { base, member, .. }) => {
275 let (base_ident, mut fields) =
276 if let Some((base_ident, fields)) = access_ident_recursive(base) {
277 (base_ident, fields)
278 } else {
279 return None;
280 };
281 let field_name = match member {
282 syn::Member::Named(ident) => ident.to_string(),
283 syn::Member::Unnamed(index) => index.index.to_string(),
284 };
285 fields.push(field_name);
286 Some((base_ident, fields))
287 }
288 _ => None,
289 }
290}
291
292pub fn match_ty_with_ident<'tcx>(
298 tcx: TyCtxt<'tcx>,
299 def_id: DefId,
300 type_ident: String,
301) -> Option<Ty<'tcx>> {
302 if let Some(primitive_ty) = match_primitive_type(tcx, type_ident.clone()) {
303 return Some(primitive_ty);
304 }
305 if let Some(param_ty) = find_declared_generic_param(tcx, def_id, &type_ident) {
306 return Some(param_ty);
307 }
308 find_generic_param(tcx, def_id, type_ident)
309}
310
311fn find_declared_generic_param<'tcx>(
312 tcx: TyCtxt<'tcx>,
313 def_id: DefId,
314 type_ident: &str,
315) -> Option<Ty<'tcx>> {
316 tcx.generics_of(def_id)
317 .own_params
318 .iter()
319 .find(|param| param.name.as_str() == type_ident)
320 .map(|param| {
321 tcx.mk_ty_from_kind(TyKind::Param(rustc_middle::ty::ParamTy {
322 index: param.index,
323 name: param.name,
324 }))
325 })
326}
327
328fn match_primitive_type<'tcx>(tcx: TyCtxt<'tcx>, type_ident: String) -> Option<Ty<'tcx>> {
331 match type_ident.as_str() {
332 "i8" => Some(tcx.types.i8),
333 "i16" => Some(tcx.types.i16),
334 "i32" => Some(tcx.types.i32),
335 "i64" => Some(tcx.types.i64),
336 "i128" => Some(tcx.types.i128),
337 "isize" => Some(tcx.types.isize),
338 "u8" => Some(tcx.types.u8),
339 "u16" => Some(tcx.types.u16),
340 "u32" => Some(tcx.types.u32),
341 "u64" => Some(tcx.types.u64),
342 "u128" => Some(tcx.types.u128),
343 "usize" => Some(tcx.types.usize),
344 "f16" => Some(tcx.types.f16),
345 "f32" => Some(tcx.types.f32),
346 "f64" => Some(tcx.types.f64),
347 "f128" => Some(tcx.types.f128),
348 "bool" => Some(tcx.types.bool),
349 "char" => Some(tcx.types.char),
350 "str" => Some(tcx.types.str_),
351 _ => None,
352 }
353}
354
355fn find_generic_param<'tcx>(
358 tcx: TyCtxt<'tcx>,
359 def_id: DefId,
360 type_ident: String,
361) -> Option<Ty<'tcx>> {
362 rap_debug!(
363 "Searching for generic param: {} in {:?}",
364 type_ident,
365 def_id
366 );
367 let (_, param_tys) = parse_signature(tcx, def_id);
368 rap_debug!("Function parameter types: {:?} of {:?}", param_tys, def_id);
369 for &ty in ¶m_tys {
370 if let Some(found) = find_generic_in_ty(tcx, ty, &type_ident) {
371 return Some(found);
372 }
373 }
374
375 if let Some(struct_ty) = get_struct_self_ty(tcx, def_id) {
376 if let Some(found) = find_generic_in_ty(tcx, struct_ty, &type_ident) {
377 return Some(found);
378 }
379 }
380
381 None
382}
383
384fn find_generic_in_ty<'tcx>(
390 tcx: TyCtxt<'tcx>,
391 ty: Ty<'tcx>,
392 type_ident: &str,
393) -> Option<Ty<'tcx>> {
394 match ty.kind() {
395 TyKind::Param(param_ty) => {
396 if param_ty.name.as_str() == type_ident {
397 return Some(ty);
398 }
399 }
400 TyKind::RawPtr(ty, _)
401 | TyKind::Ref(_, ty, _)
402 | TyKind::Slice(ty)
403 | TyKind::Array(ty, _) => {
404 if let Some(found) = find_generic_in_ty(tcx, *ty, type_ident) {
405 return Some(found);
406 }
407 }
408 TyKind::Tuple(tys) => {
409 for tuple_ty in tys.iter() {
410 if let Some(found) = find_generic_in_ty(tcx, tuple_ty, type_ident) {
411 return Some(found);
412 }
413 }
414 }
415 TyKind::Adt(adt_def, substs) => {
416 let name = tcx.item_name(adt_def.did()).to_string();
417 if name == type_ident {
418 return Some(ty);
419 }
420 for field in adt_def.all_fields() {
421 #[cfg(not(rapx_ge_99))]
422 let field_ty = field.ty(tcx, substs);
423 #[cfg(rapx_ge_99)]
424 let field_ty = field.ty(tcx, substs).skip_norm_wip();
425 if let Some(found) = find_generic_in_ty(tcx, field_ty, type_ident) {
426 return Some(found);
427 }
428 }
429 for subst in substs.iter() {
430 if let GenericArgKind::Type(subst_ty) = subst.kind() {
431 if let Some(found) = find_generic_in_ty(tcx, subst_ty, type_ident) {
432 return Some(found);
433 }
434 }
435 }
436 }
437 _ => {}
438 }
439 None
440}
441
442pub fn short_fn_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
443 let path = tcx.def_path_str(def_id);
444 path.rsplit("::").next().unwrap_or(&path).to_string()
445}
446
447pub fn resolve_field_name(
448 tcx: TyCtxt<'_>,
449 index: &usize,
450 struct_def_id: Option<DefId>,
451) -> String {
452 if let Some(struct_def_id) = struct_def_id
453 && let TyKind::Adt(adt_def, _) =
454 tcx.type_of(struct_def_id).skip_binder().kind()
455 {
456 let variant = adt_def.non_enum_variant();
457 let field_idx = rustc_abi::FieldIdx::from_usize(*index);
458 if field_idx.as_usize() < variant.fields.len() {
459 return variant.fields[field_idx].name.to_string();
460 }
461 }
462 index.to_string()
463}