Skip to main content

rapx/helpers/
api_classify.rs

1#![allow(dead_code)]
2//! Standard-library API name classification helpers.
3//!
4//! Each function matches a specific pattern in a MIR callee's
5//! `def_path_str` to determine what kind of operation it performs
6//! (pointer arithmetic, memory access, ownership transfer, etc.).
7
8pub fn is_ownership_reconstruction(name: &str) -> bool {
9    name.contains("from_raw") && !name.contains("from_raw_parts")
10        && (name.contains("boxed") || name.contains("Box")
11            || name.contains("CString") || name.contains("ffi::c_str"))
12        || name.contains("from_vec_with_nul_unchecked")
13}
14
15pub fn is_as_ptr(name: &str) -> bool {
16    name.contains("::as_ptr") && !name.ends_with("::as_ptr_range")
17        || name.ends_with("::into_raw")
18        || name.contains("::as_mut_ptr") && !name.ends_with("::as_mut_ptr_range")
19        || name.ends_with("::into_raw_mut")
20        || (name.contains("::cast") && !name.contains("::cast_to"))
21        || name.contains("cast_array")
22        || name.contains("cast_const") || name.contains("cast_mut")
23        || name.ends_with("::from") && name.contains("ptr::non_null")
24        || name.ends_with("::new_unchecked") && name.contains("ptr::non_null")
25        || name.ends_with("::as_ref") && name.contains("ptr::non_null")
26        || name.ends_with("::as_mut") && name.contains("ptr::non_null")
27}
28
29pub fn is_pointer_add(name: &str) -> bool {
30    name.ends_with("::add") || name.ends_with("::wrapping_add")
31        || name.contains("::offset") || name.contains("::wrapping_offset")
32        || name.contains("::byte_add") || name.contains("::wrapping_byte_add")
33        || name.contains("::byte_offset") || name.contains("::wrapping_byte_offset")
34}
35
36pub fn is_pointer_sub(name: &str) -> bool {
37    name.ends_with("::sub") || name.ends_with("::wrapping_sub")
38        || name.contains("::byte_sub") || name.contains("::wrapping_byte_sub")
39}
40
41pub fn is_byte_ptr_arith(name: &str) -> bool {
42    name.contains("::byte_add") || name.contains("::wrapping_byte_add")
43        || name.contains("::byte_sub") || name.contains("::wrapping_byte_sub")
44        || name.contains("::byte_offset") || name.contains("::wrapping_byte_offset")
45}
46
47pub fn is_layout_constant(name: &str) -> bool { name.contains("align_of") || name.contains("size_of") }
48pub(crate) fn is_align_of(name: &str) -> bool { name.contains("align_of") }
49pub fn is_align_offset(name: &str) -> bool { name.contains("::align_offset") }
50pub fn is_as_ptr_range(name: &str) -> bool { name.ends_with("::as_ptr_range") }
51pub fn is_as_mut_ptr_range(name: &str) -> bool { name.ends_with("::as_mut_ptr_range") }
52pub fn is_ptr_write(name: &str) -> bool {
53    (name.contains("::write") || name.ends_with("write"))
54        && !name.contains("write_bytes") && !name.contains("write_unaligned")
55        && !name.contains("write_volatile")
56}
57pub fn is_maybe_uninit_write(name: &str) -> bool {
58    name.contains("MaybeUninit") && name.ends_with("::write")
59        && !name.contains("write_bytes")
60}
61
62/// Memory copy/write intrinsics that legitimately write through a raw pointer
63/// without requiring the target bytes to be pre-initialized (e.g. `ptr::write`,
64/// `write_bytes`, `copy_nonoverlapping`, `ptr::copy`). Used by the checker to
65/// discharge `Init`/`Typed` obligations on `MaybeUninit` targets.
66pub fn is_mem_copy_or_write_api(name: &str) -> bool {
67    name.contains("copy_nonoverlapping")
68        || name == "copy"
69        || name.ends_with("::copy")
70        || name.contains("ptr::copy")
71        || name.contains("write_bytes")
72        || name.contains("ptr::write")
73}
74pub fn is_len(name: &str) -> bool { name.contains("::len") }
75pub fn is_offset_from_unsigned(name: &str) -> bool {
76    name.contains("::offset_from_unsigned") || name.contains("::offset_from")
77}
78/// Numeric operations with no precise effect model — these fall back to an
79/// unconstrained result (`eff_none`). `unchecked_add/mul` and `checked_add/mul`
80/// are deliberately absent: `REGISTRY` matches them earlier with the more
81/// precise `int_add`/`int_mul`/`int_checked_*` rows.
82pub fn is_numeric_arith(name: &str) -> bool {
83    name.contains("::unchecked_sub") || name.contains("::unchecked_div")
84        || name.contains("::unchecked_rem") || name.contains("::exact_div")
85        || name.contains("::checked_sub")
86}
87pub fn is_option_unwrap(name: &str) -> bool {
88    (name.contains("Option") || name.contains("Result"))
89        && (name.contains("::expect") || name.contains("::unwrap")
90            || name.contains("::unwrap_unchecked"))
91}
92pub fn is_maybe_uninit_uninit(name: &str) -> bool {
93    name.contains("MaybeUninit") && name.ends_with("::uninit")
94}
95pub fn is_maybe_uninit_assume_init(name: &str) -> bool {
96    name.contains("MaybeUninit") && (name.ends_with("::assume_init") || name.ends_with("::assume_init_read"))
97}
98pub fn is_from_raw_parts(name: &str) -> bool { name.contains("::from_raw_parts") }
99pub fn is_cstr_from_ptr(name: &str) -> bool {
100    name.contains("CStr") && name.ends_with("::from_ptr")
101}
102pub fn is_cstr_from_bytes_with_nul_unchecked(name: &str) -> bool {
103    name.contains("CStr") && name.ends_with("::from_bytes_with_nul_unchecked")
104}
105
106/// Strict C-string constructors whose caller must guarantee NUL termination
107/// (`CStr::from_bytes_with_nul_unchecked`, `CString::from_vec_with_nul_unchecked`).
108pub fn is_cstr_strict_constructor(name: &str) -> bool {
109    is_cstr_from_bytes_with_nul_unchecked(name) || name.contains("from_vec_with_nul_unchecked")
110}
111pub fn is_vec_push(name: &str) -> bool {
112    (name.ends_with("::push") || name.ends_with("::reserve") || name.ends_with("::reserve_exact"))
113        && name.contains("Vec")
114}
115pub fn is_vec_alloc_constructor(name: &str) -> bool {
116    name.contains("::vec::from_elem")
117        || name == "from_elem"
118}
119pub fn is_vec_from_box(name: &str) -> bool {
120    name.contains("::into_vec")
121        || name.contains("box_assume_init_into_vec_unsafe")
122}
123pub fn is_vec_with_capacity(name: &str) -> bool {
124    (name.contains("::Vec") && name.ends_with("::with_capacity"))
125        || name == "with_capacity"
126}
127pub fn is_into_boxed_slice(name: &str) -> bool {
128    name.ends_with("::into_boxed_slice")
129}
130
131// ── Alias-hazard classification (moved from verify/alias_hazard.rs) ──────
132// These are the single home for "what does this raw-pointer API do" used by the
133// alias/hazard scanner. Note: `is_ownership_transfer_api` is *not* the same as
134// [`is_ownership_reconstruction`]: it also matches `Vec::from_raw_parts` /
135// `from_parts` (ownership transfer), but not `from_vec_with_nul_unchecked`.
136
137pub fn is_read_api(name: &str) -> bool {
138    if name.contains("::ptr::") {
139        if name.ends_with("::read")
140            || name.ends_with("::read_unaligned")
141            || name.ends_with("::read_volatile")
142            || name.ends_with("::copy_to")
143            || name.ends_with("::copy_to_nonoverlapping")
144            || name.ends_with("::copy_from")
145            || name.ends_with("::copy_from_nonoverlapping")
146        {
147            return true;
148        }
149    }
150    if name.ends_with("::assume_init_read") {
151        return true;
152    }
153    if name.contains("::intrinsics::")
154        && (name.ends_with("::copy") || name.ends_with("::copy_nonoverlapping"))
155    {
156        return true;
157    }
158    false
159}
160
161pub fn is_ownership_transfer_api(name: &str) -> bool {
162    if is_vec_ownership_transfer_api(name) {
163        return true;
164    }
165    let is_from_raw = name.contains("from_raw");
166    is_from_raw
167        && (name.contains("boxed")
168            || name.contains("Box")
169            || name.contains("ffi::c_str")
170            || name.contains("CString")
171            || is_vec_ownership_transfer_api(name))
172}
173
174pub fn is_vec_ownership_transfer_api(name: &str) -> bool {
175    (name.contains("from_raw_parts") || name.contains("from_parts"))
176        && (name.contains("Vec") || name.contains("vec::"))
177}
178
179pub(crate) fn is_nonnull_api(name: &str) -> bool {
180    name.contains("ptr::non_null") || name.contains("ptr::NonNull")
181}
182
183// ── ADT type-name classifiers (match def_path_str of ADT types) ──────
184
185pub fn is_std_vec(name: &str) -> bool { name.ends_with("::Vec") || name == "Vec" }
186pub fn is_std_box(name: &str) -> bool { name.ends_with("::Box") || name == "Box" }
187pub fn is_std_cstring(name: &str) -> bool {
188    name.ends_with("::CString") || name == "CString"
189        || name.ends_with("::c_str::CString")
190}
191pub fn is_std_nonnull(name: &str) -> bool {
192    name.ends_with("::NonNull") || name == "NonNull"
193        || name.contains("::NonNull")
194}
195pub fn is_std_option(name: &str) -> bool {
196    name.ends_with("::Option") || name == "Option"
197        || name.contains("::Option")
198}
199pub fn is_std_iter_or_itermut(name: &str) -> bool {
200    name.ends_with("::Iter") || name == "Iter"
201        || name.ends_with("::IterMut") || name == "IterMut"
202}
203pub fn is_std_ordering(name: &str) -> bool { name.contains("cmp::Ordering") }
204
205// ── Function-name classifiers ────────────────────────────────────────
206
207pub fn is_select_unpredictable(name: &str) -> bool { name.contains("select_unpredictable") }
208pub fn is_post_inc_start(name: &str) -> bool { name.contains("::post_inc_start") }
209pub fn is_pre_dec_end(name: &str) -> bool { name.contains("::pre_dec_end") }
210pub fn is_iter_ptr_adj(name: &str) -> bool { is_post_inc_start(name) || is_pre_dec_end(name) }
211pub fn is_eq_or_partial_eq(name: &str) -> bool { name.contains("::eq") || name.contains("PartialEq") }
212pub fn is_vec_or_cstring_call(name: &str) -> bool { name.contains("::Vec") || name.contains("::CString") }