1use rustc_hir::{Safety, def::DefKind, def_id::DefId};
2use rustc_middle::{
3 mir::Local,
4 ty,
5 ty::{AssocKind, Mutability, Ty, TyCtxt, TyKind},
6};
7use rustc_span::{kw, sym};
8use std::{
9 collections::{HashMap, HashSet},
10 fmt::Debug,
11 hash::Hash,
12};
13use syn::Expr;
14
15pub use super::mir_scan::{
16 check_safety, collect_global_local_pairs, get_rawptr_deref, get_unsafe_callees,
17 place_has_raw_deref,
18};
19pub use super::name::{
20 access_ident_recursive, find_generic_in_ty, find_generic_param, get_cleaned_def_path_name,
21 get_known_std_names, get_std_api_signature_json, get_struct_name, match_primitive_type,
22 match_ty_with_ident, parse_local_signature, parse_outside_signature, parse_signature,
23};
24
25#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
26pub enum FnKind {
27 Fn,
28 Method,
29 Constructor,
30 Intrinsic,
31}
32
33#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
34pub struct FnInfo {
35 pub def_id: DefId,
36 pub fn_safety: Safety,
37 pub fn_kind: FnKind,
38}
39
40impl FnInfo {
41 pub fn new(def_id: DefId, fn_safety: Safety, fn_kind: FnKind) -> Self {
42 FnInfo {
43 def_id,
44 fn_safety,
45 fn_kind,
46 }
47 }
48}
49
50#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
51pub struct AdtInfo {
52 pub def_id: DefId,
53 pub literal_cons_enabled: bool,
54}
55
56impl AdtInfo {
57 pub fn new(def_id: DefId, literal_cons_enabled: bool) -> Self {
58 AdtInfo {
59 def_id,
60 literal_cons_enabled,
61 }
62 }
63}
64
65pub fn check_visibility(tcx: TyCtxt, func_defid: DefId) -> bool {
66 if !tcx.visibility(func_defid).is_public() {
67 return false;
68 }
69 true
70}
71
72pub fn get_type(tcx: TyCtxt<'_>, def_id: DefId) -> FnKind {
73 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
74 match assoc_item.kind {
75 AssocKind::Fn { has_self, .. } => {
76 if has_self {
77 return FnKind::Method;
78 } else {
79 let fn_sig = tcx.fn_sig(def_id).skip_binder();
80 let output = fn_sig.output().skip_binder();
81 if output.is_param(0) {
83 return FnKind::Constructor;
84 }
85 if let Some(impl_id) = assoc_item.impl_container(tcx) {
87 let ty = tcx.type_of(impl_id).skip_binder();
88 if output == ty {
89 return FnKind::Constructor;
90 }
91 }
92 match output.kind() {
93 TyKind::Ref(_, ref_ty, _) => {
94 if ref_ty.is_param(0) {
95 return FnKind::Constructor;
96 }
97 if let Some(impl_id) = assoc_item.impl_container(tcx) {
98 let ty = tcx.type_of(impl_id).skip_binder();
99 if *ref_ty == ty {
100 return FnKind::Constructor;
101 }
102 }
103 }
104 TyKind::Adt(adt_def, substs) => {
105 if adt_def.is_enum()
106 && (tcx.is_diagnostic_item(sym::Option, adt_def.did())
107 || tcx.is_diagnostic_item(sym::Result, adt_def.did())
108 || tcx.is_diagnostic_item(kw::Box, adt_def.did()))
109 {
110 let inner_ty = substs.type_at(0);
111 if inner_ty.is_param(0) {
112 return FnKind::Constructor;
113 }
114 if let Some(impl_id) = assoc_item.impl_container(tcx) {
115 let ty_impl = tcx.type_of(impl_id).skip_binder();
116 if inner_ty == ty_impl {
117 return FnKind::Constructor;
118 }
119 }
120 }
121 }
122 _ => {}
123 }
124 }
125 }
126 _ => todo!(),
127 }
128 }
129 return FnKind::Fn;
130}
131pub fn get_adt_via_method(tcx: TyCtxt<'_>, method_def_id: DefId) -> Option<AdtInfo> {
133 let assoc_item = tcx.opt_associated_item(method_def_id)?;
134 let impl_id = assoc_item.impl_container(tcx)?;
135 let ty = tcx.type_of(impl_id).skip_binder();
136 let adt_def = ty.ty_adt_def()?;
137 let adt_def_id = adt_def.did();
138
139 let all_fields: Vec<_> = adt_def.all_fields().collect();
140 let total_count = all_fields.len();
141
142 if total_count == 0 {
143 return Some(AdtInfo::new(adt_def_id, true));
144 }
145
146 let pub_count = all_fields
147 .iter()
148 .filter(|field| tcx.visibility(field.did).is_public())
149 .count();
150
151 if pub_count == 0 {
152 return None;
153 }
154 Some(AdtInfo::new(adt_def_id, pub_count == total_count))
155}
156pub fn get_impls_for_struct(tcx: TyCtxt<'_>, struct_def_id: DefId) -> Vec<DefId> {
158 let mut impls = Vec::new();
159 for item_id in tcx.hir_crate_items(()).free_items() {
160 let item = tcx.hir_item(item_id);
161 if let rustc_hir::ItemKind::Impl(impl_details) = &item.kind {
162 if let rustc_hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)) =
163 &impl_details.self_ty.kind
164 {
165 if let rustc_hir::def::Res::Def(_, def_id) = path.res {
166 if def_id == struct_def_id {
167 impls.push(item_id.owner_id.to_def_id());
168 }
169 }
170 }
171 }
172 }
173 impls
174}
175
176pub fn get_adt_def_id_by_adt_method(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
177 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
178 if let Some(impl_id) = assoc_item.impl_container(tcx) {
179 let ty = tcx.type_of(impl_id).skip_binder();
181 if let Some(adt_def) = ty.ty_adt_def() {
182 return Some(adt_def.did());
183 }
184 }
185 }
186 None
187}
188
189pub fn get_pointee(matched_ty: Ty<'_>) -> Ty<'_> {
191 let pointee = if let ty::RawPtr(ty_mut, _) = matched_ty.kind() {
193 get_pointee(*ty_mut)
194 } else if let ty::Ref(_, referred_ty, _) = matched_ty.kind() {
195 get_pointee(*referred_ty)
196 } else {
197 matched_ty
198 };
199 pointee
200}
201
202pub fn is_ptr(matched_ty: Ty<'_>) -> bool {
203 if let ty::RawPtr(_, _) = matched_ty.kind() {
204 return true;
205 }
206 false
207}
208
209pub fn has_mut_self_param(tcx: TyCtxt, def_id: DefId) -> bool {
210 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
211 match assoc_item.kind {
212 AssocKind::Fn { has_self, .. } => {
213 if has_self && tcx.is_mir_available(def_id) {
214 let body = tcx.optimized_mir(def_id);
215 let fst_arg = body.local_decls[Local::from_usize(1)].clone();
216 let ty = fst_arg.ty;
217 let is_mut_ref = matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut));
218 return fst_arg.mutability.is_mut() || is_mut_ref;
219 }
220 }
221 _ => (),
222 }
223 }
224 false
225}
226
227pub fn get_public_fields(tcx: TyCtxt, def_id: DefId) -> HashSet<usize> {
229 let adt_def = tcx.adt_def(def_id);
230 adt_def
231 .all_fields()
232 .enumerate()
233 .filter_map(|(index, field_def)| tcx.visibility(field_def.did).is_public().then_some(index))
234 .collect()
235}
236
237pub fn parse_expr_into_number(expr: &Expr) -> Option<usize> {
239 if let Expr::Lit(expr_lit) = expr {
240 if let syn::Lit::Int(lit_int) = &expr_lit.lit {
241 return lit_int.base10_parse::<usize>().ok();
242 }
243 }
244 None
245}
246
247pub fn get_all_std_fns_by_rustc_public(tcx: TyCtxt) -> Vec<DefId> {
248 let mut all_std_fn_def = Vec::new();
249 let mut results = Vec::new();
250 let mut core_fn_def: Vec<_> = rustc_public::find_crates("core")
251 .iter()
252 .flat_map(|krate| krate.fn_defs())
253 .collect();
254 let mut std_fn_def: Vec<_> = rustc_public::find_crates("std")
255 .iter()
256 .flat_map(|krate| krate.fn_defs())
257 .collect();
258 let mut alloc_fn_def: Vec<_> = rustc_public::find_crates("alloc")
259 .iter()
260 .flat_map(|krate| krate.fn_defs())
261 .collect();
262 all_std_fn_def.append(&mut core_fn_def);
263 all_std_fn_def.append(&mut std_fn_def);
264 all_std_fn_def.append(&mut alloc_fn_def);
265
266 for fn_def in &all_std_fn_def {
267 let def_id = crate::def_id::to_internal(fn_def, tcx);
268 results.push(def_id);
269 }
270 results
271}
272
273pub fn get_all_mutable_methods(tcx: TyCtxt, src_def_id: DefId) -> HashMap<DefId, HashSet<usize>> {
276 let mut std_results = HashMap::new();
277 if get_type(tcx, src_def_id) == FnKind::Constructor {
278 return std_results;
279 }
280 let all_std_fn_def = get_all_std_fns_by_rustc_public(tcx);
281 let target_adt_def = get_adt_def_id_by_adt_method(tcx, src_def_id);
282 let mut is_std = false;
283 for &def_id in &all_std_fn_def {
284 let adt_def = get_adt_def_id_by_adt_method(tcx, def_id);
285 if adt_def.is_some() && adt_def == target_adt_def && src_def_id != def_id {
286 if has_mut_self_param(tcx, def_id) {
287 std_results.insert(def_id, HashSet::new());
288 }
289 is_std = true;
290 }
291 }
292 if is_std {
293 return std_results;
294 }
295 let mut results = HashMap::new();
296 let public_fields = target_adt_def.map_or_else(HashSet::new, |def| get_public_fields(tcx, def));
297 let impl_vec = target_adt_def.map_or_else(Vec::new, |def| get_impls_for_struct(tcx, def));
298 for impl_id in impl_vec {
299 if !matches!(tcx.def_kind(impl_id), rustc_hir::def::DefKind::Impl { .. }) {
300 continue;
301 }
302 let associated_items = tcx.associated_items(impl_id);
303 for item in associated_items.in_definition_order() {
304 if let ty::AssocKind::Fn {
305 name: _,
306 has_self: _,
307 } = item.kind
308 {
309 let item_def_id = item.def_id;
310 if has_mut_self_param(tcx, item_def_id) {
311 let modified_fields = public_fields.clone();
312 results.insert(item_def_id, modified_fields);
313 }
314 }
315 }
316 }
317 results
318}
319
320pub fn get_cons(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<DefId> {
321 let mut cons = Vec::new();
322 if tcx.def_kind(def_id) == DefKind::Fn || get_type(tcx, def_id) == FnKind::Constructor {
323 return cons;
324 }
325 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
326 if let Some(impl_id) = assoc_item.impl_container(tcx) {
327 let ty = tcx.type_of(impl_id).skip_binder();
328 if let Some(adt_def) = ty.ty_adt_def() {
329 let adt_def_id = adt_def.did();
330 let impls = tcx.inherent_impls(adt_def_id);
331 for impl_def_id in impls {
332 for item in tcx.associated_item_def_ids(*impl_def_id) {
333 if (tcx.def_kind(*item) == DefKind::Fn
334 || tcx.def_kind(*item) == DefKind::AssocFn)
335 && get_type(tcx, *item) == FnKind::Constructor
336 {
337 cons.push(*item);
338 }
339 }
340 }
341 }
342 }
343 }
344 cons
345}
346
347pub fn get_muts(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<DefId> {
352 let mut muts = Vec::new();
353 if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
354 if let Some(impl_id) = assoc_item.impl_container(tcx) {
355 let ty = tcx.type_of(impl_id).skip_binder();
356 if let Some(adt_def) = ty.ty_adt_def() {
357 let adt_def_id = adt_def.did();
358 let impls = tcx.inherent_impls(adt_def_id);
359 for impl_def_id in impls {
360 for item in tcx.associated_item_def_ids(*impl_def_id) {
361 if !matches!(tcx.def_kind(*item), DefKind::Fn | DefKind::AssocFn) {
362 continue;
363 }
364 if get_type(tcx, *item) != FnKind::Method {
365 continue;
366 }
367 let Some(assoc) = tcx.opt_associated_item(*item) else {
368 continue;
369 };
370 if !matches!(assoc.kind, AssocKind::Fn { has_self: true, .. }) {
371 continue;
372 }
373 let fn_sig = tcx.fn_sig(*item).instantiate_identity().skip_binder();
374 let all = fn_sig.inputs_and_output;
375 let first_param = all.first().copied();
376 if let Some(TyKind::Ref(_, _, Mutability::Mut)) =
377 first_param.map(|ty| ty.kind())
378 {
379 muts.push(*item);
380 }
381 }
382 }
383 }
384 }
385 }
386 muts
387}
388
389pub fn append_fn_with_types(tcx: TyCtxt, def_id: DefId) -> FnInfo {
390 FnInfo::new(def_id, check_safety(tcx, def_id), get_type(tcx, def_id))
391}
392
393pub fn get_ptr_deref_dummy_def_id(tcx: TyCtxt<'_>) -> Option<DefId> {
394 tcx.hir_crate_items(()).free_items().find_map(|item_id| {
395 let def_id = item_id.owner_id.to_def_id();
396 let name = tcx.opt_item_name(def_id)?;
397
398 (name.as_str() == "__raw_ptr_deref_dummy").then_some(def_id)
399 })
400}
401
402pub fn get_mutated_fields(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<usize> {
408 use rustc_middle::mir::{ProjectionElem, StatementKind};
409
410 let body = tcx.optimized_mir(def_id);
411 let mut fields = Vec::new();
412
413 for (_, data) in body.basic_blocks.iter().enumerate() {
414 for statement in &data.statements {
415 if let StatementKind::Assign(assign) = &statement.kind {
416 let (place, _) = &**assign;
417 if place.local.as_usize() != 1 {
418 continue;
419 }
420 let mut saw_deref = false;
421 for proj in place.projection.iter() {
422 match proj {
423 ProjectionElem::Deref => {
424 saw_deref = true;
425 }
426 ProjectionElem::Field(index, _) if saw_deref => {
427 let idx = index.as_usize();
428 if !fields.contains(&idx) {
429 fields.push(idx);
430 }
431 }
432 _ => {}
433 }
434 }
435 }
436 }
437 }
438
439 fields
440}