Skip to main content

rapx/analysis/api_dependency/
fuzzable.rs

1#[cfg(not(rapx_has_skip_norm_wip))]
2use crate::compat::SkipNormWip;
3use crate::limit::FUZZABLE_MAX_DEPTH;
4#[cfg(rapx_has_attr_ir)]
5use rustc_attr_ir::LangItem;
6#[cfg(all(not(rapx_has_attr_ir), not(rapx_ge_100)))]
7use rustc_hir::LangItem;
8#[cfg(all(not(rapx_has_attr_ir), rapx_ge_100))]
9use rustc_hir::attrs::lang_items::LangItem;
10#[cfg(rapx_has_attr_ir)]
11use rustc_attr_ir::find_attr;
12#[cfg(rapx_has_attr_ir)]
13use rustc_attr_ir::AttributeKind;
14#[cfg(not(rapx_has_attr_ir))]
15use rustc_hir::find_attr;
16#[cfg(not(rapx_has_attr_ir))]
17use rustc_hir::attrs::AttributeKind;
18#[cfg(rapx_const_ext)]
19use rustc_middle::ty::consts::ConstExt;
20use rustc_middle::ty::{self, Ty, TyCtxt, TyKind};
21use rustc_span::sym;
22use rustc_type_ir::TypeVisitable;
23
24fn is_fuzzable_std_ty<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, depth: usize) -> bool {
25    match ty.kind() {
26        ty::Adt(def, args) => {
27            if tcx.is_lang_item(def.did(), LangItem::String) {
28                return true;
29            }
30            if tcx.is_diagnostic_item(sym::Vec, def.did())
31                && is_fuzzable_ty(args.type_at(0), tcx, depth + 1)
32            {
33                return true;
34            }
35            if tcx.is_diagnostic_item(sym::Arc, def.did())
36                && is_fuzzable_ty(args.type_at(0), tcx, depth + 1)
37            {
38                return true;
39            }
40            false
41        }
42        _ => false,
43    }
44}
45
46fn is_non_fuzzable_std_ty<'tcx>(ty: Ty<'tcx>, _tcx: TyCtxt<'tcx>) -> bool {
47    let name = format!("{}", ty);
48    if name.as_str() == "core::alloc::LayoutError" { return true }
49    false
50}
51
52fn ty_contains_region<'tcx>(ty: Ty<'tcx>) -> bool {
53    struct Visitor {
54        contains_region: bool,
55    }
56    impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor {
57        fn visit_region(&mut self, _: ty::Region<'tcx>) -> Self::Result {
58            self.contains_region = true;
59        }
60    }
61    let mut visitor = Visitor {
62        contains_region: false,
63    };
64    ty.visit_with(&mut visitor);
65    visitor.contains_region
66}
67
68/// Checks whether the given ADT, or any of its fields/variants, are marked as `#[non_exhaustive]`
69///
70/// This function is copied from Clippy
71pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: ty::AdtDef<'_>) -> bool {
72    adt.is_variant_list_non_exhaustive()
73        || find_attr!(
74            crate::compat::get_all_attrs(tcx, adt.did()),
75            AttributeKind::NonExhaustive(..)
76        )
77        || adt.variants().iter().any(|variant_def| {
78            variant_def.is_field_list_non_exhaustive()
79                || find_attr!(
80                    crate::compat::get_all_attrs(tcx, variant_def.def_id),
81                    AttributeKind::NonExhaustive(..)
82                )
83        })
84        || adt.all_fields().any(|field_def| {
85            find_attr!(
86                crate::compat::get_all_attrs(tcx, field_def.did),
87                AttributeKind::NonExhaustive(..)
88            )
89        })
90}
91
92pub fn is_fuzzable_ty<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, depth: usize) -> bool {
93    if depth > FUZZABLE_MAX_DEPTH {
94        return false;
95    }
96
97    if is_fuzzable_std_ty(ty, tcx, depth + 1) {
98        return true;
99    }
100
101    if is_non_fuzzable_std_ty(ty, tcx) {
102        return false;
103    }
104
105    match ty.kind() {
106        // Basical data type
107        TyKind::Bool
108        | TyKind::Char
109        | TyKind::Int(_)
110        | TyKind::Uint(_)
111        | TyKind::Float(_)
112        | TyKind::Str => true,
113
114        // Infer
115        TyKind::Infer(
116            ty::InferTy::IntVar(_)
117            | ty::InferTy::FreshIntTy(_)
118            | ty::InferTy::FloatVar(_)
119            | ty::InferTy::FreshFloatTy(_),
120        ) => true,
121
122        // Reference, Array, Slice
123        TyKind::Ref(_, inner_ty, _) | TyKind::Slice(inner_ty) => {
124            is_fuzzable_ty(inner_ty.peel_refs(), tcx, depth + 1)
125        }
126
127        TyKind::Array(inner_ty, const_) => {
128            if const_.try_to_value().is_none() {
129                return false;
130            }
131            is_fuzzable_ty(inner_ty.peel_refs(), tcx, depth + 1)
132        }
133
134        // Tuple
135        TyKind::Tuple(tys) => tys
136            .iter()
137            .all(|inner_ty| is_fuzzable_ty(inner_ty.peel_refs(), tcx, depth + 1)),
138
139        // ADT
140        TyKind::Adt(adt_def, args) => {
141            if adt_def.is_union() || has_non_exhaustive_attr(tcx, *adt_def) {
142                return false;
143            }
144            // if adt contain region, then we consider it non-fuzzable
145            if ty_contains_region(ty) {
146                return false;
147            }
148
149            // if any field is not public or not fuzzable, then we consider it non-fuzzable
150            if !adt_def.all_fields().all(|field| {
151                field.vis.is_public()
152                    && is_fuzzable_ty(field.ty(tcx, args).skip_norm_wip(), tcx, depth + 1)
153            }) {
154                return false;
155            }
156
157            // empty enum cannot be instantiated
158            if adt_def.is_enum() && adt_def.variants().is_empty() {
159                return false;
160            }
161
162            true
163        }
164        _ => false,
165    }
166}