1use rustc_hir::{Safety, def::DefKind, def_id::DefId};
2use rustc_middle::{
3 ty,
4 ty::{AssocKind, Mutability, TyCtxt, TyKind},
5};
6use rustc_span::{kw, sym};
7use std::{collections::HashSet, fmt::Debug, hash::Hash};
8use syn::Expr;
9
10pub use super::mir_scan::check_safety;
11pub use super::name::get_cleaned_def_path_name;
12
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
14pub enum FnKind {
15 Fn,
16 Method,
17 Constructor,
18 Intrinsic,
19}
20
21#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
22pub struct FnInfo {
23 pub def_id: DefId,
24 pub fn_safety: Safety,
25 pub fn_kind: FnKind,
26}
27
28impl FnInfo {
29 pub fn new(def_id: DefId, fn_safety: Safety, fn_kind: FnKind) -> Self {
30 FnInfo {
31 def_id,
32 fn_safety,
33 fn_kind,
34 }
35 }
36}
37
38#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
39pub struct AdtInfo {
40 pub def_id: DefId,
41 pub literal_cons_enabled: bool,
42}
43
44impl AdtInfo {
45 pub fn new(def_id: DefId, literal_cons_enabled: bool) -> Self {
46 AdtInfo {
47 def_id,
48 literal_cons_enabled,
49 }
50 }
51}
52
53pub fn check_visibility(tcx: TyCtxt, func_defid: DefId) -> bool {
54 tcx.visibility(func_defid).is_public()
55}
56
57fn is_self_ty<'tcx>(tcx: TyCtxt<'tcx>, assoc_item: &ty::AssocItem, ty: ty::Ty<'tcx>) -> bool {
60 if ty.is_param(0) {
61 return true;
62 }
63 assoc_item
64 .impl_container(tcx)
65 .is_some_and(|impl_id| ty == tcx.type_of(impl_id).skip_binder())
66}
67
68pub fn get_type(tcx: TyCtxt<'_>, def_id: DefId) -> FnKind {
69 let Some(assoc_item) = tcx.opt_associated_item(def_id) else {
70 return FnKind::Fn;
71 };
72 let AssocKind::Fn { has_self, .. } = assoc_item.kind else {
73 return FnKind::Fn;
74 };
75 if has_self {
76 return FnKind::Method;
77 }
78 let output = tcx.fn_sig(def_id).skip_binder().output().skip_binder();
79 if is_self_ty(tcx, &assoc_item, output) {
80 return FnKind::Constructor;
81 }
82 match output.kind() {
83 TyKind::Ref(_, ref_ty, _) => {
84 if is_self_ty(tcx, &assoc_item, *ref_ty) {
85 return FnKind::Constructor;
86 }
87 }
88 TyKind::Adt(adt_def, substs)
89 if adt_def.is_enum()
90 && (tcx.is_diagnostic_item(sym::Option, adt_def.did())
91 || tcx.is_diagnostic_item(sym::Result, adt_def.did())
92 || tcx.is_diagnostic_item(kw::Box, adt_def.did())) =>
93 {
94 if is_self_ty(tcx, &assoc_item, substs.type_at(0)) {
95 return FnKind::Constructor;
96 }
97 }
98 _ => {}
99 }
100 FnKind::Fn
101}
102
103pub fn returns_wrapped_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
113 let Some(assoc_item) = tcx.opt_associated_item(def_id) else {
114 return false;
115 };
116 if !matches!(
117 assoc_item.kind,
118 AssocKind::Fn {
119 has_self: false,
120 ..
121 }
122 ) {
123 return false;
124 }
125 let fn_sig = tcx.fn_sig(def_id).skip_binder();
126 let output = fn_sig.output().skip_binder();
127 let TyKind::Adt(adt_def, substs) = output.kind() else {
128 return false;
129 };
130 if !(adt_def.is_enum()
131 && (tcx.is_diagnostic_item(sym::Option, adt_def.did())
132 || tcx.is_diagnostic_item(sym::Result, adt_def.did())))
133 {
134 return false;
135 }
136 is_self_ty(tcx, &assoc_item, substs.type_at(0))
137}
138
139fn self_adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::AdtDef<'_>> {
141 let assoc_item = tcx.opt_associated_item(def_id)?;
142 let impl_id = assoc_item.impl_container(tcx)?;
143 tcx.type_of(impl_id).skip_binder().ty_adt_def()
144}
145
146pub fn get_adt_via_method(tcx: TyCtxt<'_>, method_def_id: DefId) -> Option<AdtInfo> {
148 let adt_def = self_adt_def(tcx, method_def_id)?;
149 let adt_def_id = adt_def.did();
150
151 let total_count = adt_def.all_fields().count();
152
153 if total_count == 0 {
154 return Some(AdtInfo::new(adt_def_id, true));
155 }
156
157 let pub_count = public_field_indices(tcx, adt_def).len();
158
159 if pub_count == 0 {
160 return None;
161 }
162 Some(AdtInfo::new(adt_def_id, pub_count == total_count))
163}
164pub fn get_adt_def_id_by_adt_method(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
165 self_adt_def(tcx, def_id).map(|adt_def| adt_def.did())
166}
167
168fn is_mut_self_method(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
173 let Some(assoc) = tcx.opt_associated_item(def_id) else {
174 return false;
175 };
176 if !matches!(assoc.kind, AssocKind::Fn { has_self: true, .. }) {
177 return false;
178 }
179 let fn_sig = tcx.fn_sig(def_id).instantiate_identity().skip_binder();
180 let Some(first) = fn_sig.inputs_and_output.first().copied() else {
181 return false;
182 };
183 matches!(first.kind(), TyKind::Ref(_, _, Mutability::Mut))
184}
185
186fn public_field_indices(tcx: TyCtxt<'_>, adt_def: ty::AdtDef<'_>) -> HashSet<usize> {
188 adt_def
189 .all_fields()
190 .enumerate()
191 .filter_map(|(index, field_def)| tcx.visibility(field_def.did).is_public().then_some(index))
192 .collect()
193}
194
195pub fn parse_expr_into_number(expr: &Expr) -> Option<usize> {
197 if let Expr::Lit(expr_lit) = expr {
198 if let syn::Lit::Int(lit_int) = &expr_lit.lit {
199 return lit_int.base10_parse::<usize>().ok();
200 }
201 }
202 None
203}
204
205pub fn get_all_std_fns_by_rustc_public(tcx: TyCtxt) -> Vec<DefId> {
206 let mut all_std_fn_def = Vec::new();
207 let mut results = Vec::new();
208 let mut core_fn_def: Vec<_> = rustc_public::find_crates("core")
209 .iter()
210 .flat_map(|krate| krate.fn_defs())
211 .collect();
212 let mut std_fn_def: Vec<_> = rustc_public::find_crates("std")
213 .iter()
214 .flat_map(|krate| krate.fn_defs())
215 .collect();
216 let mut alloc_fn_def: Vec<_> = rustc_public::find_crates("alloc")
217 .iter()
218 .flat_map(|krate| krate.fn_defs())
219 .collect();
220 all_std_fn_def.append(&mut core_fn_def);
221 all_std_fn_def.append(&mut std_fn_def);
222 all_std_fn_def.append(&mut alloc_fn_def);
223
224 for fn_def in &all_std_fn_def {
225 let def_id = crate::def_id::to_internal(fn_def, tcx);
226 results.push(def_id);
227 }
228 results
229}
230
231pub fn get_all_mutable_methods(tcx: TyCtxt, src_def_id: DefId) -> HashSet<DefId> {
236 if get_type(tcx, src_def_id) == FnKind::Constructor {
237 return HashSet::new();
238 }
239 let target_adt_def = get_adt_def_id_by_adt_method(tcx, src_def_id);
240 let mut mutators = HashSet::new();
241 let mut is_std = false;
242 for def_id in get_all_std_fns_by_rustc_public(tcx) {
243 let adt_def = get_adt_def_id_by_adt_method(tcx, def_id);
244 if adt_def.is_some() && adt_def == target_adt_def && src_def_id != def_id {
245 if is_mut_self_method(tcx, def_id) {
246 mutators.insert(def_id);
247 }
248 is_std = true;
249 }
250 }
251 if is_std {
252 return mutators;
253 }
254 mutators.extend(get_muts(tcx, src_def_id));
255 mutators
256}
257
258fn assoc_fns_of_self(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<DefId> {
261 let Some(adt_def) = self_adt_def(tcx, def_id) else {
262 return Vec::new();
263 };
264 let mut fns = Vec::new();
265 for impl_def_id in tcx.inherent_impls(adt_def.did()) {
266 for item in tcx.associated_item_def_ids(*impl_def_id) {
267 if matches!(tcx.def_kind(*item), DefKind::Fn | DefKind::AssocFn) {
268 fns.push(*item);
269 }
270 }
271 }
272 fns
273}
274
275pub fn get_cons(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<DefId> {
276 if tcx.def_kind(def_id) == DefKind::Fn || get_type(tcx, def_id) == FnKind::Constructor {
277 return Vec::new();
278 }
279 assoc_fns_of_self(tcx, def_id)
280 .into_iter()
281 .filter(|&item| get_type(tcx, item) == FnKind::Constructor)
282 .collect()
283}
284
285pub fn get_muts(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<DefId> {
290 assoc_fns_of_self(tcx, def_id)
291 .into_iter()
292 .filter(|&item| is_mut_self_method(tcx, item))
293 .collect()
294}
295
296pub fn append_fn_with_types(tcx: TyCtxt, def_id: DefId) -> FnInfo {
297 FnInfo::new(def_id, check_safety(tcx, def_id), get_type(tcx, def_id))
298}
299
300pub fn get_ptr_deref_dummy_def_id(tcx: TyCtxt<'_>) -> Option<DefId> {
301 tcx.hir_crate_items(()).free_items().find_map(|item_id| {
302 let def_id = item_id.owner_id.to_def_id();
303 let name = tcx.opt_item_name(def_id)?;
304
305 (name.as_str() == "__raw_ptr_deref_dummy").then_some(def_id)
306 })
307}
308
309pub fn get_mutated_fields(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<usize> {
315 use rustc_middle::mir::{ProjectionElem, StatementKind};
316
317 let body = tcx.optimized_mir(def_id);
318 let mut fields = Vec::new();
319
320 for (_, data) in body.basic_blocks.iter().enumerate() {
321 for statement in &data.statements {
322 if let StatementKind::Assign(assign) = &statement.kind {
323 let (place, _) = &**assign;
324 if place.local.as_usize() != 1 {
325 continue;
326 }
327 let mut saw_deref = false;
328 for proj in place.projection.iter() {
329 match proj {
330 ProjectionElem::Deref => {
331 saw_deref = true;
332 }
333 ProjectionElem::Field(index, _) if saw_deref => {
334 let idx = index.as_usize();
335 if !fields.contains(&idx) {
336 fields.push(idx);
337 }
338 }
339 _ => {}
340 }
341 }
342 }
343 }
344 }
345
346 fields
347}
348
349pub fn is_externally_reachable(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
350 let Some(local) = def_id.as_local() else {
351 return true;
352 };
353 tcx.effective_visibilities(()).is_reachable(local)
354}