1use quote::ToTokens;
9use rustc_hir::def_id::DefId;
10use rustc_middle::ty::{GenericParamDefKind, Ty, TyCtxt};
11use safety_parser::syn::{Expr, Lit};
12
13use crate::helpers::fn_info::parse_expr_into_number;
14use crate::helpers::name::{access_ident_recursive, match_ty_with_ident};
15
16use super::place;
17use super::types::{ContractExpr, NumericPredicate, PropertyArg, RelOp};
18
19pub(crate) fn parse_contract_expr<'tcx>(
20 tcx: TyCtxt<'tcx>,
21 def_id: DefId,
22 expr: &Expr,
23 sp: &str,
24) -> ContractExpr<'tcx> {
25 if let Expr::Field(expr_field) = expr
27 && matches!(&expr_field.member, safety_parser::syn::Member::Named(ident) if ident == "len")
28 {
29 return ContractExpr::Len(Box::new(parse_contract_expr(
30 tcx,
31 def_id,
32 &expr_field.base,
33 sp,
34 )));
35 }
36 if let Expr::MethodCall(expr_method) = expr
37 && expr_method.method == "len"
38 && expr_method.args.is_empty()
39 {
40 return ContractExpr::Len(Box::new(parse_contract_expr(
41 tcx,
42 def_id,
43 &expr_method.receiver,
44 sp,
45 )));
46 }
47
48 if let Some(place) = place::parse_contract_place(tcx, def_id, expr) {
50 return ContractExpr::Place(place);
51 }
52 if let Some(e) = parse_const_param(tcx, def_id, expr) {
53 return e;
54 }
55 if let Some(value) = parse_builtin_const(tcx, expr) {
56 return ContractExpr::Const(value);
57 }
58 if let Some(value) = parse_expr_into_number(expr) {
59 return ContractExpr::new_value(value);
60 }
61 rap_debug!(
62 "Numeric expression in {:?} could not be resolved: {:?}",
63 sp,
64 expr
65 );
66 ContractExpr::Unknown
67}
68
69pub(crate) fn resolve_type_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<Ty<'tcx>> {
70 if name == "Self" {
71 let sig = tcx.fn_sig(def_id).skip_binder();
72 return sig.inputs().skip_binder().first().copied();
73 }
74 match_ty_with_ident(tcx, def_id, name.to_string())
75}
76
77pub(crate) fn int_type_min_max<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<(u128, u128)> {
78 use rustc_middle::ty::IntTy;
79 use rustc_middle::ty::UintTy;
80 let bits: u32 = match ty.kind() {
81 rustc_middle::ty::TyKind::Uint(ut) => match ut {
82 UintTy::U8 => 8,
83 UintTy::U16 => 16,
84 UintTy::U32 => 32,
85 UintTy::U64 => 64,
86 UintTy::U128 => 128,
87 UintTy::Usize => tcx.data_layout.pointer_size().bits() as u32,
88 },
89 rustc_middle::ty::TyKind::Int(it) => match it {
90 IntTy::I8 => 8,
91 IntTy::I16 => 16,
92 IntTy::I32 => 32,
93 IntTy::I64 => 64,
94 IntTy::I128 => 128,
95 IntTy::Isize => tcx.data_layout.pointer_size().bits() as u32,
96 },
97 _ => return None,
98 };
99 if bits == 0 {
100 return None;
101 }
102 match ty.kind() {
103 rustc_middle::ty::TyKind::Uint(_) => {
104 let max = if bits == 128 {
105 u128::MAX
106 } else {
107 (1u128 << bits) - 1
108 };
109 Some((0, max))
110 }
111 rustc_middle::ty::TyKind::Int(_) => {
112 let max = (1u128 << (bits - 1)) - 1;
113 let min = max + 1;
114 Some((min, max))
115 }
116 _ => None,
117 }
118}
119
120fn parse_builtin_const<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr) -> Option<u128> {
121 let Expr::Path(expr_path) = expr else {
122 return None;
123 };
124 let mut segments = expr_path.path.segments.iter();
125 let first = segments.next()?.ident.to_string();
126 let second = segments.next()?.ident.to_string();
127 if segments.next().is_some() || second != "MAX" {
128 return None;
129 }
130
131 let pointer_bits = tcx.data_layout.pointer_size().bits();
132 match first.as_str() {
133 "isize" => Some((1_u128 << (pointer_bits - 1)) - 1),
134 "usize" => Some((1_u128 << pointer_bits) - 1),
135 _ => None,
136 }
137}
138
139fn parse_const_param<'tcx>(
140 tcx: TyCtxt<'tcx>,
141 def_id: DefId,
142 expr: &Expr,
143) -> Option<ContractExpr<'tcx>> {
144 let Expr::Path(expr_path) = expr else {
145 return None;
146 };
147 let ident = expr_path.path.get_ident()?.to_string();
148 let mut generics = Some(tcx.generics_of(def_id));
149 while let Some(current) = generics {
150 if let Some(param) = current.own_params.iter().find(|param| {
151 matches!(param.kind, GenericParamDefKind::Const { .. })
152 && param.name.as_str() == ident
153 }) {
154 return Some(ContractExpr::ConstParam {
155 index: param.index,
156 name: ident,
157 });
158 }
159 generics = current.parent.map(|parent| tcx.generics_of(parent));
160 }
161 None
162}
163
164pub(crate) fn parse_type<'tcx>(
165 tcx: TyCtxt<'tcx>,
166 def_id: DefId,
167 expr: &Expr,
168 sp: &str,
169) -> Option<Ty<'tcx>> {
170 let ty_ident_full = access_ident_recursive(expr);
171 if ty_ident_full.is_none() {
172 rap_debug!("Incorrect expression for the type of {:?} Tag!", sp);
173 return None;
174 }
175 let ty_ident = ty_ident_full.unwrap().0;
176 let ty = match_ty_with_ident(tcx, def_id, ty_ident);
177 if ty.is_none() {
178 rap_debug!("Cannot get type in {:?} Tag!", sp);
179 }
180 ty
181}
182
183pub(crate) fn parse_target_arg<'tcx>(
184 tcx: TyCtxt<'tcx>,
185 def_id: DefId,
186 expr: &Expr,
187) -> PropertyArg<'tcx> {
188 if let Expr::Path(expr_path) = expr {
192 if let Some(ident) = expr_path.path.get_ident() {
193 let s = ident.to_string();
194 if s != "return"
195 && !s.starts_with("Arg_")
196 && place::parse_expr_into_local_and_ty(tcx, def_id, expr).is_none()
197 {
198 return PropertyArg::Ident(s);
199 }
200 }
201 }
202 place::parse_contract_place(tcx, def_id, expr)
203 .map(|p| PropertyArg::Expr(ContractExpr::Place(p)))
204 .unwrap_or_else(|| PropertyArg::Expr(parse_contract_expr(tcx, def_id, expr, "target")))
205}
206
207pub(crate) fn parse_valid_num<'tcx>(
208 tcx: TyCtxt<'tcx>,
209 def_id: DefId,
210 exprs: &[Expr],
211) -> Vec<NumericPredicate<'tcx>> {
212 match exprs {
213 [] => Vec::new(),
214 [expr] => parse_numeric_predicate(tcx, def_id, expr).into_iter().collect(),
215 [value, range, ..] => {
216 if let Some(predicates) = parse_interval_predicates(tcx, def_id, value, range) {
217 predicates
218 } else {
219 parse_numeric_predicate(tcx, def_id, value)
220 .into_iter()
221 .collect()
222 }
223 }
224 }
225}
226
227fn parse_numeric_predicate<'tcx>(
228 tcx: TyCtxt<'tcx>,
229 def_id: DefId,
230 expr: &Expr,
231) -> Option<NumericPredicate<'tcx>> {
232 let text = expr.to_token_stream().to_string();
233 super::pest_conv::parse_predicate_pest(tcx, def_id, &text)
234}
235
236pub(crate) fn expr_to_pest<'tcx>(
237 tcx: TyCtxt<'tcx>,
238 def_id: DefId,
239 expr: &Expr,
240) -> ContractExpr<'tcx> {
241 let text = expr.to_token_stream().to_string();
242 super::pest_conv::parse_expr_pest(tcx, def_id, &text)
243}
244
245fn parse_interval_predicates<'tcx>(
246 tcx: TyCtxt<'tcx>,
247 def_id: DefId,
248 value: &Expr,
249 range: &Expr,
250) -> Option<Vec<NumericPredicate<'tcx>>> {
251 match range {
252 Expr::Array(array) if array.elems.len() == 2 => {
253 let mut elems = array.elems.iter();
254 let lower = elems.next().unwrap();
255 let upper = elems.next().unwrap();
256 Some(build_interval_predicates(
257 tcx, def_id, value, lower, true, upper, true,
258 ))
259 }
260 Expr::Lit(expr_lit) => {
261 let Lit::Str(range_lit) = &expr_lit.lit else {
262 return None;
263 };
264 parse_string_interval(tcx, def_id, value, &range_lit.value())
265 }
266 _ => None,
267 }
268}
269
270fn parse_string_interval<'tcx>(
271 tcx: TyCtxt<'tcx>,
272 def_id: DefId,
273 value: &Expr,
274 raw_range: &str,
275) -> Option<Vec<NumericPredicate<'tcx>>> {
276 let trimmed = raw_range.trim();
277 if trimmed.len() < 5 {
278 return None;
279 }
280
281 let lower_inclusive = trimmed.starts_with('[');
282 let upper_inclusive = trimmed.ends_with(']');
283 if !(lower_inclusive || trimmed.starts_with('('))
284 || !(upper_inclusive || trimmed.ends_with(')'))
285 {
286 return None;
287 }
288
289 let body = &trimmed[1..trimmed.len() - 1];
290 let (lower_raw, upper_raw) = body.split_once(',')?;
291 let lower = safety_parser::syn::parse_str::<Expr>(lower_raw.trim()).ok()?;
292 let upper = safety_parser::syn::parse_str::<Expr>(upper_raw.trim()).ok()?;
293
294 Some(build_interval_predicates(
295 tcx,
296 def_id,
297 value,
298 &lower,
299 lower_inclusive,
300 &upper,
301 upper_inclusive,
302 ))
303}
304
305fn build_interval_predicates<'tcx>(
306 tcx: TyCtxt<'tcx>,
307 def_id: DefId,
308 value: &Expr,
309 lower: &Expr,
310 lower_inclusive: bool,
311 upper: &Expr,
312 upper_inclusive: bool,
313) -> Vec<NumericPredicate<'tcx>> {
314 let value_expr = expr_to_pest(tcx, def_id, value);
315 let lower_expr = expr_to_pest(tcx, def_id, lower);
316 let upper_expr = expr_to_pest(tcx, def_id, upper);
317 vec![
318 NumericPredicate::new(
319 lower_expr,
320 if lower_inclusive {
321 RelOp::Le
322 } else {
323 RelOp::Lt
324 },
325 value_expr.clone(),
326 ),
327 NumericPredicate::new(
328 value_expr,
329 if upper_inclusive {
330 RelOp::Le
331 } else {
332 RelOp::Lt
333 },
334 upper_expr,
335 ),
336 ]
337}
338
339pub(crate) fn unwrap_array_expr<'tcx>(
342 tcx: TyCtxt<'tcx>,
343 def_id: DefId,
344 expr: &Expr,
345) -> Option<Ty<'tcx>> {
346 if let Expr::Array(arr) = expr
347 && arr.elems.len() == 1
348 {
349 return parse_type(tcx, def_id, &arr.elems[0], "SplitTransmute");
350 }
351 parse_type(tcx, def_id, expr, "SplitTransmute")
352}